【问题标题】:Understanding Haskell recursion in functions了解函数中的 Haskell 递归
【发布时间】:2016-04-09 23:37:44
【问题描述】:

我一直在为 Haskell 使用一些资源:向您学习 Haskell 和 wikibook。但是,我正在努力寻找一种解释来帮助我更多地理解递归。我附上了“Learn you a Haskell”一书中的一段代码示例,我部分理解。

maximum' :: (Ord a) => [a] -> a  
maximum' [] = error "maximum of empty list"  
maximum' [x] = x  
maximum' (x:xs)   
    | x > maxTail = x  
    | otherwise = maxTail  
    where maxTail = maximum' xs

直到最后一行'maxTail = maximum'xs'为止,我都理解上述所有代码。我不明白的是如何评估代码以返回最大值,而只是调用最大值'。或者它如何知道最大值'是列表的最高元素。

另一个例子:

reverse' :: [a] -> [a]  
reverse' [] = []  
reverse' (x:xs) = reverse' xs ++ [x]

了解所有内容,直到在列表的尾部调用 reverse'。换句话说,它怎么知道reverse'意味着反转尾部元素。

我真的很感激一个解释,如果它很简单,我很抱歉,我是这种语言的新手。

【问题讨论】:

  • reverse' 并不意味着“反转尾部元素”。 reverse' xs 表示“反转尾部元素”,因为xs 是尾部元素,reverse' 表示反转。
  • 请注意,在递归情况下,函数 maximum'reverse' 在给定列表的 tail(即除第一个元素之外的任何元素)上调用,例如maximum' [1,2,3] 递归地应用于列表[2,3]。这个想法是整数列表的最大值是 1. 第一个列表元素和 2. 列表其余部分的最大值中的较大值。当通过(x:xs) 对列表进行模式匹配时,x 是第一个列表元素,xs 是其余元素(“尾部”)。
  • “它怎么知道reverse'意味着反转尾部元素。”它没有;但你可以通过归纳来证明。从[][a] 案例开始;然后检查[a,b], ....,然后假设它适用于一般的[a,b,...,n] 情况,并证明下一个情况[a,b,...,n,m] 也正在反转。
  • 仅供参考:您可以在这里参观:tour 并快速了解网站和所有功能/特性。

标签: haskell recursion functional-programming


【解决方案1】:

逐行逐句,希望你能更好地理解它:

1| maximum' :: (Ord a) => [a] -> a  
2| maximum' [] = error "maximum of empty list"  
3| maximum' [x] = x  
4| maximum' (x:xs)   
    | x > maxTail = x  
    | otherwise = maxTail  
    where maxTail = maximum' xs

在第 1 行:你说你得到一个 a 的列表并返回该类型的一个元素。另外,该列表中的元素必须是可订购的,因此您可以将它们排列成一个顺序。

在第 2 行:你有一个极端情况,你说如果你得到一个空列表作为输入,那么在那个空列表中不能有“最高值”,所以你以错误结束函数。

在第 3 行:你有另一个极端情况,你说如果你得到一个包含 1 个元素的列表,那个元素必须是最高元素,所以你返回它。

在第 4 行:您使用模式匹配来匹配第一个元素 (x) 和列表的其余部分 (xs)。然后检查x 是否大于maxTail,其中maxTail 是函数调用的结果maximum' 以及列表的其余部分xs

如果x 大于maxTail,则返回x,否则maxTail 大于x,则返回maxTail


我认为一个带有一些数字的例子在这里也应该有所帮助:

输入:

[2, 5, 4, 13]

呼叫:

maximum' [2, 5, 4, 13]

会发生什么:

maximum' (x  :  xs)
          ↓     ↓
maximum' (2:[5, 4, 13]) 
           │
           ↓                               result 13
     x > maxTail = x                         ↑
     2 > maximum' [5, 4, 13] = x  //2 > 13 = 13 ← ────┐
         │                                            │
         └  maximum' (x :  xs)                        │
                      ↓    ↓                          │
            maximum' (5:[4, 13])                      │
                       │                              │
                       ↓                              ↑
                 x > maxTail = x                      │
                 5 > maximum' [4, 13] = x  //5 > 13 = 13 ← ─────┐
                     │                                          │
                     └  maximum' (x: xs)                        │
                                  ↓  ↓                          │
                        maximum' (4:[13])                       │
                                   │                            │
                                   ↓                            ↑
                              x > maxTail = x                   │
                              4 > maximum' [13] = x  //4 > 13 = 13  ┐
                                  │                                 │
                                  └ maximum' [x] = x                ↑
                                    maximum' [13] = 13 → ───────────┘

在您的第二个示例中几乎相同:

1| reverse' :: [a] -> [a]  
2| reverse' [] = []  
3| reverse' (x:xs) = reverse' xs ++ [x]

在第 1 行:你说你得到一个 a 的列表并返回一个 a 的列表。

在第 2 行:你有一个边缘情况,你说如果你得到一个空列表,反向列表也只是空的。

在第 3 行:您再次使用模式匹配来匹配列表的第一个元素 (x) 和它的尾部 (xs)。

现在您只需使用++ 将两个列表附加在一起,这是列表的第一个带有反向尾部的元素。


我希望再举一个例子,它会更清楚一点:

输入:

[1, 2, 3]

呼叫:

reverse' [1, 2, 3]

会发生什么:

reverse' (x : xs)
          ↓   ↓
reverse' (1:[2, 3])
           │                               result [3, 2, 1]
           ↓                                       ↑
   (reverse' [2, 3]) ++ [1]  //[3, 2] ++ [1] = [3, 2, 1] ← ────┐
    │                                                          │
    └ reverse' (x: xs)                                         │
                ↓  ↓                                           │
      reverse' (2:[3])                                         │
                 │                                             ↑
                 ↓                                             │
         (reverse' [3]) ++ [2]  //[3] ++ [2] = [3, 2] ← ───┐ → ┘
          │                                                │
          └ reverse' (x:xs)                                │
                      ↓ ↓                                  │
            reverse' (3:[])                                │
                       │                                   ↑
                       ↓                                   │
               (reverse' []) ++ [3]  //[] ++ [3] = [3] ┐ → ┘
                │                                      ↑           
                └ reverse' [] = [] → ──────────────────┘

【讨论】:

  • @luqui 谢谢 :),我尽了最大努力,所以它很容易理解。
  • 感谢您的精彩回复,图表和细分真的帮助了我! :)
【解决方案2】:

长度

length' [] = 0

案例1:空列表的长度为0。

length' (x:xs) = 1 + length' xs

情况 2:至少有一个元素的列表的长度比列表尾部的长度多 1,我们通过重复情况 2 直到没有更多元素剩余,满足情况 1。

length' [1, 2, 3]
=
length' (1 : (2 : (3 : [])))
=
1 + length' (2 : (3 : []))
=
1 + (1 + length' (3 : []))
=
1 + (1 + (1 + length' []))
=
1 + (1 + (1 + 0))
=
1 + (1 + 1)
=
1 + 2
=
3

反向

reverse' [] = []  

情况1:如果一个列表是空的,它的反向也是一个空列表。

reverse' (x:xs) = reverse' xs ++ [x]

案例 2:如果一个列表至少有一个元素,那么我们可以拉出第一个元素,将其移动到末尾,然后以相同的方式反转其余的元素——通过在案例 2 中继续列表直到没有元素仍然,满足案例 1。

reverse' [1, 2, 3]
=
reverse' (1 : (2 : (3 : [])))
=
reverse' (2 : (3 : [])) ++ [1]
=
reverse' (3 : []) ++ [2] ++ [1]
=
reverse' [] ++ [3] ++ [2] ++ [1]
=
[] ++ [3] ++ [2] ++ [1]
=
[3, 2, 1]

最大值

maximum' [x] = x  

案例1:如果一个列表只有一个元素,则该元素是最大值。

maximum' (x:xs)   

情况 2:如果一个列表至少有一个元素,那么……

    | x > maxTail = x  

...第一个大于所有其他的,在这种情况下它是最大值;或者……

    | otherwise = maxTail  

……不是,所以最大值是所有其他中最大的……

    where maxTail = maximum' xs

...我们通过在案例 2 的列表中继续进行查找,直到只剩下一个元素,满足案例 1。

我将改写maximum' 的定义,以便更容易说明它是如何替代的:

maximum' (x:xs) = let maxTail = maximum' xs in
    if x > maxTail then x else maxTail

现在:

maximum' [1, 3, 2]
=
maximum' (1 : (3 : (2 : [])))
=
let maxTail1 = maximum' (3 : (2 : [])) in
    if 1 > maxTail1 then 1 else maxTail1
=
let maxTail1 =
    (let maxTail2 = maximum' (2 : []) in
        if 3 > maxTail2 then 3 else maxTail2) in
    if 1 > maxTail1 then 1 else maxTail1
=
let maxTail1 =
    (let maxTail2 = 2 in
        if 3 > maxTail2 then 3 else maxTail2) in
    if 1 > maxTail1 then 1 else maxTail1
=
let maxTail1 = (if 3 > 2 then 3 else 2) in
    if 1 > maxTail1 then 1 else maxTail1
=
let maxTail1 = 3 in
    if 1 > maxTail1 then 1 else maxTail1
=
if 1 > 3 then 1 else 3
=
3

【讨论】:

  • 感谢您的回答。很高兴看到它使用 if 和 else,帮助我更好地理解它:)!
猜你喜欢
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2015-11-17
  • 1970-01-01
  • 2011-02-14
  • 2019-02-28
相关资源
最近更新 更多