第 07 章:递归函数
01 递归函数
在前文中已经看到,可以基于已有的函数定义新的函数:
fac :: Int -> Int
fac n = product [1..n]
在很多情况下,一个函数可以通过自身对自身进行定义:
fac :: Int -> Int
fac 0 = 1
fac n = n * fac (n-1)
这类函数称为 递归函数 (Recursive Function)。
02 为什么需要递归函数?
-
一些函数,其递归定义方式更为简洁
-
一些函数,其定义本身就天然存在递归
-
在一些情况下,递归定义的函数,其数学性质更易于证明
03 List 上的递归函数
递归不仅适用于整数类型,也适用于 List 以及其他类型。
示例:List 中元素的乘积
product :: Num a => [a] -> a
product [] = 1
product (n:ns) = n * product ns
示例:List 的长度
length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs
示例:List 逆序
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
示例:插入排序
isort :: Ord a => [a] -> [a]
isort [] = []
isort (x:xs) = insert x (isort xs)
insert :: Ord a => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys) | x <= y = x:y:ys
| otherwise = y:(insert x ys)
04 多参数递归
具有多个参数的函数,也可以进行递归定义。
示例:zip 函数
zip :: [a] -> [b] -> [(a,b)]
zip [] _ = []
zip _ [] = []
zip (x:xs) (y:ys) = (x, y) : zip xs ys
示例:drop 函数
drop :: Int -> [a] -> [a]
drop 0 xs = xs
drop _ [] = []
drop n (_:xs) = drop (n-1) xs
示例:序列拼接函数
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
05 多重递归 (Multiple Recursion)
所谓 多重递归,指的是:在定义一个函数时,对函数自身进行了多次递归调用。
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n - 2) + fib (n - 1)
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
where
smaller = [a | a <- xs, a <= x]
larger = [b | b <- xs, b > x]
qsort [3, 2, 4, 1, 5]
=== qsort [2,1] ++ [3] ++ qsort [4,5]
=== qsort [1] ++ [2] ++ qsort [] ++ [3] ++ qsort [] ++ [4] ++ qsort [5]
=== [1] ++ [2] ++ [] ++ [3] ++ [] ++ [4] ++ [5]
06 互递归 (Mutual Recursion)
所谓 互递归,指的是:在定义两或多个函数时,这些函数通过相互调用对方进行定义。
even :: Int -> Bool
even 0 = True
even n = odd (n-1)
odd :: Int -> Bool
odd 0 = False
odd n = even (n-1)
本章作业
作业 01
在不查看 Prelude 源码的情况下,使用递归定义如下函数:
- 判断
[Bool]
类型的一个值中的所有元素是否都为True
and :: [Bool] -> Bool
- 将类型
[[a]]
的一个值中包含的所有 list 拼接为一个 listconcat :: [[a]] -> [a]
- 获得一个 list 中编号为
n
的元素 (从0
开始编号)(!!) :: [a] -> Int -> a
- 生成一个包含
n
个重复元素的 listreplicate :: Int -> a -> [a]
- 判断一个元素是否包含在一个 list 中
elem :: Eq a => a -> [a] -> Bool
作业 02
采用递归的方式定义如下函数:
merge :: Ord a => [a] -> [a] -> [a]
该函数接收两个已经处于从小到大排序状态的 list,然后把其中包含的所有元素归并成一个保持排序状态的 list。
例如:
ghci> merge [2,5,6] [1,3,4] [1,2,3,4,5,6]
作业 03
采用递归的方式定义归并排序函数:
msort :: Ord a => [a] -> [a]
它的递归定义包含两条规则:
- 长度小于 2 的 list 已经处于排序状态
- 对于长度大于 1 的 list,将其从中间断开,形成两个更短的 list,然后:
- 对这两个更短的 list 分别进行归并排序
- 将排序后形成的两个 list 进行归并