将函数应用于列表的第n个元素

时间:2014-12-19 21:06:57

标签: haskell

我需要能够将函数应用于列表的第n个元素。例如:

> doSomething (+5) 2 [1,2,3,4,5]

应该返回[1,7,3,4,5]

我有一个可以做到这一点的功能:

doSomething :: (a -> a) -> Int -> [a] -> [a]
doSomething f n xs = ys ++ [f x] ++ zs
    where (ys, x:zs) = splitAt (n - 1) xs

但我是Haskell的新手,所以我确信(与Haskell中的许多简单函数一样)有更好的方法。

4 个答案:

答案 0 :(得分:3)

由于jamshidh表示lens包使得完成此类任务变得简单。

> over (element 2) (+5) [1..5]
[1,2,8,4,5]

这种操作适用于任何可遍历的,例如树:

> import Data.Tree
> let tree = Node 1 [Node 2 [], Node 3 []]
> putStr . drawTree . fmap show $ tree
1
|
+- 2
|
`- 3
> putStr . drawTree . fmap show $ over (element 2) (+5) tree
1
|
+- 2
|
`- 8

答案 1 :(得分:3)

如果您需要随机访问序列的元素,您可能根本不想使用列表。例如,您可以改为使用Data.Vector

import Data.Vector (Vector)
import qualified Data.Vector as V

modifyNth :: Int -> (a -> a) -> Vector a -> Vector a
modifyNth n f = V.imap f'
    where f' i a | i == n    = f a
                 | otherwise = a

使用示例:

>>> modifyNth 2 (+5) (V.fromList [1,2,3,4,5])
fromList [1,2,8,4,5]

答案 2 :(得分:2)

如果您不想深入了解镜头,而更喜欢简单的解决方案,您可以使用列表推导;它在线性时间运行,列表连接会降低大型列表的性能:

Prelude> [if i == 2 then v + 5 else v | (i, v) <- zip [1..] l]
[1,7,3,4,5]

所以,doSomething将是:

Prelude> let doSomething f i l = [if p == i then f v else v | (p, v) <- zip [1..] l]
Prelude> doSomething (+5) 2 [1,2,3,4,5]
[1,7,3,4,5]

答案 3 :(得分:2)

您可以非常轻松地使用一些手动递归来执行此操作,并且它将比splitAt版本执行得更好,并且分配的临时对象少于列表推导。

doSomething :: (a -> a) -> Int -> [a] -> [a]
doSomething _f _ [] = []
doSomething f 0 (x:xs) = f x : xs
doSomething f n (x:xs) = x : doSomething f (n - 1) xs

案例非常明显:如果列表为空,则无法执行任何操作,因此请将其返回。如果n为0,则只需在其上调用f并将其添加到列表的其余部分。否则,你可以 将当前的x放在前面,然后用较小的n递归。