すごい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
sum
suml = 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
and
and :: [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
map
map :: (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
elem
elem’ :: (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 foldl
ghci> :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 1
or’ :: [Bool] -> Bool!
を foldr を使って定義せよ
• or’ [] == False!
• or’ [True] == True!
• or’ [False] == False!
• or’ [False, True] == True!
• or’ (repeat True) == True
31
Exercise 2
concat’ :: [[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

すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)

  • 1.
    すごいHaskell読書会 in 大阪2週目 #5 第5章:高階関数 (2) 2014/05/14 なかやま よういち 1
  • 2.
    Contents 5.5 畳み込み、見込みアリ! 5.6 $を使った関数適用 5.7 関数合成 2
  • 3.
    力の階層 ↑ 自由度:高、抽象度:低 ↑ • 再帰 • foldr、foldlなど   • filter、take など ↓ 自由度:低、抽象度:高 ↓ 3 }5.5 畳み込み 5.6, 5.7 簡素化 ($)、(.)
  • 4.
    5.5 畳み込み、見込みアリ! fold 【動詞】 〈紙・布などを〉折る,折り重ねる,折りたたむ 〈端などを〉折り曲げる,折り返す (2引数関数,アキュムレータ初期値, リスト) → 答 アキュムレータ(accumulator):演算装置による演算結果を累積 する、すなわち総和を得るといったような計算に使うレジスタや 変数のこと http://ja.wikipedia.org/wiki/アキュムレータ_(コン ピュータ) 4
  • 5.
    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
  • 6.
    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
  • 7.
    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
  • 8.
    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
  • 9.
    sum suml = 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
  • 10.
    and and :: [Bool]-> Bool! 再帰による定義:! and [] = True! and (x:xs) = x && and xs! foldrを使って定義(一行で書ける!):! and = fold (&&) True 10
  • 11.
    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
  • 12.
    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
  • 13.
    map map :: (a-> b) -> [a] -> [b]! ! 再帰による定義:! map _ [] = []! map f (x:xs) = f x : map f xs! ! foldrを使って定義:! map f = foldr (x acc -> f x : acc) []! 13
  • 14.
    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
  • 15.
    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
  • 16.
    (:) 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
  • 17.
    elem elem’ :: (Eqa) => 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
  • 18.
    foldrと無限リスト foldr f z[x1, x2, ..., xn]! = x1 `f` (x2 `f` ... (xn `f` z)…)! ! 無限リストから答を取り出せる場合がある! • `f` が右辺によらない! • False && xxx! • elem’ で何か見つかったとき! • 有限個の評価でいい! • take N [y1, y2, …] 18
  • 19.
    foldr vs foldl ghci>: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
  • 20.
    foldl1,foldr1 リストからアキュムレータ初期値を取る! foldl1 :: (a-> a -> a) -> [a] -> a! foldl1 f (x:xs) = foldl f x xs! foldl1 _ [] = error “empty list”! foldr1 も同様! 20
  • 21.
    例 reverse’ :: [a]-> [a]! reverse’ = foldl (acc x -> x : acc) []! !! reverse’ = foldl (flip (:)) [] 21
  • 22.
    例 product’ :: (Numa) => [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
  • 23.
    スキャン 中間状態すべてをリストとして返す! 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
  • 24.
    スキャン ghci> scanl1 (accx ->! 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
  • 25.
    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
  • 26.
    関数 引数 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
  • 27.
    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
  • 28.
    多引数関数の関数合成 部分適用して一引数関数にできるなら関数合成で書き直せる 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
  • 29.
    ポイントフリースタイル sum’ :: (Numa) => [a] -> a! sum’ xs = foldl (+) 0 xs! ! 関数はカリー化されており、引数を省略できる! sum’ = foldl (+) 0 29
  • 30.
    第5章 高階関数(まとめ) • 再帰 •foldr、foldl など … 畳み込み(5.5) • filter、take など … map,filter(5.3) カリー化関数(5.1)、高階実演(5.2) λ(5.4)、 $(5.6)、 .(5.7) 30
  • 31.
    Exercise 1 or’ ::[Bool] -> Bool! を foldr を使って定義せよ • or’ [] == False! • or’ [True] == True! • or’ [False] == False! • or’ [False, True] == True! • or’ (repeat True) == True 31
  • 32.
    Exercise 2 concat’ ::[[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
  • 33.
    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