我想将一个函数应用于列表中的每个第二个元素:
> mapToEverySecond (*2) [1..10]
[1,4,3,8,5,12,7,16,9,20]
我已经写了以下功能:
mapToEverySecond :: (a -> a) -> [a] -> [a]
mapToEverySecond f l = map (\(i,x) -> if odd i then f x else x) $ zip [0..] l
这很有效,但我想知道是否有更惯用的方式来做这样的事情。
答案 0 :(得分:7)
我还没有写过很多Haskell,但这是我想到的第一件事:
func :: (a -> a) -> [a] -> [a]
func f [] = []
func f [x] = [x]
func f (x:s:xs) = x:(f s):(func f xs)
这有点过分,因为你不仅需要处理空列表,还需要处理带有一个元素的列表。这也不能很好地扩展(如果你想要每三分之一,或
可以像@Landei指出的那样,写下
func :: (a -> a) -> [a] -> [a]
func f (x:s:xs) = x:(f s):(func f xs)
func f xs = xs
为了摆脱[]
和[x]
的丑陋检查,恕我直言,这使得它更难阅读(至少第一次)。
答案 1 :(得分:5)
我将如何做到这一点:
mapOnlyOddNumbered f [] = []
mapOnlyOddNumbered f (x:xs) = f x : mapOnlyEvenNumbered f xs
mapOnlyEvenNumbered f [] = []
mapOnlyEvenNumbered f (x:xs) = x : mapOnlyOddNumbered f xs
这是否是"惯用的"是一个意见问题(如果它适合那里我会把它作为评论),但看到许多不同的方法可能是有用的。您的解决方案与我的解决方案或评论中的解决方案一样有效,并且更容易更改为mapOnlyEvery13nd
或mapOnlyPrimeNumbered
答案 2 :(得分:4)
mapToEverySecond = zipWith ($) (cycle [id, (*2)])
我能想到的是最小的,在我看来也很清楚。它还有点与每个 n 进行缩放。
编辑:哦,人们已经在评论中提出了建议。我不想偷它,但我真的认为这就是答案。
答案 3 :(得分:2)
我可能会这样做:
mapToEverySecond f xs = foldr go (`seq` []) xs False
where
go x cont !mapThisTime =
(if mapThisTime then f x else x) : cont (not mapThisTime)
但如果我正在编写库代码,我可能会以build
形式将其包装起来。
是的,这也可以使用mapAccumL
或traverse
完成。
import Control.Applicative
import Control.Monad.Trans.State.Strict
import Data.Traversable (Traversable (traverse), mapAccumL)
mapToEverySecond :: Traversable t => (a -> a) -> t a -> t a
-- Either
mapToEverySecond f = snd . flip mapAccumL False
(\mapThisTime x ->
if mapThisTime
then (False, f x)
else (True, x))
-- or
mapToEverySecond f xs = evalState (traverse step xs) False
where
step x = do
mapThisTime <- get
put (not mapThisTime)
if mapThisTime then return (f x) else return x
或者您可以使用scanl
来完成此操作,我将留下您的答案。
答案 4 :(得分:0)
这是对@ MartinHaTh的回答的更多评论。我稍微优化了他的解决方案
func :: (a -> a) -> [a] -> [a]
func f = loop
where
loop [] = []
loop [x] = [x]
loop (x:s:xs) = x : f s : loop xs
答案 5 :(得分:0)
不是很优雅,但这是我的看法:
mapToEverySecond f = reverse . fst . foldl' cmb ([], False) where
cmb (xs, b) x = ((if b then f else id) x : xs, not b)
或改进MartinHaTh的答案:
mapToEverySecond f (x : x' : xs) = x : f x' : mapToEverySecond f xs
mapToEverySecond _ xs = xs