20

誰もがまともな例を持っています、できれば実用的/有用です、彼らは概念を実証するために投稿することができますか?

4

6 に答える 6

21

(編集:物事を始めるための小さなOcaml FP公案

カレーの公案

Jacques Garrigue のところに生徒が来て、「カレーが何に役立つのか分からない」と言いました。ジャックは、「あなたの好きな食事と好きなデザートを教えてください」と答えました. 困惑した学生は、お好み焼きと寒天が好きだと答えたが、彼のお気に入りのレストランは素晴らしいお好み焼きを提供していたが、彼らの寒天は翌朝いつも腹痛を引き起こした. そこでジャックは学生を連れて、その学生の好みに負けないくらい美味しいお好み焼きを提供するレストランに連れて行った。それから町の向こう側にある素晴らしい寒天を作る店に連れて行った.そこで学生は残りの食欲を喜んで使った. 学生は満腹でしたが、悟りはありませんでした...翌朝目が覚め、胃が元気になるまで。

私の例では、コードの再利用とカプセル化のための使用について説明します。これは、これらを見れば明らかであり、さまざまな状況に適用できる具体的で単純な例を示しているはずです。

木の上にマップを作成したいと考えています。複数の引数が必要な場合は、この関数をカリー化して各ノードに適用できます。これは、最終引数としてノードで 1 つを適用するためです。カリー化する必要はありませんが、別の関数を書くのは (この関数が他の変数を持つ他のインスタンスで使用されていると仮定して) 無駄になります。

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

したがって、この単純なケースは説得力がありません。しかし、言語をより多く使用し、自然にこれらの状況に出くわすと、それは本当に強力です。カリー化としてコードを再利用する別の例。素数を作成する再帰関係。そこには非常に多くの類似点があります:

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

さて、rowland と cloitre はカリー化された関数です。これらには自由な変数があり、f_recurrence を知らず、気にすることなく、そのシーケンスの任意のインデックスを取得できます。

于 2008-08-12T04:46:33.480 に答える
15

前の例で質問に答えましたが、カリー化がF#プログラミングにどのように役立つかを示す2つの簡単な例を次に示します。

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];;
于 2008-08-12T16:07:24.170 に答える
10

カリー化は、複数の引数を持つ関数を単一引数関数のチェーンに変換するプロセスを表します。引数が 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# 関数の引数の順序が最初は少し奇妙に見える理由の 1 つです。別の 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"

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)

F# での部分関数適用の使用については、他の回答で既に説明されているため、詳しくは説明しません。

于 2013-02-09T05:22:59.297 に答える
3

これはかなり単純なプロセスです。関数を取り、その引数の 1 つをバインドして、新しい関数を返します。例えば:

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

単純な concatStrings 関数をカリー化することで、任意の文字列の先頭に DOS スタイルのコマンド プロンプトを簡単に追加できます。本当に便利!

わかりました、そうではありません。私が見つけたより便利なケースは、ストリームのような方法でデータを返す関数を作成したい場合です。

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() を呼び出すのではなく、自分の下から変更できない関数を持っていることです。

于 2008-08-12T04:26:41.523 に答える
3

リストに関数をマップできることを知っていますか? たとえば、関数をマッピングして、リストの各要素に 1 つ追加します。

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

演算子を使用して引数に 1 を追加する関数を作成したため、これは実際には既にカリー(+)化を使用していますが、リストのリストの同じ関数をマップするように変更することで、この例をもう少し絞ることができます。

> 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]]
于 2010-05-10T03:42:30.687 に答える
1

C# でカリー化をシミュレートする良い例をブログで紹介しました。要点は、既存のマルチパラメーター関数から、パラメーターに対して閉じられた関数を作成できることです (私の例では、特定の自治体の値に対して閉じられた消費税を計算する関数を作成します)。

ここで魅力的なのは、クック郡で消費税を計算するための特別な関数を個別に作成する代わりに、実行時に関数を動的に作成 (および再利用) できることです。

于 2011-06-02T23:09:47.580 に答える