将函数应用于列表中的每个第二个元素

时间:2015-01-31 21:40:35

标签: list haskell

我想将一个函数应用于列表中的每个第二个元素:

> 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

这很有效,但我想知道是否有更惯用的方式来做这样的事情。

6 个答案:

答案 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

这是否是"惯用的"是一个意见问题(如果它适合那里我会把它作为评论),但看到许多不同的方法可能是有用的。您的解决方案与我的解决方案或评论中的解决方案一样有效,并且更容易更改为mapOnlyEvery13ndmapOnlyPrimeNumbered

答案 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形式将其包装起来。

修改

是的,这也可以使用mapAccumLtraverse完成。

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