計算機工学には、
複数の引数を持つ関数定義を、
一つの引数を持つ関数の定義の連鎖に帰結させるカリー化が研究テーマとしてあります。
カリー化
端的に云うとカリー化とは、
”引数を一つ取り、引数を一つ取る関数を返す関数”を定義することになります。
Haskellライクにカリー化を記すと、
f (x, y) -> z のカリー化は、
f x -> f ' f ' y -> z となります。
Haskellにも当然、curry化を可能にする機能を持っていて、
一見、
二つの引数を取るように見える下記の関数定義は、
curry化の糖衣構文(シンタックスシュガー※)です。 ※要は、難しいのを簡単にする...ということです。
let f x y = x + y
Haskellにおいては、上の関数のカリー化が下記のように行われ、
f x -> f'
f' y -> x + y
実際に与えられた引数の評価の際には、
関数 f に2 3を与えた場合、
引数2に対して部分適用され、関数(f 2)を返し、
関数(f 2)に引数3を適用し、結果、値5を返すという流れになり、
カリー化のメリットは、引数の部分適用が、カリー化の”結果”、可能になることにあります。
これをくどくまた記すと、
ex.
> let f x y = x + y
> let tmp = f 2 (<- 引数の部分適用を行っている)
> tmp 3
> 5
これ、カリー化されてなければ、
> f (2, 3)
> 5
という計算過程になります。
curry関数、uncurry関数の用例
Haskellにはライブラリ関数として、curry、uncurryがあり、こんな感じで利用出来ます。
curry関数の用例
>let f (x , y) = x + y
>:t f
f :: Num a => (a, a) -> a
>let cur = curry f
>:t cur
cur :: Integer -> Integer -> Integer ( <- カリー化されている )
>cur 2 3
5
uncurry関数の用例
>let f x y = x + y
>:t f
f :: Num a => a -> a -> a
>let uc = uncurry f
uc :: (Integer, Integer) -> Integer ( <- 非カリー化されている)
>uc (2, 3)
5
ここでは、あえて自分で、curry、uncurry関数を定義してみます。
curry関数の型定義
curry :: ((a,b) -> c) -> a -> b -> c
curry関数のアイディア
Hakellは、関数定義の際、引数の間にスペースを入れて定義すると、自然とカリー化されます。
なので、これを利用します。当然引数に、関数に相当する変数を設定します。
my_curry f x y
といったところです。というか、これってこの関数自体がカリー化の一例ですよね。
curry関数プログラム
curry関数の定義
my_curry :: ((a,b) -> c) -> a -> b -> c
my_curry f x y = f (x, y)
--ラムダ式を使った定義
my_curry2 :: ((a,b) -> c) -> a -> b -> c
my_curry2 f = \x y -> f (x, y)
ラムダ式を利用した定義も作成しました。
ついで、uncurryを実装してみます。
uncurry関数の型定義
un_curry :: (a -> b -> c) -> (a, b) -> c
uncurry関数のアイディア
要素2のタプルを引数にとればいいです。当然関数に相当する変数も定義します。
my_un_curry f (x, y)
こんな感じ。
uncurry関数プログラム
uncurry関数の定義
my_un_curry :: (a -> b -> c) -> (a, b) -> c
my_un_curry f (x, y) = f x y
--ラムダ式を用いた定義
my_un_curry2 :: (a -> b -> c) -> (a, b) -> c
my_un_curry2 f = \(x, y) -> f x y
ラムダ式を利用した定義も作成しました。