[英]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
這是否是“慣用的”是一個意見問題(如果它適合那里我會把它作為評論),但看到許多不同的方法可能是有用的。 您的解決方案與我的解決方案或評論中的解決方案一樣有效,並且更容易更改為mapOnlyEvery13nd
或mapOnlyPrimeNumbered
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
形式將其包裝起來。
是的,這也可以使用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
來完成它,我將留給你弄清楚。
這更像是對@ 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.