Haskell - 如何以优雅的方式以相反的顺序迭代列表元素?

时间:2013-12-08 21:20:28

标签: list haskell iteration reverse

我正在尝试编写一个给出数字列表的函数,返回一个列表,其中每个第二个数字的值加倍,从最后一个元素开始。因此,如果列表元素是1..n,则第n个将保持原样,(n-1)-th将在值中加倍,(n-2)-th将被保留原样等等。

所以这就是我解决它的方法:

MyFunc :: [Integer] -> [Integer]
MyFunc xs = reverse (MyFuncHelper (reverse xs))

MyFuncHelper :: [Integer] -> [Integer]
MyFuncHelper []       = []
MyFuncHelper (x:[])   = [x]
MyFuncHelper (x:y:zs) = [x,y*2] ++ MyFuncHelper zs

它有效:

MyFunc [1,1,1,1] = [2,1,2,1]
MyFunc [1,1,1] = [1,2,1]

然而,我不禁想到必须有一个更简单的解决方案,而不是反转列表,处理它然后再次反转它。我可以简单地向后迭代列表吗?如果是,怎么样?

5 个答案:

答案 0 :(得分:9)

under reversed f xs库中的lens成语将以相反的顺序将x应用于xs:

under reversed (take 5) [1..100] => [96,97,98,99,100]

答案 1 :(得分:3)

当您需要从最后处理列表时,通常foldr效果很好。这里有一个解决方案,无需反转整个列表两次:

doubleOdd :: Num a => [a] -> [a]
doubleOdd = fst . foldr multiplyCond ([], False)
    where multiplyCond x (rest, flag) = ((if flag then (x * 2) else x) : rest, not flag)

multiplyCond函数采用带有标志和累加器列表的元组。旗帜不断地打开和关闭以跟踪我们是否应该增加元素。累加器列表只是收集结果数字。这个解决方案可能不是那么简洁,但是避免额外的工作,除了前奏函数之外不使用任何东西。

答案 2 :(得分:1)

myFunc = reverse
       . map (\(b,x) -> if b then x*2 else x)
       . zip (cycle [False,True])
       . reverse

但这并没有好多少。您的实施非常优雅。

答案 3 :(得分:0)

向后迭代列表的最简单方法是反转列表。我认为你真的不能做得更好;我怀疑如果你必须遍历整个列表以找到结束,并记住如何恢复,你可能只是反过来。如果这是一个大问题,也许您应该使用其他一些数据结构而不是列表 - VectorSeq可能是不错的选择。

编写辅助函数的另一种方法是使用Traversable

import Control.Monad.State
import Data.Traversable (Traversable, traverse)

toggle :: (Bool -> a -> b) -> a -> State Bool b
toggle f a = 
    do active <- get
       put (not active)
       return (f active a)

doubleEvens :: (Num a, Traversable t) => t a -> t a
doubleEvens xs = evalState (traverse (toggle step) xs) False
    where step True x  = 2*x
          step False x = x

yourFunc :: Num a => [a] -> [a]
yourFunc = reverse . doubleEvens 

或者如果我们对FoldableTraversable感到有点疯狂,我们可以试试这个:

  1. 使用Foldable的{​​{1}}从其任何实例中提取逆序列表。对于某些类型,这比撤消列表更有效。
  2. 然后我们可以使用foldltraverse以相反的顺序将原始结构的每个元素映射到其对应元素。
  3. 以下是如何操作:

    State

    可能有更好的方法来做到这一点,但是......

答案 4 :(得分:0)

func xs = zipWith (*) xs $ reverse . (take $ length xs) $ cycle [1,2]