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
  1. 两者都可以实现同一函数,只是顺序不用而已
  2. 注意括号里的lamda函数,acc的位置不同,它总是放在遍历顺序的第一位。
  3. 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]

这样就不同打一堆括号啦!?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值