Haskell学习笔记:High order function
Lambda(匿名函数)
lambda函数存在的意义是为了减少函数命名的麻烦,因为有些函数只需要用一次,没必要为他们费劲心力命名。
- 下列两个函数定义是相同的:
inc x = x + 1
inc2 = \x -> x + 1
-
那么区别在哪呢?
\x -> x + 1 表示一个未命名函数(annonymous functions) -
下列两个函数定义也大致相同
f x y = x + y
g = \x -> \y -> x + y
- 语法?
首先是一个\,它长得很像缺腿的lamda符号。接下来是参数,紧连参数的是->,之后就是对参数的操作表达式。最后,它们作为一个整体用()括起。
例如:(\ x -> x+1 )应用时把括号内整体看作一个表达式
Currying and partial application function(柯里化与函数局部应用)
- 下述两个函数有什么区别呢?
g = \x -> \y -> x + y
h (x,y) = x + y
answer:函数h是未柯里化的函数g
其中:函数g为单变量x函数,先作为函数\y -> x + y 的变量。其次y再作为函数x+y变量;函数h则为多变量函数,认为所有的变量都由一个数组集合给出。
- 将单变量函数与多变量函数互相转换,这个过程就叫做柯里化currying,反之叫做去柯里化uncurrying。说到底,柯里化就是函数partial application
curry :: ((a, b) -> c) -> a -> b -> c
curry f = \x -> \y -> f (x,y)
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f = \(x,y) -> f x y
注:类型说明是可忽略的,因为Haskell自带类型推断环节(type inference)。
函数顺序从左至右,类型说明从右至左。
- 举个例子:写一个函数,它输入一个参数,再将此参数与100比大小。
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred x = compare 100 x
注意到上述函数x出现在=左右两端。让我们思考一下“compare 100 ”代表什么?
它表示一个函数,将输入的参数与100比大小…这以例子开头想要的东西一模一样。
所以上述函数也可以省略等式两端的x:
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred = compare 100
partially applied infix function(中缀函数局部应用)
嘿嘿嘿,中缀函数也可以局部调用呢!如下:
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
divideByTen 200 = 200 / 10 = (/10) 200
- 注意:(-4)是个特例,它还是代表负四,不代表一个减4的函数。
Map and filters
map
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
filter
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs
fold
-
A fold takes a binary function, the accumulator and a list to fold up.
函数初始值分别为acc和list第一个元素。作用后输出值再作为acc,与list的第二个元素再次作为函数的输入…循环直到list遍历。输出最后acc。 -
foldl遍历list从左至右;foldr遍历从右至左。
-
foldl
例子:用foldl实现sum
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs
- foldr
例子:用foldr实现map
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> f x : acc) [] xs
- 注意
看这个用foldr和foldl实现从右到左遍历list的map’函数
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> fx : acc) [] xs
map'' f xs = foldl (\acc x -> acc ++ [f x] ) [] xs
- 两者都可以实现同一函数,只是顺序不用而已
- 注意括号里的lamda函数,acc的位置不同,它总是放在遍历顺序的第一位。
- map’比map’'更好,因为 ++ 比 : 代价更高
foldr1 & foldl1
与foldr、foldl相似,只是不需要提供初始值,默认初始值为列表中的第一个(最后一个)元素
scanl and scanr
与 foldr,foldl类似,只是输出为包含所有acc的list
ghci> scanl (+) 0 [3,5,2,1]
[0,3,8,10,11]
ghci> scanr (+) 0 [3,5,2,1]
[11,8,3,1,0]
注意:遍历顺序同样是从左至右或者从右至左。不同于fold,scanl会把结果从左到右排序;scanr为从右向左。
$ function
- left-associative: function application with space
f a b c = (f a) b) c) - right-associative : $ function
sum (map sqrt [1…]) = sum $ map sqrt [1…]
sqrt (3 + 4 + 9) = sqrt $ 3 + 4 + 9
f (g (z x)) = f $ g $ z x
Function composition
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
- right-associative:f (g (z x)) = (f . g . z) x
- 怎么用?如果你有一堆函数套用函数的表达式,把最后一个参数用$连接,其他函数用 . 连接(如果是多参数函数,给出最后一个参数以外的其他参数)。如下:
replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))=
replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]
这样就不同打一堆括号啦!?