我正在尝试编写一个给出数字列表的函数,返回一个列表,其中每个第二个数字的值加倍,从最后一个元素开始。因此,如果列表元素是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]
然而,我不禁想到必须有一个更简单的解决方案,而不是反转列表,处理它然后再次反转它。我可以简单地向后迭代列表吗?如果是,怎么样?
答案 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)
向后迭代列表的最简单方法是反转列表。我认为你真的不能做得更好;我怀疑如果你必须遍历整个列表以找到结束,并记住如何恢复,你可能只是反过来。如果这是一个大问题,也许您应该使用其他一些数据结构而不是列表 - Vector
或Seq
可能是不错的选择。
编写辅助函数的另一种方法是使用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
或者如果我们对Foldable
和Traversable
感到有点疯狂,我们可以试试这个:
Foldable
的{{1}}从其任何实例中提取逆序列表。对于某些类型,这比撤消列表更有效。foldl
和traverse
以相反的顺序将原始结构的每个元素映射到其对应元素。以下是如何操作:
State
可能有更好的方法来做到这一点,但是......
答案 4 :(得分:0)
func xs = zipWith (*) xs $ reverse . (take $ length xs) $ cycle [1,2]