7

特定の方法で関数を構成したいと思います。これらの 2 つの関数を疑似コードで検討してください (F# ではありません)。

F1 = x + y
F2 = F1 * 10 // note I did not specify arguments for F1, 'reverse curry' for lack of a better word

私が F# でやりたいことは、それを理解することです。

let F1 x y = x + y
//val F1 : int -> int -> int

コードlet F2 = F1 * 10は F1: と同じ署名を与えval F2 : int -> int -> int、呼び出しF2 2 3は 50: (2 + 3) * 10 になります。それはかなり賢いでしょう...

何が起こるかはかなり異なります。最初の行は期待どおりです。

let F1 x y = x + y
//val F1 : int -> int -> int

しかし、2 行目を追加すると、let F2 = F1 * 10F# がスローされます。the type int does not match the type 'a -> 'b -> 'cそれはそれとそのF1を今訴えていrequires member ( + )ます。

もちろん、次のように綴ることもできます。

let F1(x, y) = x + y
let F2(x, y) = F1(x, y) * 10

しかし、今では C# を使ったほうがよかったかもしれません。タプルされた引数は、F# の優雅さの多くを壊します。また、私の実際の関数 F1 と F2 には 2 つだけよりも多くの引数があるため、これはまさに私が F# を使用して回避したかったことと同じです。次のように言うと、より自然になります。

let F1 x y = x + y
let F2 = F1 * 10

私が(ほぼ)それを行う方法はありますか?

追加のクレジット: これらのエラー メッセージは正確にどうなっているのですか? let F2 = F1 * 102 行目で 1 行目の入力が変わるのはなぜですか?

ご意見ありがとうございます。

ゲルト・ヤン

update 説明されていることを (ほぼ) 実行する 2 つのアプローチ。

タプルを使用するもの。2 行目は少し風変わりに見えますが、問題なく動作します。小さな欠点は、現在カリー化を使用できないか、さらに風変わりなコードを追加する必要があることです。

let F1 (a, b) = a + b
let F2 = F1 >> (*) 10

F2(2, 3) // returns 50

別のアプローチは、レコードを使用することです。これはもう少し単純明快で、一見すると簡単に理解できますが、より多くのコードと式が必要になります。F# の優雅さの一部を取り除き、C# のように見えます。

type Arg (a, b) =
    member this.A = a
    member this.B = b

let F1 (a:Arg) = a.A + a.B
let F2 (a:Arg) = F1(a) * 10

F2 (Arg(2, 3)) // returns 50
4

3 に答える 3

5

一般に、これにはパターンはありません。larsmans が提案するようにコンビネータ ( curryand などuncurry) を使用することも 1 つのオプションですが、結果は明示的なバージョンよりも読みにくく、長くなると思います。

この特定のパターンを頻繁に使用する場合は、関数 (2 つのパラメーターを持つ) をスカラーで乗算する演算子を定義できます。

let ( ** ) f x = fun a b -> (f a b) * x

let F1 x y = x + y
let F2 = F1 ** 10

*残念ながら、標準の数値演算子 (など) の実装を既存の型 ( など) に追加することはできません'a -> 'b -> int。ただし、これは非常に頻繁に要求されます (また、他の用途にも役立ちます)。Invokeまたは、オーバーロードされた数値演算子を提供する (および関数を実行するためのメソッドを含む) オブジェクトに関数をラップすることもできます。

これの適切な名前はリフティングだと思います- *(整数で作業している)演算子を、整数を返す関数で動作するバージョンに持ち上げています。*これは、null 許容型を使用するときに C# コンパイラで行われるリフティングに似ています。

エラーメッセージを説明するには-式について不平を言いますF1 * 10

エラー FS0001: 型 'int' が型 ''a -> 'b -> 'c' と一致しません

*コンパイラが演算子のインスタンス化を見つけようとしているということだと思います。右側から、これが であるべきだintと判断し、左側もまたあるべきだと考えますint- しかし、実際には 2 つの引数の関数です - のようなもの'a -> 'b -> c'です。

于 2011-05-04T12:23:30.367 に答える
4

それはむしろ賢いでしょう...

型システムの地獄を打ち負かすほど賢い。必要なのは、APL のような配列プログラミングです。

私が(ほぼ)それを行う方法はありますか?

私は F# を話せませんが、Haskell では uncurryを使用F1し、次に で構成し*10、次にcurry を使用します。

f2 = curry ((*10) . uncurry f1)

F# などの ML の方言では、次のようになります。

let curry f x y = f (x,y)
let uncurry f (x,y) = f x y

let mult x y = x * y

let F1 x y = x + y
let F2 = curry (uncurry F1 >> mult 10)

( と が F# 標準ライブラリにあるかどうかわからなかったのでcurry、それらを定義しました。 を定義せずuncurryに を部分適用するもっときれいな方法があるかもしれません。)*mult

于 2011-05-04T12:08:04.547 に答える
1

ところで、ポイントフリー(またはこの場合は無意味)アプローチを使用すると、これらの関数を次のように定義できます。

let F1 = (+)
let F2 = (<<)((*)10) << F1
于 2011-05-06T13:58:05.020 に答える