Foldr应用程序

时间:2012-11-27 15:57:15

标签: haskell

最近我正在尝试使用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编写此程序?

2 个答案:

答案 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 = 5xs = [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的回答所示。