使用案例表达式Haskell进行递归

时间:2018-12-09 20:40:54

标签: haskell

考虑以下代码,其中函数sumFood采用 列出xs的{​​{1}}并返回一个整数元组。在此元组中,第一个整数表示Food中包含的水果数量,第二个整数表示蔬菜数量。

xs

但是如果我键入module Apples where data Food = Apple | Orange | Carrot | Potato deriving(Show) sumFood :: [Food] -> (Int, Int) sumFood (x:xs) = let v=0 f=0 in if ((length xs)-1) > 0 then case x of Apple -> (f + 1, v) Orange -> (f + 1, v) Carrot -> (f, v + 1) Potato -> (f, v + 1) else sumFood xs sumFood [Apple , Orange],它将返回(0,0),答案应该是(2,0)。

实现必须使用大小写表达式。

也许提示会有用。

2 个答案:

答案 0 :(得分:3)

您实际上已经掌握了大部分正确的点点滴滴,但顺序错误-您忘记了递归的结果。

您可以替换fv的值,然后看看发生了什么:

sumFood (x:xs) = let v = 0
                     f = 0  in 
    if ((length xs)-1) > 0  then
        case x of  Apple -> (f + 1, v)
                   Orange -> (f + 1, v)
                   Carrot -> (f, v + 1)
                   Potato -> (f, v + 1)
    else sumFood xs

成为

sumFood (x:xs) = 
    if ((length xs)-1) > 0  then
        case x of  Apple -> (0 + 1, 0)
                   Orange -> (0 + 1, 0)
                   Carrot -> (0, 0 + 1)
                   Potato -> (0, 0 + 1)
    else sumFood xs

现在,((length xs)-1) > 0length xs > 1,表示length (x:xs) > 2,因此实际上您拥有

  • 如果列表包含两个以上的元素,则结果为(1,0)(0,1)
  • 否则,递归。

现在(希望)很明显,结果只能是(1,0)(0,1)-除非输入的元素少于三个,在这种情况下,您最终会遇到模式匹配失败。

主要问题是您永远不会使用递归的结果,因此结果始终是基本情况的结果。

首先,一个有用的经验法则是永远不要使用length;在列表结构上使用模式匹配。

从基本情况开始:空列表既不包含水果也不包含蔬菜。

sumFood [] = (0,0)

接下来,您需要从递归中获取结果,然后将其添加到结果的适当元素中:

sumFood (x:xs) = let (f, v) = sumFood xs 
                 in
                    case x of Apple -> (f + 1, v)
                   ...

答案 1 :(得分:1)

Bifunctor的{​​{1}}实例使此操作变得容易;它使您可以使用(,)firstsecond应用于元组的适当元素,这使您可以简单地将列表折叠成元组。

(+1)

如果需要使用import Data.Bifunctor sumFood :: [Food] -> (Int, Int) sumFood = foldr foo (0,0) where foo Apple = first (+1) foo Orange = first (+1) foo Carrot = second (+1) foo Potato = second (+1) 表达式,请注意,定义一个函数的多个方程式只是一个语法糖:

case

如果您也被禁止使用sumFood :: [Food] -> (Int, Int) sumFood = foldr foo (0,0) where foo food = case food of Apple -> first (+1) Orange -> first (+1) Carrot -> second (+1) Potato -> second (+1) ,那么就可以轻松实现自己的运行:

Bifunctor