2014-10-08 43 views
3

實現對於列表中的一個函數映射到每個第n個元素的功能:可以mapEvery與foldr相似

mapEvery :: Int -> (a -> a) -> [a] -> [a] 
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id) 

是否有可能與foldr像普通map來實現這一點?

編輯:在標題中,將'文件夾'更改爲'foldr'。自動更正...

+0

我不會期望如此,至少不會有單一的摺疊。 – 2014-10-08 16:38:00

+0

嗯?這真的很有趣。你將如何用多個'foldr'來實現它? – 2014-10-08 16:42:50

+0

...沒關係,不要認爲這也可以。使用'foldr',你只能計算你列表的_end_中有多少元素,而不是從一開始就有多少元素。 – 2014-10-08 16:45:02

回答

6

這裏有一個解決方案

mapEvery :: Int -> (a -> a) -> [a] -> [a] 
mapEvery n f as = foldr go (const []) as 1 where 
    go a as m 
    | m == n = f a : as 1 
    | otherwise = a : as (m+1) 

這將使用「foldlfoldr」招自左沿爲你折列表右側傳狀態。本質上,如果我們將foldr的類型讀作(a -> r -> r) -> r -> [a] -> r,那麼我們將r實例化爲Int -> [a],其中傳遞的整數是我們未調用函數而傳遞的當前元素數。

1

是的,它可以:

mapEvery :: Int -> (a -> a) -> [a] -> [a] 
mapEvery n f xs 
    = foldr (\y ys -> g y : ys) [] 
    $ zip [1..] xs 
    where 
     g (i, y) = if i `mod` n == 0 then f y else y 

而且因爲它是possible to implement zip in terms of foldr,你可以得到更多的摺疊-Y,如果你真的想要的。這甚至適用於無限列表:

> take 20 $ mapEvery 5 (+1) $ repeat 1 
[1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2] 

這是它看起來像更foldr和內聯g

mapEvery :: Int -> (a -> a) -> [a] -> [a] 
mapEvery _ _ [] = [] 
mapEvery n f xs 
    = foldr (\(i, y) ys -> (if i `mod` n == 0 then f y else y) : ys) [] 
    $ foldr step (const []) [1..] xs 
    where 
     step _ _ [] = [] 
     step x zipsfn (y:ys) = (x, y) : zipsfn ys 

現在,我會建議寫這樣說?絕對不。儘管您仍然可以編寫「可讀」代碼,但您仍然可以獲得這種混淆。但它確實證明了可以使用非常強大的foldr來實現相對複雜的功能。