考虑以下代码,其中函数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)。
实现必须使用大小写表达式。
也许提示会有用。
答案 0 :(得分:3)
您实际上已经掌握了大部分正确的点点滴滴,但顺序错误-您忘记了递归的结果。
您可以替换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)
-除非输入的元素少于三个,在这种情况下,您最终会遇到模式匹配失败。
主要问题是您永远不会使用递归的结果,因此结果始终是基本情况的结果。
首先,一个有用的经验法则是永远不要使用length
;在列表结构上使用模式匹配。
从基本情况开始:空列表既不包含水果也不包含蔬菜。
sumFood [] = (0,0)
接下来,您需要从递归中获取结果,然后将其添加到结果的适当元素中:
sumFood (x:xs) = let (f, v) = sumFood xs
in
case x of Apple -> (f + 1, v)
...
答案 1 :(得分:1)
Bifunctor
的{{1}}实例使此操作变得容易;它使您可以使用(,)
和first
将second
应用于元组的适当元素,这使您可以简单地将列表折叠成元组。
(+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