for文を書かずに繰り返す
map、filter、fold は、リストの全部の要素に関数を使うための、3つの基本部品です。for 文を書かなくても、「全部に適用する」「条件で選ぶ」「1つにまとめる」を、それぞれ1行で書けます。
レシートの金額のリストから、500円以上だけを選び、税込みにして、合計を出す場面を考えます。この3つの部品を、順につなぐだけで書けます。
このレッスンのゴール
メモ
for 文の繰り返しを、map、filter、fold で置きかえることができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 高階関数 | 関数を引数として受け取る関数 | |
| map | リストの全部の要素に、関数を適用する | map (+1) [1, 2] |
| filter | 条件に合う要素だけを残す | filter even [1, 2] |
| 畳み込み | リストを1つの値にまとめる(fold) | foldl (+) 0 [1, 2] |
| 初期値 | 畳み込みを始めるときの値 |
学生が入力するセルは、このプレビューでは空白です。
map は全部の要素を変える
map f list は、list の要素の1つ1つに関数 f を適用し、その結果のリストを返します。元のリストは変わりません。要素の数も同じです。
(+1) は「1を足す関数」を表す書き方です。map (+1) [1, 2, 3] は、全部の要素に1を足します。
リストの全部に 1 を足す
docker exec haskell ghc -e 'map (+1) [1, 2, 3]'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
filter は条件に合うものを残す
filter test list は、test が True を返す要素だけを残します。test は、1つの要素を受け取って True か False を返す関数です。
残る要素の順番は、元のままです。条件に合うものが1つもなければ、空のリストを返します。
2 より大きい要素を残す
docker exec haskell ghc -e 'filter (> 2) [1, 2, 3, 4]'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
例: 3つの金額に map と filter
| 金額(円) | 500円以上か | 残ったら税込み(10%) |
|---|---|---|
| 300 | いいえ | 残らない |
| 800 | はい | 880 |
| 1200 | はい | 1320 |
学生が入力するセルは、このプレビューでは空白です。
fold はリストを1つの値にまとめる
畳み込み(fold)は、リストの要素を1つずつ関数でまとめて、1つの値にします。foldl f 初期値 リスト は、左の要素から順に、結果と次の要素を f に渡します。
合計も、積も、最大値も、畳み込みで書けます。foldl (+) 0 [1, 2, 3, 4] は、0 に1、2、3、4を順に足します。
4つの数の合計を畳み込む
docker exec haskell ghc -e 'foldl (+) 0 [1, 2, 3, 4]'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
引き算では左右で結果が変わる
foldr は、右の要素から順にまとめます。足し算なら、左から始めても右から始めても同じです。
しかし引き算は、順番で結果が変わります。下の表のとおり、foldl (-) 0 [10, 3, 2] と foldr (-) 0 [10, 3, 2] は別の値になります。
例: 引き算の左右の畳み込み
| 畳み込み | 計算の形 | 結果 |
|---|---|---|
| foldl (-) 0 [10, 3, 2] | ((0 - 10) - 3) - 2 | -15 |
| foldr (-) 0 [10, 3, 2] | 10 - (3 - (2 - 0)) | 9 |
学生が入力するセルは、このプレビューでは空白です。
左から引いていく
docker exec haskell ghc -e 'foldl (-) 0 [10, 3, 2]'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
右から引いていく
docker exec haskell ghc -e 'foldr (-) 0 [10, 3, 2]'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
「foldl と foldr は、どちらを使っても同じ結果だ」と考える人が多くいます。足し算や掛け算では同じですが、引き算や文字をつなぐ処理では、順番で結果が変わります。どちらから始めるかを、必ず意識します。
3つの部品をつなぐ
docker exec haskell ghc -e 'sum (map (*2) (filter odd [1 .. 10]))'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Fold.hs を動かす
docker exec haskell runghc Fold.hsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Python で畳み込みの形を見る
# for 文で書いた畳み込みです。total が「ここまでの結果」です。
def put_front(total, item):
return item + total
total = ""
for item in ["a", "b", "c"]:
total = put_front(total, item)
print(total)
# 中の item + total を total + item に変えて、結果がどう変わるか確かめましょう。