需要从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) )
答案 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,[])