「カリー化」の版間の差分
div の例を実装例に移動、数式の修正 |
|||
15行目: | 15行目: | ||
この例をSchemeとJavaScriptで実装した例を示す。 |
この例をSchemeとJavaScriptで実装した例を示す。 |
||
< |
<syntaxhighlight lang="scheme"> |
||
(define div (lambda (x y) (/ x y))) |
(define div (lambda (x y) (/ x y))) |
||
;(div 1 3) == (/ 1 3), これはカリー化ではない。 |
;(div 1 3) == (/ 1 3), これはカリー化ではない。 |
||
28行目: | 28行目: | ||
(inv 2) |
(inv 2) |
||
;=> 1/2 |
;=> 1/2 |
||
</syntaxhighlight> |
|||
</source> |
|||
< |
<syntaxhighlight lang="javascript"> |
||
function div(x, y) { return x / y; } |
function div(x, y) { return x / y; } |
||
//div(1, 3) == 1 / 3, これはカリー化ではない。 |
//div(1, 3) == 1 / 3, これはカリー化ではない。 |
||
40行目: | 40行目: | ||
console.log(inv(2)); |
console.log(inv(2)); |
||
//=> 1/2 = 0.5 |
//=> 1/2 = 0.5 |
||
</syntaxhighlight> |
|||
</source> |
|||
==部分適用との混同== |
==部分適用との混同== |
2020年7月5日 (日) 22:48時点における版
カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。クリストファー・ストレイチーにより論理学者ハスケル・カリーにちなんで名付けられたが、実際に考案したのはMoses Schönfinkelとゴットロープ・フレーゲである。
ごく簡単な例として、f(a, b) = c という関数 f があるときに、F(a) = g(ここで、g は g(b) = c となる関数である)という関数 F が、f のカリー化である。
関数 f が の形のとき、 をカリー化したものを とすると、 の形を取る。uncurryingは、これの逆の変換である。
理論計算機科学の分野では、カリー化を利用すると、複数の引数をとる関数を、一つの引数のみを取る複数の関数のラムダ計算などの単純な理論的モデルと見なして研究できるようになる。圏論ではカリー化の概念を、デカルト閉圏における冪対象の普遍性に見出せる。適当な2つの対象の積から別の対象への射 に対して、射 が一意に対応する。
カリー化をする現実の動機の1つに、カリー化することで後述する部分適用が行いやすくなることが挙げられる。たとえば、加算を行う関数 (+)
をカリー化してから、最初の引数だけに 1
を適用すれば、インクリメント用の関数が簡単に作れる。
カリー化を基盤としているプログラミング言語もある。特にMLとHaskellでは関数は常に一つの引数のみを取り、複数の引数を取る関数とは、単にネストされた複数の一引数関数の糖衣構文にすぎない。第一級関数を扱える言語、たとえばLISP、Scheme、F#、Scala、Erlang、Eiffel、Perl、Ruby、Python、R言語、S言語、JavaScript、Swiftなどでは、カリー化関数を作ることができる。
実装例
たとえば、除算の関数 をカリー化したものを とすると、 は のみを引数に取る。そして とすると、 は のみを引数に取る新しい関数となり、、つまり引数の逆数を返す関数になる。
この例をSchemeとJavaScriptで実装した例を示す。
(define div (lambda (x y) (/ x y)))
;(div 1 3) == (/ 1 3), これはカリー化ではない。
(define cdiv (lambda (x) (lambda (y) (div x y) )))
;(cdiv a) == (div a y)
((cdiv 10) 3)
;=> 10/3
(define inv (lambda (x) ((cdiv 1) x)))
;inv == (cdiv 1)
(inv 2)
;=> 1/2
function div(x, y) { return x / y; }
//div(1, 3) == 1 / 3, これはカリー化ではない。
function cdiv(x) { return function(y) { return div(x, y); } }
console.log(cdiv(10)(3));
//=> 10/3 = 3.333...
var inv = cdiv(1);
console.log(inv(2));
//=> 1/2 = 0.5
部分適用との混同
カリー化は部分適用と混同されやすい[1][2]。部分適用とは、複数の引数をとる関数の一部の引数に実引数を適用する操作のことで、例えば上述の から を導く操作を指す。一方、カリー化は例えば から を導く操作であり、引数への実引数の適用までは行わない。
標準C++ライブラリの std::bind1st
は部分適用の一例である。
この混同はしばしば言語設計者によってもなされる。Groovy では、部分適用を行う標準メソッドに curry
という名前がつけられている。また、Haskell の標準関数 curry
も、カリー化と厳密には違う動作をする。この関数は (a, b) -> c
という型を持つ関数を a -> b -> c
型の関数に変換するが、元の関数は「2 要素のタプルを取る 1 引数関数」であり、2 引数関数ではない。Haskell は全ての関数が 1 引数関数であり(つまり、全ての関数が元々カリー化された形であるとも表現される)、Haskell プログラムでは、厳密な意味でカリー化を行う関数は定義できない。ただし型理論上は、X
, Y
という型の要素を持つタプルの型は直積 とほぼ同一視できるため、必ずしも誤用というわけではない。