带case表达式的递归Haskell

带case表达式的递归Haskell,haskell,Haskell,考虑以下代码,其中函数sumFood采用 列出Food的xs,并返回一个整数元组。在这个元组中,第一个整数表示xs中包含的水果数量,第二个整数表示蔬菜数量 module Apples where data Food = Apple | Orange | Carrot | Potato deriving(Show) sumFood :: [Food] -> (Int, Int) sumFood (x:xs) = let v=0 f=0

考虑以下代码,其中函数
sumFood
采用 列出
Food
xs
,并返回一个整数元组。在这个元组中,第一个整数表示
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]
[Apple,Apple]
它将返回(0,0),答案应该是(2,0)

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


也许一个提示会有用。

实际上,您已经获得了大部分正确的位和片段,但顺序错误-并且您忘记了对递归的结果执行任何操作

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

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)>0
length xs>1
,这意味着
length(x:xs)>2
,所以实际上

  • 如果列表包含两个以上的元素,则结果为
    (1,0)
    (0,1)
  • 否则,将递归
现在(希望)很明显,结果只能是
(1,0)
(0,1)
——除非输入少于三个元素,否则最终会遇到模式匹配失败

主要问题是,您从未使用递归的结果,因此结果始终是基本情况的结果

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

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

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

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

(,)
Bifunctor
实例使这变得容易;它允许您使用
第一个
第二个
(+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
,那么您可以很容易地动态实现自己:

sumFood :: [Food] -> (Int, Int)
sumFood = foldr foo (0,0)
  where foo food = case food of
                     Apple -> \(f,v) -> (f+1,v)
                     Orange -> \(f,v) -> (f+1,v)
                     Carrot -> \(f,v) -> (f,v+1)
                     Potato -> \(f,v) -> (f,v+1)

您的实现只匹配具有一个元素的列表。。。看一看我知道这就是为什么我在这里问我知道递归,我试图使用它,但与大小写表达式!!!很难,你是不是在问如何匹配列表?这与递归无关。我的问题是,如果我键入[Apple,Orange,Apple]=>我将得到(3,0),我想我需要递归,我不知道!!!所以在递归的每个步骤中,您都会有一个列表,其中少了一个元素。。。最后是一张空名单。你最好考虑一下。此外,在
case
中,您将检查当前元素而不是列表,如苹果的
case x->…
等更合适。