33
すごいHaskell読書会 in 大阪 2週目 #5 5章:高階関数 (2) 2014/05/14 なかやま よういち 1

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

Embed Size (px)

Citation preview

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

すごいHaskell読書会 in 大阪 2週目 #5

第5章:高階関数 (2)2014/05/14

なかやま よういち

1

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

Contents

5.5 畳み込み、見込みアリ! 5.6 $ を使った関数適用 5.7 関数合成

2

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

力の階層

↑ 自由度:高、抽象度:低 ↑

• 再帰

• foldr、foldl など  

• filter、take など

↓ 自由度:低、抽象度:高 ↓

3

}5.5 畳み込み 5.6, 5.7 簡素化($)、(.)

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

5.5 畳み込み、見込みアリ!

fold 【動詞】

〈紙・布などを〉折る,折り重ねる,折りたたむ

〈端などを〉折り曲げる,折り返す

(2引数関数, アキュムレータ初期値, リスト) → 答

アキュムレータ(accumulator):演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のこと http://ja.wikipedia.org/wiki/アキュムレータ_(コンピュータ)

4

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

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

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

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

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

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

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

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

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

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

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

andand :: [Bool] -> Bool!

再帰による定義:!

and [] = True!

and (x:xs) = x && and xs!

foldrを使って定義(一行で書ける!):!

and = fold (&&) True

10

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

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

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

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

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

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

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

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

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

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

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

(:) 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

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

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

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

foldrと無限リストfoldr f z [x1, x2, ..., xn]!= x1 `f` (x2 `f` ... (xn `f` z)…)!!無限リストから答を取り出せる場合がある!

• `f` が右辺によらない!• False && xxx!• elem’ で何か見つかったとき!

• 有限個の評価でいい!• take N [y1, y2, …]

18

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

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

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

foldl1,foldr1リストからアキュムレータ初期値を取る!

foldl1 :: (a -> a -> a) -> [a] -> a!

foldl1 f (x:xs) = foldl f x xs!

foldl1 _ [] = error “empty list”!

foldr1 も同様!

20

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

reverse’ :: [a] -> [a]!

reverse’ = foldl (\acc x -> x : acc) []!

!!

reverse’ = foldl (flip (:)) []

21

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

例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

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

スキャン中間状態すべてをリストとして返す!

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

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

スキャン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

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

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

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

関数↔引数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

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

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

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

多引数関数の関数合成部分適用して一引数関数にできるなら関数合成で書き直せる

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

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

ポイントフリースタイル

sum’ :: (Num a) => [a] -> a!

sum’ xs = foldl (+) 0 xs!

! 関数はカリー化されており、引数を省略できる!

sum’ = foldl (+) 0

29

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

第5章 高階関数(まとめ)• 再帰

• foldr、foldl など … 畳み込み(5.5)

• filter、take など … map,filter(5.3)

カリー化関数(5.1)、高階実演(5.2)

λ(5.4)、 $(5.6)、 .(5.7)

30

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

Exercise 1or’ :: [Bool] -> Bool!

を foldr を使って定義せよ

• or’ [] == False!

• or’ [True] == True!

• or’ [False] == False!

• or’ [False, True] == True!

• or’ (repeat True) == True

31

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

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

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

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