前回のおさらい
第5章 1-3
複数の引数を受け取れるかのように見えた関数は、 実はすべてカリー化された関数だったのです。 カリー化関数は、 複数の引数を取る代わりに、 常にちょうど1つの引数を取る関数です。
- セクション
- 高階関数
- 標準ライブラリの再実装
例
こんなのが出てきてもあんまり驚かない。驚けない。
5.4 ラムダ式
ラムダ式とは、 1回だけ必要な関数を作るときに使う無名関数です。通常、 ラムダ式は高階関数に渡す関数を作るためだけに使われます。 ラムダ式を宣言するには、 バックスラッシュ( \ )を書いて17、 それから関数の引数をスペース区切りで書きます。
こんな書き方。
でも普通はこんな書き方しても非効率である。
ラムダ式を括弧なしで書いた場合、 -> の右側のすべてがそのラムダ式に属します。 なので、 このケースでは括弧を省略してタイプ数を節約しています。 もちろん、 括弧を付けるのがお好みなら付けてもかまいません
畳み込み、見込みアリ
畳み込みを使えば、 リストを1要素ずつ一回だけ走査してそれに基づいた結果を返すような関数なら何でも実装できます
畳み込み関数は、 2引数関数(2つの引数を取る関数。 + や div など)と、 畳み込みに用 いる値(アキュムレータと呼ばれることが多い)の初期値、 それに畳み込むリストを受け取ります。
ここではサンプルコードがない。
foldlで左畳み込み
一般に foo a = bar b a のような関数があった場合、 カリー化のおかげで foo = bar b の ように書き換えることができます
カリー化によって、実装を簡略化できる。
畳み込みに用いる値はアキュムレータと呼ばれることが多い。
リストの中の値を一つの値にまとめることは畳み込みと呼ばれることがおおい。
Prelude> :t foldl foldl :: (a -> b -> a) -> a -> [b] -> a
foldrで右畳み込み
新しいリストを作るときはfoldrのほうがやりやすい。なお、foldrは無限リストに対応しているが、foldlは無限リストに対応できない。
(って書いてあるけど、↑の例だとどっちも終わらない)
って一例を出そうとしたけど、結構難しいぞ。
foldl1とfoldr1関数
畳み込みを作る際には空リストに対する動作について考えましょう。 空リストに対しては意味を成さない関数なら、 foldl1 や foldr1 を使える可能性があります。
いくつかの畳み込みの例
別の視点から見た畳み込み
畳み込みを関数適用と考える。
なんか難しいことが書いてあるぞ。
[3,4,5,6]に対する畳み込みを関数適用と考えてみる。アキュムレータは0。
f 3 (f 4 (f 5 (f 6 z)))
3 + (4 + (5 + (6 + 0)))
(+) 3 ((+) 4 ((+) 5 ((+) 6 0)))
スタックぽい実装だけど、64ビットPCだとスタックオーバーフローにはならなさそう。
無限リストを畳み込む
畳み込みを一連の関数適用として考えると、 foldr が無限リストに対して完璧に正しく動作する理由が見えてきます
Haskellは遅延評価なので、 本当に必要な部分だけを計算します
スキャン
$を使った関数適用
リスト中のすべての関数が 3 に適用され、 上のような結果が得られます
5.7 関数合成
「 . 」関数を使って関数合成
汚いコードもスッキリ。
多引数関数の関数合成
ポイントフリースタイル
ポイントとは、 fn x = f (g x) のような関数定義に登場する一時変数 x のことで、このポイントを使わないで関数を定義するから、 ポイントフリースタイルと呼ばれる。
$ .
$も.も右から実行していくけど、優先順位が違う。
Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c Prelude> :t ($) ($) :: (a -> b) -> a -> b