《Haskell趣学指南》笔记之高阶函数

栏目: 编程语言 · 发布时间: 6年前

内容简介:系列文章一个函数满足以下任一条件即为高阶函数《计算的本质》讲得更好一些,我感觉这本书这里讲得不通透。

系列文章

一个函数满足以下任一条件即为高阶函数

  1. 接受函数作为参数
  2. 将函数作为返回值

《计算的本质》讲得更好一些,我感觉这本书这里讲得不通透。

柯里化

让函数只接受一个参数就够了。 如果想接受两个参数,就先接受一个,返回一个接受另一个参数的函数即可。

multThree :: Int -> Int -> Int -> Int -- 等价于 Int -> (Int -> (Int -> Int))
multThree x y z = x * y * z 
复制代码

截断(section)

divideByTen :: (Floating a) => a -> a 
divideByTen = (/10) -- 这就是截断
isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..' Z']) -- 这就是截断
复制代码

一个需要注意的问题是 (-4) 不能表示截断,必须换成 (`subtract` 4)

函数作参数

applyTwice :: (a -> a) -> a -> a -- 注意这里的括号不能省略
applyTwice f x = f (f x)
复制代码

注意这里的括号不能省略,因为 -> 默认是右结合。

技巧:在编写函数(尤其是高阶函数)时如果拿不准函数的类型,可以先不写函数的类型声明,定义完毕后再利用 :t 查看 Haskell 推导出的结果。

工具箱

ghci> map (++ "!") ["BIFF"," BANG"," POW"]
["BIFF!"," BANG!"," POW!"]
ghci> filter even [1.. 10] 
[2, 4, 6, 8, 10]
ghci> let listOfFuns = map (*) [0..] 
ghci> (listOfFuns !! 4) 5 
20
复制代码

lambda

lambda 就是一次性的匿名函数。写法是 \param1 param2 -> returnValue ,必要的时候可以用圆括号括起来。

如果参数是二元组,应该怎么声明 lambda:

ghci> map (\(a, b) -> a + b) [(1, 2),( 3, 5)] -- 注意这里的 (a,b) 是模式匹配,不是两个参数
[3, 8]
复制代码

以下例子说明 Haskell 的函数是默认柯里化的

addThree :: Int -> Int -> Int -> Int 
addThree x y z = x + y + z 

addThree' :: Int -> Int -> Int -> Int 
addThree' = \x -> \y -> \z -> x + y + z

-- 上面俩函数等价

flip1 :: (a -> b -> c) -> b -> a -> c 
flip1 f y x = f x y

flip2 :: (a -> b -> c) -> b -> a -> c 
flip2 f = \y x -> f x y

-- 上面俩函数等价
复制代码

fold 折叠(类似 reduce)

foldl (\acc x -> acc + x) init list
foldr (\x acc -> x : acc) init list
foldl1 step list -- 第一个元素就是 init
foldr1 step list -- 第一个元素就是 init
复制代码

另一种方式理解折叠:折叠就是对列表中的所有元素连续地应用某个函数。如:

foldr f 0 [1,2,3,4] 
-- 其实就是
f 1 (f 2 (f 3 (f 4 0)))

foldl g 0 [1,2,3,4]
-- 其实就是
g (g (g (g 0 1) 2) 3) 4
复制代码

当二元函数 f 不总是需要对第二个参数求值时,即可通过 foldr 对无限列表做处理。fordl 则不行。

scan 与 fold 类似,区别在于 scan 会把每一次 step 的返回值记录在一个列表里。

$ 函数应用符号

后面的是函数的参数,类似 JS 的 .call。

sum $ filter (> 10) $ map (*2) [2.. 10] -- 得到 80
sum (filter (> 10) (map (*2) [2.. 10])) -- 如果不用 $ 就要加很多括号

ghci> map ($ 3) [(4+),( 10*),(^ 2), sqrt]
[7. 0, 30. 0, 9. 0, 1. 7320508075688772]
复制代码

函数组合 composition

数学定义: (f·g)(x) = f(g(x)) ,它的目的是方便生成新函数。

map (negate . sum . tail) [[1.. 5],[ 3.. 6],[ 1.. 7]] 
-- [-14,- 15,- 27] 
复制代码

如果要组合的函数有多个参数,就只能化为只有一个参数的函数(柯里化)再组合。

sum . replicate 5 $ max 6.7 8.9 --44.5
--等价于
sum . (replicate 5) $ max 6.7 8.9 -- 44.5
复制代码

技巧:如果你打算用组合来省去大量括号,可以先找出最里面的函数和参数,写下来,在前面加一个 前面,如

replicate 2 (product (map (*3) (zipWith max [1, 2] [4, 5])))
-- 改写为
replicate 2 . product . map (*3) $ zipWith max [1, 2] [4, 5]
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Design Patterns

Pro JavaScript Design Patterns

Dustin Diaz、Ross Harmes / Apress / 2007-12-16 / USD 44.99

As a web developer, you’ll already know that JavaScript™ is a powerful language, allowing you to add an impressive array of dynamic functionality to otherwise static web sites. But there is more power......一起来看看 《Pro JavaScript Design Patterns》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具