簡體   English   中英

將函數應用於列表中的每個第二個元素

[英]Apply a function to every second element in a list

我想將一個函數應用於列表中的每個第二個元素:

> 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

這有效,但我想知道是否有更慣用的方法來做這樣的事情。

我沒有寫過很多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]的丑陋檢查,這使得它更難以閱讀(至少第一次)。

我將如何做到這一點:

mapOnlyOddNumbered f []      = []
mapOnlyOddNumbered f (x:xs)  = f x : mapOnlyEvenNumbered f xs

mapOnlyEvenNumbered f []     = []
mapOnlyEvenNumbered f (x:xs) = x : mapOnlyOddNumbered f xs

這是否是“慣用的”是一個意見問題(如果它適合那里我會把它作為評論),但看到許多不同的方法可能是有用的。 您的解決方案與我的解決方案或評論中的解決方案一樣有效,並且更容易更改為mapOnlyEvery13ndmapOnlyPrimeNumbered

mapToEverySecond = zipWith ($) (cycle [id, (*2)])

我能想到的是最小的,在我看來也很清楚。 它也有點與每n個比例。

編輯:哦,人們已在評論中提出建議。 我不想偷它,但我真的認為這就是答案。

這是我可能會這樣做的:

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來完成它,我將留給你弄清楚。

這更像是對@ MartinHaTh的答案的評論。 我稍微優化了他的解決方案

func :: (a -> a) -> [a] -> [a]
func f = loop
  where
    loop []  = []
    loop [x] = [x]
    loop (x:s:xs) = x : f s : loop xs

不是很優雅,但這是我的看法:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM