F#から始めたばかりで、カリー化を使用して1番目のパラメーターを関数にプリロードする方法を確認します。しかし、2番目、3番目、またはその他のパラメーターを使用して、どのようにそれを行うのでしょうか。これを簡単にするためにパラメータに名前を付けますか?パラメータに名前を付けた他の関数型言語や、カリー化をパラメータの順序に無関心にする他の方法はありますか?
5 に答える
通常、ラムダを使用します。
fun x y z -> f x y 42
'f'のような関数ですが、3番目のパラメーターは42にバインドされています。
引数を並べ替えるコンビネータ(誰かがコメントでHaskellの「フリップ」に言及したように)を使用することもできますが、混乱することがあります。
ほとんどのカレー関数は、部分的に適用される可能性が最も高い引数が最初に来るように記述されていることに注意してください。
F#はメソッドのパラメーターに名前を付けています(let-bound関数値ではありません)が、名前は「tupled」パラメーターに適用されます。名前付きのカレーパラメータはあまり意味がありません。2つの引数のカリー化関数'f'がある場合、
let g = f
let h x y = f x y
その場合、「g」または「h」は「f」の代わりに使用できますが、「named」パラメーターはこれを必ずしも真とはしません。つまり、「名前付きパラメーター」は言語設計の他の側面とうまく相互作用しない可能性があり、私は個人的に「ファーストクラスのカリー化関数値」とうまく相互作用する「名前付きパラメーター」の優れた設計を知りません。
F#のベースとなった言語であるOCamlには、任意の順序で指定できるラベル付き(およびオプションの)引数があり、それらの引数の名前に基づいて関数を部分的に適用できます。F#にこの機能があるとは思わない。
flip
Haskellの関数のようなものを作成してみてください。引数リストで引数をさらにジャンプするバリアントを作成するのはそれほど難しいことではありません。
let flip f a b = f b a
let flip2 f a b c = f b c a
let flip3 f a b c d = f b c d a
完全を期すために、そして他の関数型言語について質問したので、これは、おそらくF#の「母」であるOCamlでそれを行う方法です。
$ ocaml
# let foo ~x ~y = x - y ;;
val foo : x:int -> y:int -> int = <fun>
# foo 5 3;;
- : int = 2
# let bar = foo ~y:3;;
val bar : x:int -> int = <fun>
# bar 5;;
- : int = 2
したがって、OCamlでは、名前を使用するだけで、任意の名前付きパラメーターをハードコーディングできます(y
上記の例)。
ご存知のように、Microsoftはこの機能を実装しないことを選択しました...私の謙虚な意見では、それは「言語設計の他の側面との不十分な相互作用」についてではありません...これには追加の努力が必要になる可能性が高いです(言語の実装において)そしてそれが言語を世界にもたらすのに遅れをもたらすでしょう-実際、(a)OCamlからの「ステップダウン」に気付く人はごくわずかであり、(b)とにかく名前付き関数の引数を使用します。
私は少数派であり、それらを使用していますが、実際には、ローカル関数バインディングを使用してF#で簡単にエミュレートするものです。
let foo x y = x - y
let bar x = foo x 3
bar ...
何も宣言せずにこれを行うことは可能ですが、ラムダまたはカスタム関数がおそらくより良い解決策であるというブライアンに同意します。
除算や減算の部分適用にこれが最も頻繁に必要であることがわかりました。
> let halve = (/) >> (|>) 2.0;;
> let halfPi = halve System.Math.PI;;
val halve : (float -> float)
val halfPi : float = 1.570796327
一般化するために、関数を宣言することができますapplySecond
:
> let applySecond f arg2 = f >> (|>) arg2;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)
ロジックに従うには、次のように関数を定義すると役立つ場合があります。
> let applySecond f arg2 =
- let ff = (|>) arg2
- f >> ff;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)
今はからへf
の関数です。これは、からの関数で構成されます。この関数は、フォワードパイプライン演算子への部分適用の結果です。この関数は、渡された特定の値を引数に適用します。したがって、で作成すると、引数に指定された値を使用する関数from toを取得します。これは、まさに私たちが望んでいたことです。'a
'b -> 'c
ff
'b -> 'c
'c
arg2
'b
arg2
f
ff
'a
'c
'b
上記の最初の例を次の例と比較してください。
> let halve f = f / 2.0;;
> let halfPi = halve System.Math.PI;;
val halve : f:float -> float
val halfPi : float = 1.570796327
これらも比較してください:
let filterTwoDigitInts = List.filter >> (|>) [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 1)
let evenTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 0)
let filterTwoDigitInts f = List.filter f [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 1)
let evenTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 0)
または、以下を比較します。
let someFloats = [0.0 .. 10.0]
let theFloatsDividedByFour1 = someFloats |> List.map ((/) >> (|>) 4.0)
let theFloatsDividedByFour2 = someFloats |> List.map (fun f -> f / 4.0)
ラムダバージョンは読みやすいようです。
functools.partial
Pythonでは、、またはラムダを使用できます。Pythonは引数に名前を付けています。
functools.partial
最初の位置引数と任意の名前付き引数を指定するために使用できます。
from functools import partial
def foo(a, b, bar=None):
...
f = partial(foo, bar='wzzz') # f(1, 2) ~ foo(1, 2, bar='wzzz')
f2 = partial(foo, 3) # f2(5) ~ foo(3, 5)
f3 = lambda a: foo(a, 7) # f3(9) ~ foo(9, 7)