すごいHaskell読書会 in 大阪 2週目 #5
第5章:高階関数 (2)2014/05/14
なかやま よういち
1
Contents
5.5 畳み込み、見込みアリ! 5.6 $ を使った関数適用 5.7 関数合成
2
力の階層
↑ 自由度:高、抽象度:低 ↑
• 再帰
• foldr、foldl など
• filter、take など
↓ 自由度:低、抽象度:高 ↓
3
}5.5 畳み込み 5.6, 5.7 簡素化($)、(.)
5.5 畳み込み、見込みアリ!
fold 【動詞】
〈紙・布などを〉折る,折り重ねる,折りたたむ
〈端などを〉折り曲げる,折り返す
(2引数関数, アキュムレータ初期値, リスト) → 答
アキュムレータ(accumulator):演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のこと http://ja.wikipedia.org/wiki/アキュムレータ_(コンピュータ)
4
foldl(左畳み込み)
リストの左から畳み込んでいく!foldl f z [x1, x2, ..., xn]!= (...((z `f` x1) `f` x2) `f`...) `f` xn!! foldl (+) 0 [1, 2, 3, 4, 5]! = ((((0 + 1) + 2) + 3) + 4) + 5
5
foldl(定義)foldl f z [x1, x2, ..., xn]!= (...((z `f` x1) `f` x2) `f`...) `f` xn!!再帰による定義:!foldl :: (b -> a -> b) -> b -> [a] -> b!foldl _ z [] = z!foldl f z (x:xs) = foldl f (f z x) xs!! 再帰定義の右辺でfoldlが一番外側に→末尾再帰
6
foldr(右畳み込み)
リストの右から畳み込んでいく!foldr f z [x1, x2, ..., xn]!= x1 `f` (x2 `f` ... (xn `f` z)…)!! foldr (+) 0 [1, 2, 3, 4, 5]! = 1 + (2 + (3 + (4 + (5 + 0))))!! 評価順序については何も言っていないことに注意
7
foldr(定義)
foldr f z [x1, x2, ..., xn]!= x1 `f` (x2 `f` ... (xn `f` z)…)!!再帰による定義:!foldr :: (a -> b -> b) -> b -> [a] -> b!foldr _ z [] = z!foldr f z (x:xs) = f x (foldr f z xs)
8
sumsuml = foldl (+) 0!!
sumr = foldr (+) 0!!
suml [1, 2, 3, 4, 5]! = ((((0 + 1) + 2) + 3) + 4) + 5!! sumr [1, 2, 3, 4, 5]! = 1 + (2 + (3 + (4 + (5 + 0))))
9
andand :: [Bool] -> Bool!
再帰による定義:!
and [] = True!
and (x:xs) = x && and xs!
foldrを使って定義(一行で書ける!):!
and = fold (&&) True
10
and(with foldr)andr :: [Bool] -> Bool!
andr = foldr (&&) True!
!
andr [] == True!
andr [True] == True!
andr [False] == False!
andr [True, False] == False!
andr (repeat False) == False … 無限リストでもちゃんとFalseで止まる
11
andモドキ(with foldl)andl :: [Bool] -> Bool!
andl = foldl (&&) True!
!
andl [] == True!
andl [True] == True!
andl [False] == False!
andl [True, False] == False!
andl (repeat False) …?
12
mapmap :: (a -> b) -> [a] -> [b]!!再帰による定義:! map _ [] = []! map f (x:xs) = f x : map f xs!!foldrを使って定義:! map f = foldr (\x acc -> f x : acc) []!
13
map (with foldr)mapr :: (a -> b) -> [a] -> [b]!
mapr f = foldr (\x acc -> f x : acc) []!
!
mapr (*2) [] == []!
mapr (*2) [1..3] == [2,4,6]!
take 3 (mapr (*2) [1..]) == [2,4,6]!
不要な部分は評価されない→無限リストに使える
14
mapモドキ(with foldl)mapl :: (a -> b) -> [a] -> [b]!
mapl f = foldl (\acc x -> acc ++ [f x]) []!
!
mapl (*2) [] == []!
mapl (*2) [1..3] == [2,4,6]!
take 3 (mapl (*2) [1..]) …?
15
(:) vs (++)ghci> :set +s!
ghci> foldr (\x acc -> x : acc) [] [1..10000]!
(0.19 secs, 44499728 bytes)!
ghci> foldr (\x acc -> acc ++ [x]) [] [1..10000]!
(4.83 secs, 4335728232 bytes)!
16
elemelem’ :: (Eq a) => a -> [a] -> Bool!
elem’ y ys!
= foldr (\x acc -> if x == y then True else acc) False ys!
elem’ 3 [1..]!
= 1 `f` (2 `f` (3 `f` (…)))!
= 2 `f` (3 `f` (…))!
= 3 `f` (…) = True
17
foldrと無限リストfoldr f z [x1, x2, ..., xn]!= x1 `f` (x2 `f` ... (xn `f` z)…)!!無限リストから答を取り出せる場合がある!
• `f` が右辺によらない!• False && xxx!• elem’ で何か見つかったとき!
• 有限個の評価でいい!• take N [y1, y2, …]
18
foldr vs foldlghci> :set +s!
ghci> sumr [1..10000000]!
(2.02 secs, 1621588592 bytes)!
ghci> suml [1..10000000]!
(3.30 secs, 1694662448 bytes)!
ghci> suml' [1..10000000] … foldl’ (正格なfoldl, see 6.2)!
(0.27 secs, 969254424 bytes)!
http://sikhote.wordpress.com/2010/12/01/foldr-vs-foldl-a-small-survey-with-the-help-of-ghc/
http://ja.wikipedia.org/wiki/評価戦略
19
foldl1,foldr1リストからアキュムレータ初期値を取る!
foldl1 :: (a -> a -> a) -> [a] -> a!
foldl1 f (x:xs) = foldl f x xs!
foldl1 _ [] = error “empty list”!
foldr1 も同様!
20
例
reverse’ :: [a] -> [a]!
reverse’ = foldl (\acc x -> x : acc) []!
!!
reverse’ = foldl (flip (:)) []
21
例product’ :: (Num a) => [a] -> a!
product’ = fold (*) 1!
!
filter’ :: (a -> Bool) -> [a] -> [a]!
filter’ p = foldr (\x acc -> if p x then x : acc else acc) []!
!
last’ :: [a] -> a!
last’ = foldl1 (\_ x -> x)
22
スキャン中間状態すべてをリストとして返す!
ghci> scanl (+) 0 [3,5,2,1]!
[0,3,8,10,11]!
ghci> scanr (+) 0 [3,5,2,1]!
[11,8,3,1,0]
23
スキャンghci> scanl1 (\acc x ->!
if x > acc then x else acc) [3,4,5,3,7,9,2,1]!
[3,4,5,5,7,9,9,9] !
ghci> scanl (flip (:)) [] [3,2,1]!
[[],[3],[2,3],[1,2,3]] !
ghci> take 10 (scanl1 (+) [1,3..])!
[1,4,9,16,25,36,49,64,81,100]
24
5.6 $ を使った関数適用
($) :: (a -> b) -> a -> b!f $ x = f x!!ghci> sum (map sqrt [1..130])!993.6486803921487!ghci> sum $ map sqrt [1..130]!993.6486803921487! … 括弧が減ってる。!!ghci> sum (filter (> 10) (map (*2) [2..10]))!80!ghci> sum $ filter (> 10) $ map (*2) [2..10]!80!
25
関数↔引数f: X -> Y <=> x: (X -> Y) -> Y!
(X -> Y) x X -> Y !
ghci> (\x -> 2 * x + 1) 3!
7!
ghci> (\x -> 2 * x + 1) $ 3!
ghci> ($ 3) (\x -> 2 * x + 1) … セクション!
ghci> :t ($ 3)!
($ 3) :: Num a => (a -> b) -> b
26
5.7 関数合成
(.) :: (b -> c) -> (a -> b) -> a -> c!
f . g = \x -> f (g x)!
ghci> map ((+1) . (*2)) [1..10]!
[3,5,7,9,11,13,15,17,19,21]!
数式で書くと: g(x) = 2x, f(y) = y+1!
=> (f・g)(x) = 2x + 1
27
多引数関数の関数合成部分適用して一引数関数にできるなら関数合成で書き直せる
sum (replicate 5 (max 6.7 8.9))!
= (sum . replicate 5) (max 6.7 8.9)!
= sum . replicate 5 $ max 6.7 8.9!
!
replicate 2 (product (map (*3) (zipWith max [1,2] [4,5])))!
= replicate 2 . product . map (*3) $ zipWith [1,2] [4,5]
28
ポイントフリースタイル
sum’ :: (Num a) => [a] -> a!
sum’ xs = foldl (+) 0 xs!
! 関数はカリー化されており、引数を省略できる!
sum’ = foldl (+) 0
29
第5章 高階関数(まとめ)• 再帰
• foldr、foldl など … 畳み込み(5.5)
• filter、take など … map,filter(5.3)
カリー化関数(5.1)、高階実演(5.2)
λ(5.4)、 $(5.6)、 .(5.7)
30
Exercise 1or’ :: [Bool] -> Bool!
を foldr を使って定義せよ
• or’ [] == False!
• or’ [True] == True!
• or’ [False] == False!
• or’ [False, True] == True!
• or’ (repeat True) == True
31
Exercise 2concat’ :: [[a]] -> [a]
を畳み込みを使って定義せよ
• concat’ [] == []
• concat’ [[1,2,3],[4,5,6]] == [1,2,3,4,5,6]
• concat’ [[[1,2,3],[4,5,6]]] == [[1,2,4],[4,5,6]]
• take 3 $ concat’ $ map (:[]) [1..] == [1,2,3]32
Exercise 3隣接した重複を取り除く関数remdupsを定義せよ
remdups :: Eq a => [a] -> [a]!
• remdups [1,1,2,2,3,3] == [1,2,3]!
• remdups [1,1,2,2,1,1] == [1,2,1]!
• take 3 $ remdups [1..] == [1,2,3]
33