最近我正在尝试使用Foldr解决问题。任务如下:
In:[5,1,3,8,2,4,7,1]
Out:[16,8]
这意味着,我将输入列表中的那些元素加倍,这些元素位于奇数索引位置和偶数位。我没有使用以下的foldr编写程序:(它显示模式匹配失败:head [])
findPos list elt =
map fst $ filter ((elt==).snd) $ zip [0..] list
doublePos [] = []
doublePos (x:xs)
| ((head(findPos xs x)`mod` 2) /= 0) && (x `mod` 2 == 0) =
[2*x] ++ doublePos xs
| otherwise = doublePos xs
如何使用foldr编写此程序?
答案 0 :(得分:5)
foldr
对于这个函数来说不是一个好的选择,因为你需要从列表的前面传递每个元素的索引的奇偶校验。
列表理解可能是最干净的:
doublePos xs = [2*x | (i,x) <- zip [0..] xs, even x, odd i]
或者您可以使用普通的旧递归:
doublePos' (_:x:xs)
| even x = (2*x) : doublePos' xs
| otherwise = doublePos' xs
doublePos' _ = []
但是,如果必须使用foldr
,可以通过将累加器作为函数来实现
它将当前索引的奇偶校验作为参数:
doublePos'' xs = foldr step (const []) xs False where
step x next p
| p && even x = 2*x : next (not p)
| otherwise = next (not p)
答案 1 :(得分:2)
为什么现有代码会为您提供模式匹配失败:doublePos [5,1,3,8,2,4,7,1]
将第二个等式与x = 5
和xs = [1,3,8,2,4,7,1]
匹配。这会导致head (findPos [1,3,8,2,4,7,1] 5)
被评估,但会减少到head []
,您就会收到错误。
为了扩展这一点:你似乎希望摆脱findPos
的是当前元素的索引,相对于原始列表的开头。但实际上你得到的是当前元素的下一次出现的索引,相对于下一个元素......如果它不再出现,则会出错。
(此处使用字符作为列表元素,以避免列表索引和列表元素之间的混淆。)
0 1 2 3 4 5 6 7 8 9 10 <-- indices relative to start
'H':'e':'l':'l':'o':' ':'t':'h':'e':'r':'e':[] <-- original list
| |
x = 'e' | V say we're here
xs = 'l':'l':'o':' ':'t':'h':'e':'r':'e':[] head (findPos xs x) = 6 but we wanted 1
| ^
x = 'o' say we're here instead
xs = ' ':'t':'h':'e':'r':'e':[] head (findPos xs x) = error "head []" but we wanted 4
唯一可行的方法是将原始列表传递给findPos
。但是,您唯一可用的列表是您尚未递归的原始列表的一部分。无论如何,有更好的方法可以做到这一点,如hammar的回答所示。