2008-08-12 13 views

उत्तर

19

(संपादित करें: एक छोटा सा Ocaml FP Koan बातें शुरू करने के लिए)

Currying की कोआन (खाने के बारे में एक koan, कि खाने के बारे में नहीं है)

एक छात्र आया जैक्स गैरीग के लिए और कहा, "मुझे समझ में नहीं आता कि किस चीज के लिए अच्छा है।" जैक्स ने जवाब दिया, "मुझे अपना पसंदीदा भोजन और अपना पसंदीदा मिठाई बताएं"। परेशान छात्र ने जवाब दिया कि उन्हें ओकोनोमियाकी और कांटन पसंद आया, लेकिन जब उनके पसंदीदा रेस्तरां ने ओकोनोमियाकी की सेवा की, तो उनके कंटन ने उन्हें अगली सुबह पेट में दर्द दिया। तो जैक्स ने छात्र को एक रेस्तरां में खाने के लिए लिया जो कि छात्र के पसंदीदा के रूप में अच्छी तरह से ओकोनोमियाकी की सेवा करता था, फिर उसे शहर भर में एक दुकान में ले गया जिसने उत्कृष्ट कंटन बनाया जहां छात्र खुशी से अपनी भूख को लागू करते थे। छात्र बैठे थे, लेकिन वह प्रबुद्ध नहीं था ... अगली सुबह जब वह जाग गया और उसका पेट ठीक लगा।

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

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

let sample_tree2 = tree_map (fun x -> x * 3) sample_tree 

तो इस सरल मामले कायल नहीं है: के रूप में

type 'a tree = E of 'a | N of 'a * 'a tree * 'a tree 
let rec tree_map f tree = match tree with 
    | N(x,left,right) -> N(f x, tree_map f left, tree_map f right) 
    | E(x) -> E(f x) 

let sample_tree = N(1,E(3),E(4) 
let multiply x y = x * y 
let sample_tree2 = tree_map (multiply 3) sample_tree 

लेकिन यह एक ही है। यह वास्तव में तब भी शक्तिशाली है जब आप भाषा का अधिक उपयोग करते हैं और स्वाभाविक रूप से इन परिस्थितियों में आते हैं। करीबी के रूप में कुछ कोड पुन: उपयोग के साथ दूसरा उदाहरण। एक recurrence relation to create prime numbers।वहाँ में समानता की भयंकर बहुत:

let rec f_recurrence f a seed n = 
    match n with 
    | a -> seed 
    | _ -> let prev = f_recurrence f a seed (n-1) in 
      prev + (f n prev) 

let rowland = f_recurrence gcd 1 7 
let cloitre = f_recurrence lcm 1 1 

let rowland_prime n = (rowland (n+1)) - (rowland n) 
let cloitre_prime n = ((cloitre (n+1))/(cloitre n)) - 1 

ठीक है, अब रोलैंड और Cloitre curried कार्य हैं, क्योंकि वे मुक्त चर है, और यह जानने की या f_recurrence के बारे में चिंता किए बिना अनुक्रम है कि हम किसी भी सूचकांक मिल सकती है।

+3

यह उत्तर आंशिक फ़ंक्शन एप्लिकेशन का वर्णन करता है, [जो करी से संबंधित है, लेकिन एक ही बात नहीं है] (http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application) – phoog

2

यह एक काफी सरल प्रक्रिया है। एक समारोह लें, अपने तर्कों में से एक को बांधें और एक नया कार्य वापस करें। उदाहरण के लिए:

let concatStrings left right = left + right 
let makeCommandPrompt= appendString "c:\> " 

अब सरल concatStrings समारोह currying से, आप आसानी से एक डॉस शैली कमांड प्रॉम्प्ट किसी भी स्ट्रिंग के सामने से जोड़ सकते हैं! वास्तव में उपयोगी!

ठीक है, वास्तव में नहीं। मुझे लगता है कि एक और उपयोगी मामला यह है कि जब मैं एक ऐसा फ़ंक्शन बनाना चाहता हूं जो मुझे स्ट्रीम में डेटा देता है।

let readDWORD array i = array[i] | array[i + 1] << 8 | array[i + 2] << 16 | 
    array[i + 3] << 24 //I've actually used this function in Python. 

इसके बारे में सुविधाजनक बात यह है कि बजाय, बात की इस तरह के लिए एक पूरे वर्ग बनाने निर्माता बुला, obj.readDWORD() कॉल की तुलना में, तुम सिर्फ एक समारोह है कि से बाहर उत्परिवर्तित नहीं किया जा सकता है तुम्हारे नीचे।

+2

यह उत्तर आंशिक फ़ंक्शन एप्लिकेशन का वर्णन करता है, [जो करी से संबंधित है, लेकिन एक ही चीज़ नहीं है] (http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application) – phoog

14

जबकि पिछले उदाहरणों में सवाल का जवाब दिया गया है, तो एफ # प्रोग्रामिंग के लिए करीइंग लाभकारी कैसे हो सकता है, इसके दो सरल उदाहरण यहां दिए गए हैं।

open System.IO 

let appendFile (fileName : string) (text : string) = 
    let file = new StreamWriter(fileName, true) 
    file.WriteLine(text) 
    file.Close() 

// Call it normally  
appendFile @"D:\Log.txt" "Processing Event X..." 

// If you curry the function, you don't need to keep specifying the 
// log file name. 
let curriedAppendFile = appendFile @"D:\Log.txt" 

// Adds data to "Log.txt" 
curriedAppendFile "Processing Event Y..." 

और भूलें कि आप फ़ंक्शन के प्रिंटफ परिवार को करी कर सकते हैं! विवाहित संस्करण में, लैम्ब्डा की विशिष्ट कमी को ध्यान दें।

// Non curried, Prints 1 2 3 
List.iter (fun i -> printf "%d " i) [1 .. 3];; 

// Curried, Prints 1 2 3 
List.iter (printfn "%d ") [1 .. 3];; 
+3

यह उत्तर आंशिक फ़ंक्शन एप्लिकेशन का वर्णन करता है, [जो करी से संबंधित है, लेकिन एक ही चीज़ नहीं है] (http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application) – phoog

2

क्या आप जानते हैं कि आप किसी सूची में फ़ंक्शन को मानचित्र कर सकते हैं? उदाहरण के लिए, एक सूची के प्रत्येक तत्व के लिए एक जोड़ने के लिए एक समारोह मानचित्रण:

> List.map ((+) 1) [1; 2; 3];; 
val it : int list = [2; 3; 4] 

यह वह जगह है वास्तव में पहले से ही currying का उपयोग कर, क्योंकि (+) ऑपरेटर अपने तर्क को एक जोड़ने के लिए एक समारोह बनाने के लिए इस्तेमाल किया गया था लेकिन आप एक निचोड़ कर सकते हैं

> List.map (List.map ((+) 1)) [[1; 2]; [3]];; 
val it : int list = [[2; 3]; [4]] 

आप currying आंशिक रूप से इन कार्यों लागू नहीं कर सकता है बिना और इसके बजाय कुछ इस तरह लिखने के लिए होता है:

> List.map((fun xs -> List.map((fun n -> n + 1), xs)), [[1; 2]; [3]]);; 
val it : int list = [[2; 3]; [4]] 
थोड़ा यह फेरबदल सूचियों की एक सूची का एक ही समारोह को मैप करने के द्वारा इस उदाहरण का अधिकाधिक लाभ उठाएं
+2

यह उत्तर आंशिक फ़ंक्शन एप्लिकेशन का वर्णन करता है, [जो करी से संबंधित है, लेकिन एक ही चीज़ नहीं है] (http://en.wikipedia.org/wiki/ करीबी # Contrast_with_partial_function_application) – phoog

+0

@phoog यह उत्तर सही ढंग से स्पष्टीकरण ऐन्स कि "बिना घुमाए आप इन कार्यों को आंशिक रूप से लागू नहीं कर सके"। –

1

मैंने सी # on my blog में करी को अनुकरण करने का एक अच्छा उदाहरण दिया। यह बात यह है कि आप मौजूदा मल्टी-पैरामीटर फ़ंक्शन से बाहर एक पैरामीटर पर बंद एक फ़ंक्शन बना सकते हैं (मेरे उदाहरण में किसी दिए गए नगर पालिका के मूल्य पर बंद कर कर की गणना करने के लिए एक फ़ंक्शन बनाएं)।

कुक काउंटी में बिक्री कर की गणना के लिए विशेष रूप से एक अलग कार्य करने के बजाय अपील करना क्या है, आप रनटाइम पर गतिशील रूप से फ़ंक्शन (और पुन: उपयोग) कर सकते हैं।

+0

+1, हालांकि आपका ब्लॉग लिंक टूटा हुआ प्रतीत होता है, मुझे संदेह है कि आपका उदाहरण सी # में वास्तविक फ़ंक्शन करीइंग दिखाता है, नकल नहीं। यह उत्तर केवल एकमात्र ऐसा है जो वास्तव में उस चीज के रूप में घुमाव का वर्णन करता है जो आंशिक फ़ंक्शन एप्लिकेशन को भ्रमित करने के बजाय आंशिक फ़ंक्शन एप्लिकेशन को सक्षम बनाता है। – phoog

+0

मैंने हाल ही में सामुदायिक सर्वर से साइटफिनिटी में अपना ब्लॉग माइग्रेट किया है। मुझे अपने पुराने ब्लॉग के डेटा को आयात करने के लिए एक उपयोगिता लिखने के लिए नहीं मिला है :(लेकिन मैंने करी को अनुकरण करने के लिए फ़ंक्शन ओवरलोडिंग का उपयोग किया। यदि आप दोनों पैरामीटर पास करते हैं, तो आप परिणाम प्राप्त करते हैं, यदि आप एक पास करते हैं, तो आपको एक func मिलता है जो लेता है दूसरा और परिणाम देता है। सही करी के रूप में सुरुचिपूर्ण नहीं है लेकिन यह काम करता है;) –

9

करीबी एक तर्क को एकल तर्क कार्यों की एक श्रृंखला में एकाधिक तर्कों के साथ बदलने की प्रक्रिया का वर्णन करता है। सी # में उदाहरण के लिए, एक तीन तर्क समारोह के लिए:

Func<T1, Func<T2, Func<T3, T4>>> Curry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f) 
{ 
    return a => b => c => f(a, b, c); 
} 

void UseACurriedFunction() 
{ 
    var curryCompare = Curry<string, string, bool, int>(String.Compare); 
    var a = "SomeString"; 
    var b = "SOMESTRING"; 
    Console.WriteLine(String.Compare(a, b, true)); 
    Console.WriteLine(curryCompare(a)(b)(true)); 

    //partial application 
    var compareAWithB = curryCompare(a)(b); 
    Console.WriteLine(compareAWithB(true)); 
    Console.WriteLine(compareAWithB(false)); 
} 

अब, बूलियन तर्क शायद नहीं तर्क आप सबसे अधिक संभावना एक आंशिक आवेदन के साथ खुला छोड़ना चाहते हैं चाहते हैं। यह एक कारण है कि एफ # कार्यों में तर्कों का क्रम पहले थोड़ा अजीब लग सकता है। के एक अलग सी # करी समारोह को परिभाषित करते हैं:

void UseADifferentlyCurriedFunction() 
{ 
    var curryCompare = BackwardsCurry<string, string, bool, int>(String.Compare); 

    var caseSensitiveCompare = curryCompare(false); 
    var caseInsensitiveCompare = curryCompare(true); 

    var format = Curry<string, string, string, string>(String.Format)("Results of comparing {0} with {1}:"); 

    var strings = new[] {"Hello", "HELLO", "Greetings", "GREETINGS"}; 

    foreach (var s in strings) 
    { 
     var caseSensitiveCompareWithS = caseSensitiveCompare(s); 
     var caseInsensitiveCompareWithS = caseInsensitiveCompare(s); 
     var formatWithS = format(s); 

     foreach (var t in strings) 
     { 
      Console.WriteLine(formatWithS(t)); 
      Console.WriteLine(caseSensitiveCompareWithS(t)); 
      Console.WriteLine(caseInsensitiveCompareWithS(t)); 
     } 
    } 
} 

सी # में इन उदाहरणों क्यों कर रहे हैं:

Func<T3, Func<T2, Func<T1, T4>>> BackwardsCurry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f) 
{ 
    return a => b => c => f(c, b, a); 
} 

अब, हम कुछ और भी अधिक उपयोगी कर सकते हैं? क्योंकि एफ # में, फ़ंक्शन घोषणाएं डिफ़ॉल्ट रूप से curried हैं। आपको आमतौर पर कार्यों को करी करने की आवश्यकता नहीं होती है; वे पहले से ही चिंतित हैं। इसका मुख्य अपवाद फ्रेमवर्क विधियों और अन्य अधिभारित फ़ंक्शंस है, जो उनके एकाधिक तर्क वाले टुपल लेते हैं। इसलिए आप ऐसे कार्यों को करी करना चाहते हैं, और वास्तव में, मैं इस प्रश्न पर आया था जब मैं लाइब्रेरी फ़ंक्शन की तलाश में था जो ऐसा करेगा।मैं इसे याद आ रही है लगता है (यदि वास्तव में यह है), क्योंकि इसे लागू करने के बहुत तुच्छ है:

let curry f a b c = f(a, b, c) 

//overload resolution failure: there are two overloads with three arguments. 
//let curryCompare = curry String.Compare 

//This one might be more useful; it works because there's only one 3-argument overload 
let backCurry f a b c = f(c, b, a) 
let intParse = backCurry Int32.Parse 
let intParseCurrentCultureAnyStyle = intParse CultureInfo.CurrentCulture NumberStyles.Any 
let myInt = intParseCurrentCultureAnyStyle "23" 
let myOtherInt = intParseCurrentCultureAnyStyle "42" 

String.Compare साथ विफलता के आसपास पाने के लिए, जहाँ तक मैं बता सकता हूँ वहाँ जो 3 निर्दिष्ट करने के लिए कोई रास्ता नहीं है के बाद से तर्क लेने के लिए ओवरलोड, आप एक गैर सामान्य समाधान का उपयोग कर सकते हैं:

let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b) 
let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b) 

मैं एफ # में आंशिक समारोह आवेदन के उपयोग के बारे में विस्तार में जाने नहीं होगा क्योंकि अन्य उत्तर है कि कवर किया है पहले से ही।

+1

यह स्वीकार्य उत्तर होना चाहिए –