理解Haskell中的递归
我很难理解如何以递归的方式思考问题,并使用Haskell解决问题。我花了好几个小时的时间阅读,试图对递归进行思考。我最常从理解它的人那里得到的解释从来都不清楚,类似于“你传递一个函数,函数的名称作为参数,然后函数将执行,解决问题的一小部分,并一次又一次地调用函数,直到达到基本情况” 有没有人能给我介绍一下这三个简单递归函数的思想过程?与其说是它们的功能,不如说是代码如何递归地执行和解决问题 非常感谢 职能1理解Haskell中的递归,haskell,Haskell,我很难理解如何以递归的方式思考问题,并使用Haskell解决问题。我花了好几个小时的时间阅读,试图对递归进行思考。我最常从理解它的人那里得到的解释从来都不清楚,类似于“你传递一个函数,函数的名称作为参数,然后函数将执行,解决问题的一小部分,并一次又一次地调用函数,直到达到基本情况” 有没有人能给我介绍一下这三个简单递归函数的思想过程?与其说是它们的功能,不如说是代码如何递归地执行和解决问题 非常感谢 职能1 maximum' [] = error "maximum of empty list"
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:rest) = max x(maximum' rest)
职能2
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xs
查看功能3:
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
假设你称之为反向“[1,2,3],那么
1. reverse' [1,2,3] = reverse' [2,3] ++ [1]
reverse' [2,3] = reverse' [3] ++ [2] ... so replacing in equation 1, we get:
2. reverse' [1,2,3] = reverse' [3] ++ [2] ++ [1]
reverse' [3] = [3] and there is no xs ...
** UPDATE ** There *is* an xs! The xs of [3] is [], the empty list.
We can confirm that in GHCi like this:
Prelude> let (x:xs) = [3]
Prelude> xs
[]
So, actually, reverse' [3] = reverse' [] ++ [3]
Replacing in equation 2, we get:
3. reverse' [1,2,3] = reverse' [] ++ [3] ++ [2] ++ [1]
Which brings us to the base case: reverse' [] = []
Replacing in equation 3, we get:
4. reverse' [1,2,3] = [] ++ [3] ++ [2] ++ [1], which collapses to:
5. reverse' [1,2,3] = [3,2,1], which, hopefully, is what you intended!
也许你可以试着对另外两个做一些类似的事情。选择小参数。成功 指导方针 当试图理解递归时,您可能会发现更容易考虑算法对于给定输入的行为。执行路径看起来是什么样子很容易让人挂断电话,因此,要问自己这样的问题:
- 如果我传递一个空列表会发生什么
- 如果我传递一个包含一项的列表,会发生什么
- 如果我传递了一个包含许多项的列表,会发生什么
- 如果我传递一个负数会发生什么
- 如果我超过0会发生什么
- 如果我传递的数字大于0,会发生什么
maximum' (x:rest) = max x (maximum' rest)
用[1,2]
调用此函数可扩展为:
maximum [1, 2] ~ max 1 (maximum' [2])
~ max 1 2
max'
通过返回一个数字来工作,本例知道如何使用max
递归处理该数字。让我们再看一个案例:
maximum [0, 1, 2] ~ max 0 (maximum' [1, 2])
~ max 0 (max 1 2)
~ max 0 2
您可以看到,对于这个输入,第一行中对max'
的递归调用与前面的示例完全相同
反向'
相反的方法是将给定列表的开头粘贴到末尾。对于空列表,这不涉及任何工作,因此这是基本情况。因此,根据定义:
reverse' (x:xs) = reverse' xs ++ [x]
让我们做一些替换。考虑到[x]
相当于x:[]
,您可以看到实际上有两个值需要处理:
reverse' [1] ~ reverse' [] ++ 1
~ [] ++ 1
~ [1]
很简单。对于双元素列表:
reverse' [0, 1] ~ reverse' [1] ++ 0
~ [] ++ [1] ++ 0
~ [1, 0]
拿走
此函数在整数参数和列表上引入递归,因此有两种基本情况
take' n _ | n <= 0 = []
take' -1 [1] = []
take' 0 [1] = []
take' n (x:xs) = x : take' (n-1) xs
因此,在首先满足数字基本情况的情况下,我们在到达列表末尾之前停止
take' 1 [9, 8] ~ 9 : take (1-1) [8]
~ 9 : take 0 [8]
~ 9 : []
~ [9]
在首先满足列表基本情况的情况下,我们在计数器达到0之前用完了项,只返回我们能返回的
take' 3 [9, 8] ~ 9 : take (3-1) [8]
~ 9 : take 2 [8]
~ 9 : 8 : take 1 []
~ 9 : 8 : []
~ [9, 8]
递归是将特定函数应用于集合的一种策略。将函数应用于该集合的第一个元素,然后对其余元素重复该过程 让我们举一个例子,您希望将列表中的所有整数加倍。首先,您考虑我应该使用哪个函数?答案->
2*
,现在您必须递归地应用此函数。让我们称之为apply\u rec
,因此您有:
apply_rec (x:xs) = (2*x)
但这只会更改第一个元素,您希望更改集合中的所有元素。因此,您还必须将apply_rec
应用于其余元素。因此:
apply_rec (x:xs) = (2*x) : (apply_rec xs)
现在你有了一个不同的问题。apply\u rec
何时结束?当您到达列表的末尾时,它将结束。换句话说,[]
,所以您也需要介绍这个案例
apply_rec [] = []
apply_rec (x:xs) = (2*x) : (apply_rec xs)
当您到达终点时,您不想应用任何函数,因此函数apply\u rec
应该“return”[]
让我们看看这个函数在集合中的行为=[1,2,3]
apply_rec[1,2,3]=(2*1):(apply_rec[2,3])
apply_rec[2,3]=2:((2*2):(apply_rec[3])
apply_rec[3]=2:(4:((2*3):(apply_rec[])
apply_rec[]=2:(4:(6:[]))
[2,4,6]
由于您可能不太了解递归,最好从比您所介绍的更简单的示例开始。再看看这个。也许你提出问题的方式不是很好,我的意思是,这不是通过研究现有递归函数的实现,你将了解如何复制它。我更愿意为您提供另一种方法,它可以被视为一个系统化的过程,帮助您编写递归调用的标准框架,然后促进关于它们的推理 你们所有的例子都是关于列表的,当你们使用列表的时候,首先要做的是详尽无遗,我的意思是使用模式匹配
rec_fun [] = -- something here, surely the base case
rec_fun (x:xs) = -- another thing here, surely the general case
现在,基本情况不能包含recursive,否则您肯定会得到一个无限循环,那么基本情况应该返回一个值,掌握这个值的最好方法是查看函数的类型注释
例如:
reverse :: [a] -> [a]
可以鼓励您将基础情况视为类型[a]的值,如[/]针对反向
maximum :: [a] -> a
可以鼓励您将基本情况视为最大值
类型A的值。 现在是递归部分,正如前面所说,函数应该apply_rec [] = []
apply_rec (x:xs) = (2*x) : (apply_rec xs)
rec_fun [] = -- something here, surely the base case
rec_fun (x:xs) = -- another thing here, surely the general case
reverse :: [a] -> [a]
maximum :: [a] -> a
rec_fun (x:xs) = fun x rec_fun xs
rec_fun (x:xs) = x `fun` rec_fun xs
g :: Integer -> Bool
g x | x<=0 = False
g 1 = True
g 2 = True
g x = x == y+z where
y = head [y | y<-[x-1,x-2..], g y] -- biggest y<x that g y
z = head [z | z<-[y-1,y-2..], g z] -- biggest z<y that g z
filter g [0..]
reverse' l
| lenL == 1 || lenL == 0 = l
where lenL = length l
reverse' xs ++ [x]
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]