haskell函数进阶
Haskell 函数进阶
本文会涉及 Haskell 中的部分进阶函数概念.这些功能是 Haskell 编程的基础,也是许多高级功能的核心.函数不仅是输入和输出的工具,它们在 Haskell 中是一等公民,可以像数据一样传递、组合和操作.这种特性使得 Haskell 编程语言能够通过组合简单的函数来构建复杂的程序结构.Haskell 还具有惰性求值、类型推导和强大的高阶函数功能,让它非常适合表达数学模型和处理抽象问题.由于haskell官方库的东西太多太杂,而且有很多完全是实验性质的内容,本文只涉及一些极为常用的部分.
简化函数
简化函数是 Curry 化的应用,最常见的用途就是省去这个函数紧接着的参数
示例1
2
3
4
5divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..'Z'])
说明
divideByTen
: 这个函数接受一个数字,将其除以 10.它实际上是通过 Currying 技术将除法操作简化成了一个只接受一个参数的函数.isUpperAlphanum
: 检查字符是否属于大写英文字母.
注意:
(-4)
表示负号操作.subtract 4
表示减去 4 的操作.
函数作为参数
Haskell 中的函数可以作为输入参数传递.
类型 (a -> a)
:表示 f
是一个将 a
类型值映射到 a
类型值的函数.
示例1
2applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
说明
applyTwice
:这个函数接受一个函数f
和一个值x
,然后将f
应用两次于x
.例如,applyTwice (+3) 10 会先将 10 增加 3 得到 13,再把 13 增加 3 得到 16.
更多示例1
2
3
4
5
6
7
8
9
10
11
12applyTill :: (Ord t1, Num t1) => (t2 -> t2) -> t2 -> t1 -> t2
applyTill f x n
| n > 1 = f (applyTill f x (n - 1))
| otherwise = f x
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
flip' :: (a -> b -> c) -> b -> a -> c
flip' f y x = f x y
applyTill
: 这个函数会将函数f
应用到x
上n
次,直到达到终止条件.例如,applyTill (+1) 0 5 会递归地把 +1 操作应用 5 次.zipWith'
: 按照给定的二元函数,将两个列表对应位置的元素进行组合.flip'
: 交换二元函数的参数顺序.
Map and Filter
map 和 filter 是列表操作的两大经典函数,它们的核心思想是将函数作用于列表中的每个元素,或者根据条件筛选元素.map 是一种批量操作,它将给定的函数作用于列表中的每个元素.而 filter 则是基于条件对列表进行筛选.它们都是典型的高阶函数,因为它们接受其他函数作为参数.这种函数式操作使得代码更加简洁,避免了显式的循环结构.
示例1
2
3map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
map
:将给定的函数f
应用于列表的每个元素.例如,map (*2) [1,2,3] 会返回 [2,4,6].
1 | filter :: (a -> Bool) -> [a] -> [a] |
filter
:根据谓词p
过滤列表中的元素.例如,filter (>2) [1,2,3,4] 会返回 [3,4],筛选出大于 2 的元素.
Fold
折叠操作是一种将列表中的元素合并成一个单一结果的方法.Haskell 提供了 foldl 和 foldr 两种方式,分别从左到右和从右到左地遍历列表.它们本质上是一种将递归模式转化为迭代的方式,在处理列表时非常高效.
示例1
2sum' :: (Num a) => [a] -> a
sum' xs = foldl (CHUacc x -> acc + x) 0 xs
- sum’ :使用 foldl 从左到右对列表求和.foldl 的第一个参数是一个累积函数,0 是初始值,xs 是要遍历的列表.
1 | map' :: (a -> b) -> [a] -> [b] |
map’ :使用 foldr 实现的 map,它将每个元素通过函数 f 转换后返回新的列表.
折叠的关键:遍历列表并根据元素返回结果.
- 区别:
foldl
从左到右,foldr
从右到左,右折叠可以处理无限列表.
运算符 $ 和 组合 .
运算符 $
$ 运算符是 Haskell 中一个非常特殊的运算符,它的优先级非常低(低于所有常见的操作符).事实上, $ 运算符的优先级是最低的(0),所以它会吞噬掉表达式中多余的括号.$
运算符的作用是将它右边的表达式应用到左边的函数上,等价于将一个函数应用于一个表达式.
在实际使用中,$ 最主要的作用是用来 减少括号,特别是在嵌套的函数调用中,避免过多的括号.
示例1
2ghci> sqrt $ 3 + 4 + 9
ghci> map ($ 3) [(4+), (10*), (^2), sqrt]
- $ :使用 $ 运算符,表达式 3 + 4 + 9 会先被求值,然后传递给 sqrt 函数,等同于 sqrt (3 + 4 + 9).
组合.
. 运算符是 函数组合运算符,它的作用是将多个函数组合成一个函数.. 运算符的优先级是较高的(9),比大多数运算符都要高.它是右结合的,也就是说它会将最右边的函数优先组合..通过这个运算符,您可以把多个简单的函数“串联”起来,形成一个新的函数,从而避免冗长的函数调用.
组合运算符:1
fn = ceiling . negate . tan . cos . max 50
.
:将多个函数组合起来,使得函数调用更直观.fn 是一个组合函数,它先求 x 和 50 的最大值,再进行 cos 和 tan 运算,最后取负并取整.
$ 和 . 结合使用的例子
有时,我们会在同一个表达式中同时使用 $ 和 .,这通常是为了优化代码的结构,使其更加简洁.例如:1
2ghci> ceiling . sqrt $ 3 + 4 + 9
4
在这个例子中,$ 用来减少括号的使用,ceiling . sqrt 则先计算平方根,然后对结果取整.$ 确保 3 + 4 + 9 会先被求值,然后传递给组合后的函数.