एक ट्रैम्पोलिन फ़ंक्शन क्या है?


93

काम पर हालिया चर्चा के दौरान, किसी ने एक ट्रैम्पोलिन फ़ंक्शन का उल्लेख किया।

मैंने विकिपीडिया पर वर्णन पढ़ा है । यह कार्यक्षमता का एक सामान्य विचार देने के लिए पर्याप्त है, लेकिन मैं कुछ और ठोस करना चाहूंगा।

क्या आपके पास कोड का एक सरल स्निपेट है जो एक ट्रैम्पोलिन का वर्णन करेगा?


2
Microsoft दुनिया में, trampolines को आमतौर पर 'थ्रक्स' कहा जाता है। [यहाँ एक पृष्ठ है] [1] आंद्रेई अलेक्जेंड्रेस्कु के "मॉडर्न सी ++ डिज़ाइन" से ---- [१]: books.google.com/…
माइकल बूर


यह कुछ कार्यक्षमता का सामान्यीकृत रूप है, जिसे आप सेटजम्प / लोमगजम्प के साथ कार्यान्वित कर सकते हैं, अर्थात् स्टैक ओवेरफ्लो से बचने के लिए।
इंगो

12
क्यों किसी को भी stackoverflow से बचना चाहते हैं?
निकोल

जवाबों:


72

विकिपीडिया पर वर्णित 'ट्रम्पोलिन' का लिस्प अर्थ भी है:

कुछ एलआईएसपी कार्यान्वयनों में उपयोग किया जाता है, एक ट्रैम्पोलिन एक लूप है जो पुनरावृत्तियां थंक-रिटर्न फ़ंक्शन को लागू करता है। एक एकल trampoline एक कार्यक्रम के सभी नियंत्रण स्थानान्तरण को व्यक्त करने के लिए पर्याप्त है; एक कार्यक्रम जिसे व्यक्त किया गया है, वह trampolined या "trampolined शैली" है; एक कार्यक्रम को परिवर्तित शैली में परिवर्तित करना trampolining है। स्टैम्प-ओरिएंटेड फ़ंक्शन का उपयोग स्टैक-ओरिएंटेड भाषाओं में टेल पुनरावर्ती फ़ंक्शन कॉल को कार्यान्वित करने के लिए किया जा सकता है

हम कहते हैं कि हम जावास्क्रिप्ट का उपयोग कर रहे हैं और निरंतर-गुजर-शैली में भोले फाइबोनैचि फ़ंक्शन लिखना चाहते हैं। उदाहरण के लिए JS को पोर्ट करने के लिए या CPS के साथ खेलने के लिए, जो हमें वैसे भी सर्वर-साइड फ़ंक्शन को कॉल करने के लिए उपयोग करना पड़ता है, यह हम प्रासंगिक नहीं है।

तो, पहला प्रयास है

function fibcps(n, c) {
    if (n <= 1) {
        c(n);
    } else {
        fibcps(n - 1, function (x) {
            fibcps(n - 2, function (y) {
                c(x + y)
            })
        });
    }
}

लेकिन, n = 25फ़ायरफ़ॉक्स में इसे चलाने से एक त्रुटि 'बहुत अधिक पुनरावृत्ति!' अब यह वास्तव में समस्या है (जावास्क्रिप्ट में टेल-कॉल ऑप्टिमाइज़ेशन गायब) जो कि ट्रम्पोलिनिंग सॉल्व करता है। किसी फ़ंक्शन में एक (पुनरावर्ती) कॉल करने के बजाय, हमें returnउस फ़ंक्शन को कॉल करने के लिए एक निर्देश (थंक) दें, जिसे लूप में व्याख्या किया जाए।

function fibt(n, c) {
    function trampoline(x) {
        while (x && x.func) {
            x = x.func.apply(null, x.args);
        }
    }

    function fibtramp(n, c) {
        if (n <= 1) {
            return {func: c, args: [n]};
        } else {
            return {
                func: fibtramp,
                args: [n - 1,
                    function (x) {
                        return {
                            func: fibtramp,
                            args: [n - 2, function (y) {
                                return {func: c, args: [x + y]}
                            }]
                        }
                    }
                ]
            }
        }
    }

    trampoline({func: fibtramp, args: [n, c]});
}

39

मुझे विभिन्न भाषाओं में, trampolines के साथ कार्यान्वित फैक्टरियल फंक्शन के लिए कुछ उदाहरण जोड़ते हैं:

स्काला:

sealed trait Bounce[A]
case class Done[A](result: A) extends Bounce[A]
case class Call[A](thunk: () => Bounce[A]) extends Bounce[A]

def trampoline[A](bounce: Bounce[A]): A = bounce match {
  case Call(thunk) => trampoline(thunk())
  case Done(x) => x
}

def factorial(n: Int, product: BigInt): Bounce[BigInt] = {
    if (n <= 2) Done(product)
    else Call(() => factorial(n - 1, n * product))
}

object Factorial extends Application {
    println(trampoline(factorial(100000, 1)))
}

जावा:

import java.math.BigInteger;

class Trampoline<T> 
{
    public T get() { return null; }
    public Trampoline<T>  run() { return null; }

    T execute() {
        Trampoline<T>  trampoline = this;

        while (trampoline.get() == null) {
            trampoline = trampoline.run();
        }

        return trampoline.get();
    }
}

public class Factorial
{
    public static Trampoline<BigInteger> factorial(final int n, final BigInteger product)
    {
        if(n <= 1) {
            return new Trampoline<BigInteger>() { public BigInteger get() { return product; } };
        }   
        else {
            return new Trampoline<BigInteger>() { 
                public Trampoline<BigInteger> run() { 
                    return factorial(n - 1, product.multiply(BigInteger.valueOf(n)));
                } 
            };
        }
    }

    public static void main( String [ ] args )
    {
        System.out.println(factorial(100000, BigInteger.ONE).execute());
    }
}

सी (बड़ी संख्या में कार्यान्वयन के बिना अशुभ):

#include <stdio.h>

typedef struct _trampoline_data {
  void(*callback)(struct _trampoline_data*);
  void* parameters;
} trampoline_data;

void trampoline(trampoline_data* data) {
  while(data->callback != NULL)
    data->callback(data);
}

//-----------------------------------------

typedef struct _factorialParameters {
  int n;
  int product;
} factorialParameters;

void factorial(trampoline_data* data) {
  factorialParameters* parameters = (factorialParameters*) data->parameters;

  if (parameters->n <= 1) {
    data->callback = NULL;
  }
  else {
    parameters->product *= parameters->n;
    parameters->n--;
  }
}

int main() {
  factorialParameters params = {5, 1};
  trampoline_data t = {&factorial, &params};

  trampoline(&t);
  printf("\n%d\n", params.product);

  return 0;
}

आपकी व्याख्या, विशेष रूप से सी उदाहरण, साथ ही नेस्टेड कार्यों के बारे में नीचे दिए गए विशेषण के उत्तर ने आखिरकार मुझे ट्रम्पोलिन समझा। एक प्रकार का सहायक कार्य जो राज्य को बंद करने की तरह अद्यतन करने के लिए इस्तेमाल किया जा सकता है।
बाइट

Scala कोड को ठीक किया जाना चाहिए if (n < 2) Done(product), SO ने मुझे 1 प्रतीक संपादित करने की अनुमति नहीं दी ...
Max

21

मैं आपको एक उदाहरण दूंगा जिसका उपयोग मैंने ऑनलाइन गेम के लिए एंटी-चीट पैच में किया था।

मुझे उन सभी फ़ाइलों को स्कैन करने में सक्षम होने की आवश्यकता है जो संशोधन के लिए खेल द्वारा लोड की जा रही थीं। तो मुझे ऐसा करने का सबसे मज़बूत तरीका CreateFileA के लिए एक trampoline का उपयोग करना पड़ा। इसलिए जब गेम लॉन्च किया गया, तो मुझे GetProcAddress का उपयोग करके CreateFileA के लिए पता मिल जाएगा, फिर मैं फ़ंक्शन के पहले कुछ बाइट्स को संशोधित करूंगा और असेंबली कोड सम्मिलित करूंगा जो मेरे खुद के "ट्रैम्पोलिन" फ़ंक्शन में कूद जाएगा, जहां मैं कुछ चीजें करूंगा, और तब मैं अपने jmp कोड के बाद CreateFile में अगले स्थान पर वापस जाऊंगा। मज़बूती से करने में सक्षम होने की तुलना में यह थोड़ा पेचीदा है, लेकिन मूल अवधारणा सिर्फ एक फ़ंक्शन को हुक करने के लिए है, इसे दूसरे फ़ंक्शन पर पुनर्निर्देशित करने के लिए मजबूर करें, और फिर मूल फ़ंक्शन पर वापस जाएं।

संपादित करें: Microsoft के पास इस प्रकार की चीज़ के लिए एक रूपरेखा है जिसे आप देख सकते हैं। जिन्हें कॉल किया जाता है


8

मैं वर्तमान में एक योजना दुभाषिया के लिए पूंछ कॉल अनुकूलन को लागू करने के तरीकों के साथ प्रयोग कर रहा हूं, और इसलिए इस समय मैं यह पता लगाने की कोशिश कर रहा हूं कि क्या ट्रम्पोलिन मेरे लिए संभव होगा।

जैसा कि मैं इसे समझता हूं, यह मूल रूप से एक trampoline फ़ंक्शन द्वारा किए गए फ़ंक्शन कॉल की एक श्रृंखला है। प्रत्येक फ़ंक्शन को एक थन कहा जाता है और गणना समाप्त होने तक अगला चरण देता है जब तक कि कार्यक्रम समाप्त नहीं होता (खाली निरंतरता)।

यहाँ कोड का पहला टुकड़ा है जो मैंने ट्रम्पोलिन की अपनी समझ को बेहतर बनाने के लिए लिखा था:

#include <stdio.h>

typedef void *(*CONTINUATION)(int);

void trampoline(CONTINUATION cont)
{
  int counter = 0;
  CONTINUATION currentCont = cont;
  while (currentCont != NULL) {
    currentCont = (CONTINUATION) currentCont(counter);
    counter++;
  }
  printf("got off the trampoline - happy happy joy joy !\n");
}

void *thunk3(int param)
{
  printf("*boing* last thunk\n");
  return NULL;
}

void *thunk2(int param)
{
  printf("*boing* thunk 2\n");
  return thunk3;
}

void *thunk1(int param)
{
  printf("*boing* thunk 1\n");
  return thunk2;
}

int main(int argc, char **argv)
{
  trampoline(thunk1);
}

का परिणाम:

meincompi $ ./trampoline 
*boing* thunk 1
*boing* thunk 2
*boing* last thunk
got off the trampoline - happy happy joy joy !

7

यहाँ नेस्टेड कार्यों का एक उदाहरण है:

#include <stdlib.h>
#include <string.h>
/* sort an array, starting at address `base`,
 * containing `nmemb` members, separated by `size`,
 * comparing on the first `nbytes` only. */
void sort_bytes(void *base,  size_t nmemb, size_t size, size_t nbytes) {
    int compar(const void *a, const void *b) {
        return memcmp(a, b, nbytes);
    }
    qsort(base, nmemb, size, compar);
}

comparएक बाहरी फ़ंक्शन नहीं हो सकता है, क्योंकि यह उपयोग करता है nbytes, जो केवल sort_bytesकॉल के दौरान मौजूद है । कुछ आर्किटेक्चर पर, एक छोटा स्टब फ़ंक्शन - ट्रम्पोलिन - रनटाइम पर उत्पन्न होता है, और इसमें वर्तमान इनवॉइस का स्टैक स्थान होता है sort_bytes। जब इसे कॉल किया जाता है, तो यह comparउस पते को पास करते हुए कोड पर पहुंच जाता है।

पावरपीसी जैसे आर्किटेक्चर पर इस गड़बड़ी की आवश्यकता नहीं है, जहां एबीआई निर्दिष्ट करता है कि फ़ंक्शन पॉइंटर वास्तव में एक "वसा सूचक" है, एक संरचना जिसमें निष्पादन योग्य कोड के लिए एक सूचक और डेटा के लिए एक अन्य सूचक है। हालाँकि, x86 पर, एक फ़ंक्शन पॉइंटर केवल एक पॉइंटर है।


0

सी के लिए, एक ट्रैम्पोलिन एक फ़ंक्शन पॉइंटर होगा:

size_t (*trampoline_example)(const char *, const char *);
trampoline_example= strcspn;
size_t result_1= trampoline_example("xyzbxz", "abc");

trampoline_example= strspn;
size_t result_2= trampoline_example("xyzbxz", "abc");

संपादित करें: अधिक गूढ़ trampolines संकलक द्वारा उत्पन्न किया जाएगा। इस तरह का एक उपयोग एक कूद तालिका होगा। (हालांकि स्पष्ट रूप से और अधिक जटिल हैं, जहां आप नीचे जटिल कोड उत्पन्न करने का प्रयास शुरू करते हैं।)


0

अब जबकि C # में स्थानीय कार्य हैं , बॉलिंग गेम कोडिंग काटा एक ट्रैंपोलिन के साथ सुरुचिपूर्ण ढंग से हल किया जा सकता है:

using System.Collections.Generic;
using System.Linq;

class Game
{
    internal static int RollMany(params int[] rs) 
    {
        return Trampoline(1, 0, rs.ToList());

        int Trampoline(int frame, int rsf, IEnumerable<int> rs) =>
              frame == 11             ? rsf
            : rs.Count() == 0         ? rsf
            : rs.First() == 10        ? Trampoline(frame + 1, rsf + rs.Take(3).Sum(), rs.Skip(1))
            : rs.Take(2).Sum() == 10  ? Trampoline(frame + 1, rsf + rs.Take(3).Sum(), rs.Skip(2))
            :                           Trampoline(frame + 1, rsf + rs.Take(2).Sum(), rs.Skip(2));
    }
}

विधि Game.RollManyको कई रोल के साथ कहा जाता है: आमतौर पर 20 रोल अगर कोई पुर्ज या स्ट्राइक नहीं हैं।

पहली पंक्ति तुरंत ट्रैम्पोलिन फ़ंक्शन को कॉल करती है return Trampoline(1, 0, rs.ToList());:। यह स्थानीय फ़ंक्शन पुनरावर्ती रूप से रोल सरणी का पता लगाता है। स्थानीय फ़ंक्शन (ट्रम्पोलिन) ट्रैवर्सल को दो अतिरिक्त मूल्यों के साथ शुरू करने की अनुमति देता है: frame1 से शुरू करें और rsf(अब तक परिणाम) 0।

स्थानीय कार्य के भीतर पांच मामलों को संभालने वाला टर्नरी ऑपरेटर होता है:

  • खेल 11 फ्रेम पर समाप्त होता है: अब तक परिणाम लौटाएं
  • यदि कोई और रोल नहीं हैं तो गेम समाप्त होता है: अब तक का परिणाम लौटाएं
  • स्ट्राइक: फ्रेम स्कोर की गणना करें और ट्रैवर्सल जारी रखें
  • स्पेयर: फ्रेम स्कोर की गणना करें और ट्रैवर्सल जारी रखें
  • सामान्य स्कोर: फ्रेम स्कोर की गणना करें और ट्रैवर्सल जारी रखें

ट्रैवर्सल को जारी रखना ट्रम्पोलिन को फिर से कॉल करके किया जाता है, लेकिन अब अद्यतन मूल्यों के साथ।

अधिक जानकारी के लिए, " पूंछ पुनरावृत्ति संचायक " खोजें। ध्यान रखें कि संकलक पूंछ पुनरावृत्ति का अनुकूलन नहीं करता है। तो यह समाधान जितना सुंदर हो सकता है, उतनी तेजी से यह संभव नहीं होगा।


-2
typedef void* (*state_type)(void);
void* state1();
void* state2();
void* state1() {
  return state2;
}
void* state2() {
  return state1;
}
// ...
state_type state = state1;
while (1) {
  state = state();
}
// ...

3
क्या आप कोई टिप्पणी या स्पष्टीकरण जोड़ सकते हैं कि यह एक ट्रैम्पोलिन क्यों है?
प्रसून
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.