从右到左处理Haskell列表保持原始顺序

时间:2015-09-27 13:52:49

标签: list haskell iteration reverse

需要从Haskell列表中的右边开始每隔一个项目增加,但保持原始顺序(例如reverse不是这种情况)。例如:

 f [1, 2, 3] --  [1, 3, 3]

 f [1, 2, 3, 4] -- [2, 2, 4, 4]

我尝试了以下内容:

fc ([]) = []
fc (x:[]) = [x]
fc (x:[y]) = [x+1,y]
fc( x:xs ) = fc [x] : ( fc xs ) -- this line is wrong

P.S。显然我可以反转(但更喜欢理解原始任务)两次列表并应用类似的东西:

helper (x:y:tail) = [x, y+1] ++ tail
fc x = reverse (helper (reverse x) )

4 个答案:

答案 0 :(得分:4)

从右到左处理Haskell列表的典型方法是反转它。由于您希望获得结果的原始顺序,因此您只需再次反转:

f1 = reverse . zipWith (+) (cycle [0,1]) . reverse

但是如果你真的想要,你可以让每个递归调用返回更新的尾部和一个标志,该标志指示该位置是否从末尾开始计数,因此你知道是否增加该位置的元素:

f2 = snd . g
  where
  g []     = (False, [])
  g (x:xs) = let (addOne, xs') = g xs
                 x' = if addOne then x + 1 else x
             in (not addOne, x':xs')

我们基本上是在列表上映射一个函数,但是这个函数需要一个额外的参数,从列表的右端开始计算。我们可以使用标准功能:

import Data.List (mapAccumR)
f2' = snd . mapAccumR g False
  where
  g addOne x = (not addOne, if addOne then x + 1 else x)

答案 1 :(得分:4)

我认为你想要的一个更清晰的规范就是如果长度是偶数则增加偶数,并且奇数表示长度是奇数。例如,当从零开始索引时,长度为3的列表导致索引1递增。一种方法是使用明显的两通解决方案:

f xs = zipWith (+) (cycle sol) xs
 where sol = map fromEnum [even len, odd len] 
       len = length xs

这可以通过“打结”来一次完成(不依赖于编译器融合规则)。例如(使用手动递归样式作为通信手段)。

f2 xs = let (isEven, result) = go isEven xs in result
 where
  go _ []     = (True,     [])
  go e (x:xs) = let (ne,rest) = go (not e) xs
                in (not ne, x+fromEnum e : rest)

答案 2 :(得分:2)

使用左侧折叠

可以有效地完成此操作
inc :: Num a => [a] -> [a]
inc xs = foldl go (\_ _ acc -> acc) xs id (+ 1) []
    where go run x f g acc = run g f (f x: acc)

请注意,即使认为这是左侧折叠,列表也是使用cons (:)运算符构建的;并且它将线性地执行而不是二次(与difference lists中类似的构造)。

\> inc [1, 2, 3]
[1,3,3]
\> inc [1, 2, 3, 4]
[2,2,4,4]

它也可以推广到id(+ 1)以外的交替函数。

答案 3 :(得分:0)

我喜欢托马斯的解决方案。但是,我认为一个简单的foldr就足够了。

process = snd . foldr (\x (b,xs) -> (not b, x + fromEnum b:xs)) (False,[])