2009-06-21 17 views
35

मैं एक समारोह _stack_push(stack* stk, void* el) के लिए सूचक है कहो। मैं curry(_stack_push, my_stack) फोन और एक समारोह है कि बस void* el लेता वापस प्राप्त करने में सक्षम होना चाहता हूँ। मैं यह करने के लिए एक रास्ता के बारे में सोच सकता है, के बाद से सी की अनुमति नहीं है क्रम समारोह परिभाषा है, लेकिन मुझे पता है कि यहाँ मुझे की तुलना में कहीं चालाक लोग हैं :)। कोई विचार?सी में करी करने का कोई तरीका है?

उत्तर

19

मैं लॉरेंट Dami द्वारा एक कागज कि C/C++/ऑब्जेक्टिव-सी में currying चर्चा पाया:

More Functional Reusability in C/C++/Objective-c with Curried Functions

रुचि का

यह कैसे सी में लागू किया गया है करने के लिए:

हमारे वर्तमान कार्यान्वयन तंत्र को जोड़ने के लिए कार्यान्वयन मौजूदा सी संरचनाओं का उपयोग करता है। कंपाइलर को संशोधित करने से यह करना बहुत आसान था, और करी के ब्याज को साबित करने के लिए पर्याप्त है। हालांकि, इस दृष्टिकोण में दो दोष हैं। सबसे पहले, करीबी कार्यों को टाइप-चेक नहीं किया जा सकता है, और इसलिए त्रुटियों से बचने के लिए सावधानीपूर्वक उपयोग की आवश्यकता होती है। दूसरा, करी फ़ंक्शन अपने तर्कों के आकार को नहीं जान सकता है, और उन्हें गिना जाता है जैसे कि वे एक पूर्णांक के आकार के थे।

कागज curry() के एक कार्यान्वयन शामिल नहीं है, लेकिन आप कल्पना कर सकते हैं कि यह कैसे function pointers और variadic functions का उपयोग कर कार्यान्वित किया जाता है।

+0

एक अच्छा पढ़ने की तरह दिखता है, धन्यवाद! – Burke

+9

+1 बहुत अच्छा लगता है, और मुझे पसंद है "हालांकि हमने व्यापक परीक्षण नहीं चलाए हैं, फिर भी हम एक सामान्य फ़ंक्शन कॉल से लगभग 60 गुना धीमी गति से चलने वाले फ़ंक्शन कॉल का अनुमान लगा सकते हैं।" –

+5

(मुझे यह पसंद है क्योंकि कभी-कभी आपको बहुत बुरी तरह की आवश्यकता होती है, और एक समाधान जो केवल 60 गुना धीमा चलता है, कोई समाधान नहीं होने से असीम रूप से बेहतर होता है।) –

4

यहाँ मेरे सिर के ऊपर से मेरी पहली अनुमान है (सबसे अच्छा समाधान नहीं हो सकता है)।

करी समारोह ढेर से कुछ स्मृति को आबंटित है, और उस ढेर-आबंटित स्मृति में पैरामीटर मान डाल सकता है। चाल तब लौटाए गए फ़ंक्शन के लिए जानती है कि यह उस पैरामीटर को उस ढेर-आवंटित स्मृति से पढ़ना है। यदि लौटाए गए फ़ंक्शन का केवल एक उदाहरण है, तो उन पैरामीटर के लिए पॉइंटर को सिंगलटन/ग्लोबल में संग्रहीत किया जा सकता है। अन्यथा यदि लौटाए गए फ़ंक्शन के एक से अधिक उदाहरण हैं, तो मुझे लगता है कि करी को हेप-आवंटित मेमोरी में लौटाए गए फ़ंक्शन के प्रत्येक उदाहरण को बनाने की आवश्यकता है ("पैरामीटर को उस पॉइंटर प्राप्त करें" जैसे ऑपकोड लिखकर, "पुश पैरामीटर ", और" अन्य फ़ंक्शन का आह्वान करें "ढेर-आवंटित स्मृति में)। उस स्थिति में आपको सावधान रहना होगा कि आवंटित स्मृति निष्पादन योग्य है, और शायद (मुझे नहीं पता) एंटी-वायरस प्रोग्राम से डर भी है।

6

जीसीसी नेस्टेड कार्यों की परिभाषा के लिए एक विस्तार प्रदान करता है। हालांकि यह आईएसओ मानक सी नहीं है, यह कुछ हितों का हो सकता है, क्योंकि यह काफी आसानी से प्रश्न का उत्तर देने में सक्षम बनाता है। संक्षेप में, नेस्टेड फ़ंक्शन पैरेंट फ़ंक्शन स्थानीय चर और पॉइंटर्स तक पहुंच सकता है, उन्हें पैरेंट फ़ंक्शन द्वारा भी वापस किया जा सकता है।

#include <stdio.h> 

typedef int (*two_var_func) (int, int); 
typedef int (*one_var_func) (int); 

int add_int (int a, int b) { 
    return a+b; 
} 

one_var_func partial (two_var_func f, int a) { 
    int g (int b) { 
     return f (a, b); 
    } 
    return g; 
} 

int main (void) { 
    int a = 1; 
    int b = 2; 
    printf ("%d\n", add_int (a, b)); 
    printf ("%d\n", partial (add_int, a) (b)); 
} 

लेकिन इस निर्माण करने के लिए एक सीमा है:

यहाँ एक छोटी, आत्म व्याख्यात्मक उदाहरण है। आप, जिसके परिणामस्वरूप कार्य करने के लिए एक सूचक रखने

one_var_func u = partial (add_int, a); 

में के रूप में यदि समारोह कॉल u(0), एक अप्रत्याशित व्यवहार में हो सकता है चर a जो u पढ़ता सिर्फ partial समाप्त के बाद नष्ट हो गया था के रूप में।

this section of GCC's documentation देखें।

+5

मैन्युअल से (आपके द्वारा प्रदान किए गए लिंक के तहत): "यदि आप युक्त फ़ंक्शन से बाहर निकलने के बाद नेस्टेड फ़ंक्शन को अपने पते के माध्यम से कॉल करने का प्रयास करते हैं, तो ** सभी नरक ढीले हो जाएंगे **।" –

+0

यदि आप पहले से ही जीसीसी में स्वयं को प्रतिबंधित कर रहे हैं, तो आप कॉलिंग फ़ंक्शन से बाहर निकलने तक नरक स्थगित करने के लिए कथन अभिव्यक्तियों का उपयोग कर सकते हैं (यानी: सब कुछ के लिए काम करेंगे लेकिन असीमित कॉलबैक): https://gist.github.com/a3f/2729c1248d0f2ee39b4a – a3f

0

सी में करी करने के लिए यहां एक दृष्टिकोण है।हालांकि यह नमूना अनुप्रयोग सुविधा के लिए सी ++ iostream आउटपुट का उपयोग कर रहा है, यह सभी सी शैली कोडिंग है।

इस दृष्टिकोण की कुंजी struct है जिसमें unsigned char की एक सरणी है और यह सरणी किसी फ़ंक्शन के लिए तर्क सूची बनाने के लिए उपयोग की जाती है। कहा जाने वाला फ़ंक्शन सरणी में धकेलने वाले तर्कों में से एक के रूप में निर्दिष्ट किया गया है। परिणामस्वरूप सरणी को प्रॉक्सी फ़ंक्शन को दिया जाता है जो वास्तव में फ़ंक्शन और तर्कों को बंद कर देता है।

इस उदाहरण में मैं struct या अन्य मेमोरी क्षेत्र को धक्का देने के लिए बंद करने के तर्कों के साथ-साथ एक सामान्य pushMem() फ़ंक्शन को तर्क देने के लिए कुछ प्रकार के विशिष्ट सहायक कार्यों को प्रदान करता हूं।

इस दृष्टिकोण को स्मृति क्षेत्र के आवंटन की आवश्यकता होती है जिसका उपयोग बंद डेटा के लिए किया जाता है। इस स्मृति क्षेत्र के लिए ढेर का उपयोग करना सबसे अच्छा होगा ताकि स्मृति प्रबंधन कोई समस्या न हो। यह भी मुद्दा है कि क्लोजर स्टोरेज मेमोरी एरिया को कितना बड़ा बनाना है ताकि आवश्यक तर्कों के लिए पर्याप्त जगह हो लेकिन इतना बड़ा न हो कि स्मृति में या स्टैक पर अतिरिक्त जगह अप्रयुक्त स्थान से ली जाती है।

मैंने थोड़ा अलग परिभाषित बंद संरचना के उपयोग के साथ प्रयोग किया है जिसमें बंद डेटा को संग्रहीत करने के लिए उपयोग किए जाने वाले सरणी के वर्तमान में उपयोग किए गए आकार के लिए एक अतिरिक्त फ़ील्ड है। इस अलग बंद संरचना को तब एक संशोधित सहायक कार्यों के साथ उपयोग किया जाता है जो सहायक संरचना के तर्क को जोड़ते समय अपने unsigned char * सूचक को बनाए रखने के लिए सहायक कार्यों के उपयोगकर्ता की आवश्यकता को हटा देता है।

नोट्स और प्रतिवाद

निम्न उदाहरण कार्यक्रम संकलित और विजुअल स्टूडियो 2013 इस नमूने से उत्पादन नीचे दी गई है साथ परीक्षण किया गया था। मुझे इस उदाहरण के साथ जीसीसी या क्लैंग के उपयोग के बारे में निश्चित नहीं है और न ही मुझे यकीन है कि 64 बिट कंपाइलर के साथ देखा जा सकता है क्योंकि मुझे लगता है कि मेरा परीक्षण 32 बिट एप्लिकेशन के साथ था। इसके अलावा यह दृष्टिकोण केवल उन कार्यों के साथ काम करेगा जो मानक सी घोषणा का उपयोग करते हैं जिसमें कॉलिंग फ़ंक्शन कैली रिटर्न (__cdecl और Windows API में __stdcall नहीं) के बाद स्टैक से तर्कों को पॉप करने में संभालता है।

चूंकि हम रन टाइम पर तर्क सूची बना रहे हैं और फिर प्रॉक्सी फ़ंक्शन को कॉल कर रहे हैं, तो यह दृष्टिकोण संकलक पर जांच करने की अनुमति नहीं देता है। इससे मेल खाने वाले पैरामीटर प्रकारों के कारण रहस्यमय विफलताओं का कारण बन सकता है जो संकलक ध्वजांकित करने में असमर्थ हैं।

उदाहरण आवेदन

// currytest.cpp : Defines the entry point for the console application. 
// 
// while this is C++ usng the standard C++ I/O it is written in 
// a C style so as to demonstrate use of currying with C. 
// 
// this example shows implementing a closure with C function pointers 
// along with arguments of various kinds. the closure is then used 
// to provide a saved state which is used with other functions. 

#include "stdafx.h" 
#include <iostream> 

// notation is used in the following defines 
// - tname is used to represent type name for a type 
// - cname is used to represent the closure type name that was defined 
// - fname is used to represent the function name 

#define CLOSURE_MEM(tname,size) \ 
    typedef struct { \ 
     union { \ 
      void *p; \ 
      unsigned char args[size + sizeof(void *)]; \ 
     }; \ 
    } tname; 

#define CLOSURE_ARGS(x,cname) *(cname *)(((x).args) + sizeof(void *)) 
#define CLOSURE_FTYPE(tname,m) ((tname((*)(...)))(m).p) 

// define a call function that calls specified function, fname, 
// that returns a value of type tname using the specified closure 
// type of cname. 
#define CLOSURE_FUNC(fname, tname, cname) \ 
    tname fname (cname m) \ 
    { \ 
     return ((tname((*)(...)))m.p)(CLOSURE_ARGS(m,cname)); \ 
    } 

// helper functions that are used to build the closure. 
unsigned char * pushPtr(unsigned char *pDest, void *ptr) { 
    *(void * *)pDest = ptr; 
    return pDest + sizeof(void *); 
} 

unsigned char * pushInt(unsigned char *pDest, int i) { 
    *(int *)pDest = i; 
    return pDest + sizeof(int); 
} 

unsigned char * pushFloat(unsigned char *pDest, float f) { 
    *(float *)pDest = f; 
    return pDest + sizeof(float); 
} 

unsigned char * pushMem(unsigned char *pDest, void *p, size_t nBytes) { 
    memcpy(pDest, p, nBytes); 
    return pDest + nBytes; 
} 


// test functions that show they are called and have arguments. 
int func1(int i, int j) { 
    std::cout << " func1 " << i << " " << j; 
    return i + 2; 
} 

int func2(int i) { 
    std::cout << " func2 " << i; 
    return i + 3; 
} 

float func3(float f) { 
    std::cout << " func3 " << f; 
    return f + 2.0; 
} 

float func4(float f) { 
    std::cout << " func4 " << f; 
    return f + 3.0; 
} 

typedef struct { 
    int i; 
    char *xc; 
} XStruct; 

int func21(XStruct m) { 
    std::cout << " fun21 " << m.i << " " << m.xc << ";"; 
    return m.i + 10; 
} 

int func22(XStruct *m) { 
    std::cout << " fun22 " << m->i << " " << m->xc << ";"; 
    return m->i + 10; 
} 

void func33(int i, int j) { 
    std::cout << " func33 " << i << " " << j; 
} 

// define my closure memory type along with the function(s) using it. 

CLOSURE_MEM(XClosure2, 256)   // closure memory 
CLOSURE_FUNC(doit, int, XClosure2) // closure execution for return int 
CLOSURE_FUNC(doitf, float, XClosure2) // closure execution for return float 
CLOSURE_FUNC(doitv, void, XClosure2) // closure execution for void 

// a function that accepts a closure, adds additional arguments and 
// then calls the function that is saved as part of the closure. 
int doitargs(XClosure2 *m, unsigned char *x, int a1, int a2) { 
    x = pushInt(x, a1); 
    x = pushInt(x, a2); 
    return CLOSURE_FTYPE(int, *m)(CLOSURE_ARGS(*m, XClosure2)); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int k = func2(func1(3, 23)); 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    XClosure2 myClosure; 
    unsigned char *x; 

    x = myClosure.args; 
    x = pushPtr(x, func1); 
    x = pushInt(x, 4); 
    x = pushInt(x, 20); 
    k = func2(doit(myClosure)); 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    x = myClosure.args; 
    x = pushPtr(x, func1); 
    x = pushInt(x, 4); 
    pushInt(x, 24);    // call with second arg 24 
    k = func2(doit(myClosure)); // first call with closure 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 
    pushInt(x, 14);    // call with second arg now 14 not 24 
    k = func2(doit(myClosure)); // second call with closure, different value 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    k = func2(doitargs(&myClosure, x, 16, 0)); // second call with closure, different value 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    // further explorations of other argument types 

    XStruct xs; 

    xs.i = 8; 
    xs.xc = "take 1"; 
    x = myClosure.args; 
    x = pushPtr(x, func21); 
    x = pushMem(x, &xs, sizeof(xs)); 
    k = func2(doit(myClosure)); 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    xs.i = 11; 
    xs.xc = "take 2"; 
    x = myClosure.args; 
    x = pushPtr(x, func22); 
    x = pushPtr(x, &xs); 
    k = func2(doit(myClosure)); 
    std::cout << " main (" << __LINE__ << ") " << k << std::endl; 

    x = myClosure.args; 
    x = pushPtr(x, func3); 
    x = pushFloat(x, 4.0); 

    float dof = func4(doitf(myClosure)); 
    std::cout << " main (" << __LINE__ << ") " << dof << std::endl; 

    x = myClosure.args; 
    x = pushPtr(x, func33); 
    x = pushInt(x, 6); 
    x = pushInt(x, 26); 
    doitv(myClosure); 
    std::cout << " main (" << __LINE__ << ") " << std::endl; 

    return 0; 
} 

टेस्ट उत्पादन

इस नमूने कार्यक्रम से उत्पादन। कंस्ट्रैसिस में संख्या मुख्य रूप से लाइन नंबर है जहां फ़ंक्शन कॉल किया जाता है।

func1 3 23 func2 5 main (118) 8 
func1 4 20 func2 6 main (128) 9 
func1 4 24 func2 6 main (135) 9 
func1 4 14 func2 6 main (138) 9 
func1 4 16 func2 6 main (141) 9 
fun21 8 take 1; func2 18 main (153) 21 
fun22 11 take 2; func2 21 main (161) 24 
func3 4 func4 6 main (168) 9 
func33 6 26 main (175) 
संबंधित मुद्दे