无法理解haskell中的原始递归定义

时间:2011-05-09 04:35:54

标签: list haskell recursion product tail-recursion

我已经清楚原始递归定义是什么,但是我似乎仍然无法理解它。

例如,我似乎无法向自己解释如何执行以下操作(但我似乎能够这样做):

  

练习:

     

定义功能   productIt :: [Int] -> Int   它给出了整数列表的乘积,并为空列表返回1;为什么选择这个特定值作为空列表的结果?

我(当然)提出了解决方案:

productIt :: [Int] -> Int
productIt [] = 1
productIt (x:xs) = x * productIt xs

完美适用于练习中的问题。然而,我似乎仍然难以绕过最后一行。

关于如何考虑这一点的任何想法都将受到最高的赞赏。

3 个答案:

答案 0 :(得分:5)

在英语中,您可以将最后一行读作:

  

数字列表的乘积是第一个数字乘以其余数字的乘积。如果没有数字,我们只说产品是1。

将它转换为英语通常可以帮助我理解递归函数的工作原理和原因。您也可以使用头脑中的Haskell解释器来评估它:

  productIt [2,3,4]
= 2 * (productIt [3,4])
= 2 * (3 * (productIt [4]))
= 2 * (3 * (4 * (productIt [])))
= 2 * (3 * (4 * (1)))
= 2 * (3 * (4))
= 2 * (12)
= 24

答案 1 :(得分:2)

你使用1作为普通案例[]的原因是因为当你乘以它时它不会改变结果。

productIt (x:xs) = x * productIt xs

例如,假设您将其定义为:

productIt [] = 2                      -- line 1
productIt (x:xs) = x * productIt xs   -- line 2

然后考虑:

productIt [1] = productIt (1:[])
              = 1 * productIt []    -- by line 2
              = 1 * 2               -- by line 1
              = 2                   -- WRONG!!!

您正在寻找整数列表的乘积,对于[1],答案应该是1.

答案 2 :(得分:1)

在我解释最后一行代码之前,让我总结一下解决此类问题的过程。有两个关键问题:

  1. 什么是最简单的案例?
  2. 我可以用下一个最简单的解决方案来表达解决方案吗?
  3. 好的,所以我们想写一个函数来计算列表中所有数字的乘积。

    问题1:最简单的情况是没有任何数字。如果这是我第一次遇到这个问题,对我来说可能不是很明显,在这种情况下答案应该是什么,但让我们暂时把它放在一边。

    问题2:如果我有一个列表[n0,n1,n2,.. nk],并且我知道除了第一个之外的所有数字的产品(我们称之为p),那么答案就是第一个元素乘以该产品,或n0 * p。

    第一行代码将处理琐碎的案例:

    productIt [] = 1
    

    这就是说空列表参数[]的函数productIt的值为1.(我在上面解释了为什么答案必须是1.)这样可以解决这个小问题。现在我们需要在列表不为空的情况下定义productIt。让我们看看最后一行代码:

    productIt (x:xs) = ... something?
    

    左侧使用模式匹配。模式(x:xs)将匹配具有一个或多个元素的列表。当该表达式匹配时,它将x绑定到第一个元素,将xs绑定到列表的其余部分。因此,我们不仅匹配模式,我们将x和xs定义为奖励。这就是使模式匹配在Haskell中非常强大的原因。

    因此,如果列表的第一个元素是x,列表的其余部分(除第一个元素之外的所有元素)都是xs,那么答案是什么?我们已经确定它是所有剩余元素(xs)的乘积的第一个元素(x)。所以......

    productIt (x:xs) = x * productIt xs
    

    另外,yjerem给出了一个关于Haskell如何评估它的出色解释。