软糖

Haskell 5 HOF

Taking Notes from http://learnyouahaskell.com

Curried functions

1
2
3
4
5
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred = compare 100
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)

High order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith _ [] _ = []
zipWith _ _ [] = []
zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]
[6,8,7,9]
ghci> zipWith' max [6,3,2,1] [7,3,1,5]
[7,3,2,5]
ghci> zipWith' (++) ["foo ""bar ""baz "] ["fighters""hoppers""aldrin"]
["foo fighters","bar hoppers","baz aldrin"]
ghci> zipWith' (*) (replicate 5 2) [1..]
[2,4,6,8,10]
ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]
[[3,4,6],[9,20,30],[10,12,12]]

map & filter

map

1
2
3
4
5
6
7
8
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
ghci> map (+3) [1,5,3,1,6]
[4,8,6,4,9]
[x+3 | x <- [1,5,3,1,6] ]

filter

1
2
3
4
5
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter f (x:xs)
| f x = x : filter f xs
| otherwise = filter f xs

lambda

1
2
numLongChains :: Int
numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))

map (+3) [1,6,3,2] VS map (\x -> x+3) [1,6,3,2]

fold

foldl foldr (foldl1 foldr1)

foldl1 与 foldr1 的行为与 foldl 和 foldr 相似,只是你无需明确提供初始值。他们假定 List 的首个(或末尾)元素作为起始值,并从旁边的元素开始折叠

1
2
3
4
5
6
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs
sum' :: (Num a) => [a] -> a
sum' = foldl (+) 0

有点消除同类项的感觉. I like it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
maximum' :: (Ord a) => [a] -> a
maximum' = foldr1 (\x acc -> if x > acc then x else acc)
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
product' :: (Num a) => [a] -> a
product' = foldr1 (*)
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x acc -> if p x then x : acc else acc) []
head' :: [a] -> a
head' = foldr1 (\x _ -> x)
last' :: [a] -> a
last' = foldl1 (\_ x -> x)

scanl scanr (scanl1 scanr1)

scanl 和 scanr 与 foldl 和 foldr 相似,只是它们会记录下累加值的所有状态到一个 List

1
2
ghci> scanl (+) 0 [3,5,2,1]
[0,3,8,10,11]

‘$’ function

定义

1
2
($) :: (a -> b) -> a -> b
f $ x = f x

$看作是在右面写一对括号的等价形式

1
2
3
4
5
6
7
8
9
10
11
sum (map sqrt [1..130])
sum $ map sqrt [1..130]
sqrt (3+4+9)
sqrt $ 3+4+9
f (g (z x))
f $ g $ z x
sum (filter (> 10) (map (*2) [2..10])
sum $ filter (> 10) $ map (*2) [2..10]

Function composition

1
2
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
1
2
3
4
5
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]

point free style

1
2
3
4
5
6
7
8
9
sum :: (Num a) => [a] -> a
sum xs = foldl (+) 0 xs
// change to
sum = foldl (+) 0
fn x = ceiling (negate (tan (cos (max 50 x))))
fn = ceiling . negate . tan . cos . max 50

函数若过于复杂,再使用 point free style 往往会适得其反,因此构造较长的函数组合链是不被鼓励的。更好的解决方法,就是使用 let 语句给中间的运算结果绑定一个名字,或者说把问题分解成几个小问题再组合到一起。这样一来我们代码的读者就可以轻松些,不必要纠结那巨长的函数组合链了

1
2
3
4
5
6
7
8
9
oddSquareSum :: Integer
oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
oddSquareSum = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]
oddSquareSum =
let oddSquares = filter odd $ map (^2) [1..]
belowLimit = takeWhile (<10000) oddSquares
in sum belowLimit