Haskell 入門


Haskell の高階関数

高階関数とは「高度で難解な関数」ではなく、単に引数に関数をとれる関数のことだ。引数に関数をとるというと難しそうに見えるが、次の map という関数の動作を見てみると、結構わかりやすい事が分かる。

map 関数がすることは、[1,2,3,4] のようなリストの各要素のそれぞれに同じ演算を加える操作だ。map は第1引数として、各要素に加えたい演算の関数をとり、第2引数にリストをとる。こういうふうに文章で書くとややこしいが、たとえば、リストの要素をすべて2倍にしたいようなときは次のようにする。

Prelude> map (* 2) [1,2,3,4,5]
[2,4,6,8,10]

上の式で、(*2) というのが、要素を2倍にするという関数だ。それでは2乗にしたいときはどうだろうか、

Prelude> map (^ 2) [1,2,3,4,5]
[1,4,9,16,25]

のようなやり方もできるが、自前の関数を作ってmapに渡してみよう。渡すのは f(x) = x * x という関数だ。この関数は x という変数を持ち値 hx * x を返す。この fx をHaskell風にラムダ記法で表現すると、

(\x -> x * x)

となる。これを map に渡してやればいいのだ。

Prelude> map (\x -> x * x) [1,2,3,4,5]
[1,4,9,16,25]

map 以外の高階関数で分かりやすいのに、filter がある。filter の第1引数に関数を渡すと、その関数がTrueを返す要素を選別してリストにしてくれる。たとえば、奇数を取り出したいときは、

Prelude>> filter odd [1,2,3,4,5]
[1,3,5]

偶数を取り出したいときは、

Prelude> filter even [1,2,3,4,5]
[2,4]

3より大きい要素を取り出したいときは、

Prelude> filter (>p; 3) [1,2,3,4,5]
[4,5]

このように高階関数を使えば、ループも分岐も記述せずに、リストを加工できる。やらせたいことを関数の形で map や filter などの高階関数の引数として渡してやればいいだけだからだ。むしろ、こちらのほうが、頭のなかの直観的なアイディアをそのまま表現できる。高階関数は「高度で難解」どころか、「高度に理解しやすい」関数なのだ。

追記

map のような高階関数は決してブラックボックスではなく、高階関数はユーザーが普通に定義できる。自前の map 関数 mymap を次のように定義したが、きちんと動作する。

Prelude> :set +m
Prelude> let
Prelude| mymap f [] = []
Prelude| mymap f (x:xs) = f x : mymap f xs
Prelude|
Prelude> mymap (^2) [1..5]
[1,4,9,16,25]

Haskell では関数も first order values なので上のプログラムのようなものも普通に書ける。また、コンパイルは単に(数学的な)式の展開をしているだけなので、プログラムのコンパイルが成功すれば、その関数はきちんと動作することが証明される。