递归模式
Functional languages excel at wholemeal programming, a term coined by Geraint Jones. Wholemeal programming means to think big: work with an entire list, rather than a sequence of elements; develop a solution space, rather than an individual solution; imagine a graph, rather than a single path. The wholemeal approach often offers new insights or provides new perspectives on a given problem. It is nicely complemented by the idea of projective programming: first solve a more general problem, then extract the interesting bits and pieces by transforming the general program into more specialised ones.
全麦编程/整体编程 强调要从整体上看问题。
在haskell这样一门函数式编程语言中, 递归是非常常用和重要的操作。
而大部分都递归操作其实是存在一些共性的, 尤其是对于列表:
- 对列表每个元素进行一些操作, 得到新列表
- 去除不满足条件的一些元素
- 由列表计算值
- 生成一个列表
如去除不满足条件的一些元素, 本质上是对列表进行一次遍历, 并对每个元素应用一段逻辑来决定是否要去除, 最后得到新的一个列表。
具体的判断逻辑我们并不在意, 重要的是递归遍历 + 逻辑判断这个相同的"递归逻辑", 我们叫他递归模式。
根据我们学过的知识, 很容易就能写出这样的代码:
filterIntList :: (Int -> Bool) -> [Int] -> [Int]
filterIntList f [] = []
filterIntList f (x:xs)
| f x = x : filterIntList f xs
| otherwise = filterIntList f xs
封装/抽取递归模式, 使得我们只需要编写具体的业务逻辑即可, 屏蔽了大量没必要的关注点。
泛型
filterIntList :: (Int -> Bool) -> [Int] -> [Int]
filterIntList f [] = []
filterIntList f (x:xs)
| f x = x : filterIntList f xs
| otherwise = filterIntList f xs
这个函数还不错, 但它只能用在[Int]
类型上。
如果要进一步提升它的抽象程度, 就需要使用泛型, 让它能适配于任何类型的列表。
类型上的泛型
在自定义类型上使用泛型, 只需要将泛型符号加到类型名后即可。
如一个列表类型:
data List e = EMPTY | CONS e (List e)
EMPTY
, 空参构造函数, 表示列表的结束CONS
, 列表的结点e
: 结点存储的数据(LIST e)
: 下一个结点
其中e
叫做类型变量, 它代表了一个不确定的类型, 需要由使用时指定或推断。
使用具有泛型的类型也很简单, 就像为一个函数传参一样:
lst1 :: List Int
lst1 = CONS 3 (CONS 2 EMPTY)
请注意, 类型变量必须以小写字母开头, 而类型以大写字母开头
函数上的泛型
很简单 ,直接在函数类型定义上使用类型变量即可。
myFilter :: (e -> Bool) -> [e] -> [e]
myFilter f [] = []
myFilter f (x : xs)
| f x = x : myFilter f xs
| otherwise = myFilter f xs
main = do
print (myFilter (> 3) [1, 2, 3, 4, 5])
Prelude
Prelude是haskell的标准库, 它是默认导入, 因此不需要进行任何导入即可使用它。
prelude中有非常非常多的实用函数!像map
、filter
、foldl
等等。