سؤال

أي شخص يكون مثال ، ويفضل أن تكون عملية مفيدة ، يتمكنوا بعد مما يدل على المفهوم ؟

هل كانت مفيدة؟

المحلول

(تحرير:صغير Ocaml FP Koan لبدء تشغيل الأشياء)

Koan من التمشيط (koan عن الطعام ، وهذا ليس عن الطعام)

طالب جاء إلى جاك Garrigue وقال: "أنا لا أفهم ما التمشيط هو جيد." جاك فأجاب: "قل لي وجبتك المفضلة و الحلوى المفضلة لديك".حيرة الطالب أجاب أنه يحب أوكونوميياكي و kanten ، ولكن حين مطعمه المفضل خدم كبيرة أوكونوميياكي ، kanten دائما أعطاه ألم في المعدة في صباح اليوم التالي.اذا جاك أخذت الطالب لتناول الطعام في مطعم الذي يخدم أوكونوميياكي كل بت جيدة مثل الطالب المفضل ، ثم أخذت له في جميع أنحاء المدينة إلى المحل الذي قدم ممتازة kanten فيها الطالب بسعادة تطبيق ما تبقى من شهيته.الطالب كان متخم ، لكنه لم يكن المستنير ...حتى صباح اليوم التالي عندما استيقظ و بطنه شعر ناعم.

أمثلة بلدي سوف تغطي استخدامه لإعادة استعمالها و التغليف كود.هذا هو إلى حد ما واضحة بمجرد إلقاء نظرة على هذه وينبغي أن تعطيك ملموسة ، مثال بسيط التي يمكنك التفكير في تطبيق في العديد من الحالات.

نحن نريد أن نفعل الخريطة فوق شجرة.يمكن أن يكون هذا العمل بالكاري و تطبيقها على كل عقدة إذا كان يحتاج أكثر من حجة واحدة-منذ كنا تطبيق واحد في عقدة كما انها حجة النهائية.فإنه لا يجب أن يكون بالكاري, لكن الكتابة آخر وظيفة (على افتراض هذه الدالة تستخدم في حالات أخرى مع المتغيرات الأخرى) سيكون مضيعة للوقت.

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

ولكن هذا هو نفسه:

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

حتى هذه الحالة بسيطة ليست مقنعة.هو حقا على الرغم من قوة بمجرد استخدام لغة أكثر وبطبيعة الحال تأتي عبر هذه الحالات.المثال الآخر مع بعض إعادة استخدام التعليمات البرمجية كما التمشيط.A تكرار علاقة لخلق الأعداد الأولية.الكثير من التشابه في:

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 هي وظائف بالكاري, لأن لديهم المتغيرات الحرة ، يمكننا الحصول على أي مؤشر من تسلسل دون معرفة أو القلق حول f_recurrence.

نصائح أخرى

في حين أن الأمثلة السابقة أجاب السؤال هنا نوعان من أبسط الأمثلة على كيفية التمشيط يمكن أن تكون مفيدة بالنسبة البرمجة# F.

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..."

و لا تنسى يمكنك الكاري Printf الأسرة من وظيفة!في بالكاري إصدار إشعار نقص واضح في امدا.

// 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];;

التمشيط يصف عملية تحويل وظيفة مع حجج متعددة في سلسلة واحدة-حجة وظائف.مثلا في C#, لمدة ثلاث الحجة الدالة:

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));
}

الآن حجة منطقية وربما لا الحجة كنت على الأرجح تريد أن تترك مفتوحة مع تطبيق جزئي.وهذا هو أحد الأسباب لماذا ترتيب الحجج في F# وظائف يمكن أن تبدو غريبة قليلا في البداية.دعونا تعريف مختلف C# الكاري وظيفة:

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);
}

الآن, يمكننا أن نفعل شيئا أكثر من ذلك بقليل مفيدة:

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));
        }
    }
}

لماذا هذه الأمثلة في C# ؟ لأنه في# F, وظيفة الإعلانات بالكاري بشكل افتراضي.أنت لا تحتاج عادة إلى الكاري المهام ؛ انهم بالفعل بالكاري.الرئيسية استثناء هذا الإطار أساليب أخرى مثقلة الوظائف ، والتي تأخذ المجموعة التي تحتوي على حجج متعددة.لذا قد ترغب في الكاري هذه المهام, و, في الواقع, لقد جاء على هذا السؤال عندما كنت تبحث عن وظيفة المكتبة التي من شأنها أن تفعل هذا.أعتقد أنه مفقود (إذا كان الواقع هو) لأنها تافهة جدا لتنفيذ ما يلي:

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"

للحصول على جميع أنحاء فشل مع سلسلة.قارن منذ بقدر ما استطيع ان اقول لا يوجد طريقة لتحديد 3-حجة الزائد لاختيار يمكنك استخدام غير الحل العام:

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

لن أخوض في التفاصيل حول استخدامات جزئية وظيفة التطبيق و# لأن إجابات أخرى قد غطت بالفعل.

انها عملية بسيطة إلى حد ما.تأخذ دالة ربط واحدة من الحجج و العودة وظيفة جديدة.على سبيل المثال:

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

الآن من خلال التمشيط بسيطة concatStrings الوظيفة ، يمكنك بسهولة إضافة دوس نمط موجه الأوامر إلى الأمام من أي سلسلة!من المفيد حقا!

حسنا, لا حقا.أكثر فائدة القضية وجدته عندما كنت تريد أن تجعل الدالة التي ترجع لي البيانات في تيار المنوال.

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.

مريحة جزء عن ذلك هو أنه بدلا من إنشاء فئة كاملة عن هذا النوع من الشيء ، استدعاء منشئ ، داعيا الكائنات.readDWORD () ، لديك مجرد وظيفة لا يمكن أن يكون تحور من تحت لك.

كنت أعلم أنك يمكن أن خريطة دالة على قائمة ؟ على سبيل المثال ، في تعيين وظيفة إضافة إلى كل عنصر من عناصر القائمة:

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

هذا هو في الواقع بالفعل باستخدام التمشيط لأن (+) المشغل تم استخدامه لإنشاء وظيفة إضافة إلى الحجة ولكن يمكنك ضغط أكثر قليلا من هذا المثال عن طريق تغيير إلى خريطة نفس الوظيفة من قائمة من القوائم:

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

دون التمشيط لا يمكنك جزئيا تطبيق هذه الوظائف يجب أن أكتب شيئا مثل هذا بدلا من ذلك:

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

أعطى مثال جيد على محاكاة التمشيط في C# على بلدي بلوق.جوهر هو أنه يمكنك إنشاء دالة مغلقة على معلمة (في بلدي على سبيل المثال إنشاء دالة لحساب ضريبة المبيعات أغلق على قيمة معينة بلدية)من القائمة متعددة المعلمة وظيفة.

ما هو جذابة هنا بدلا من الاضطرار إلى جعل وظيفة منفصلة خصيصا لحساب ضريبة المبيعات في مقاطعة كوك, يمكنك إنشاء (وإعادة) وظيفة بشكل حيوي في وقت التشغيل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top