简体   繁体   English

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

[英]Apply a function to the nth element of a list

I need to be able to apply a function to the nth element of a list. 我需要能够将函数应用于列表的第n个元素。 For example: 例如:

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

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

I have a function that can do this: 我有一个功能可以做到这一点:

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

but I'm new to Haskell, and so I'm sure (as with many simple functions in Haskell) there is a much better way of doing this. 但是我是Haskell的新手,因此,我确信(与Haskell中的许多简单函数一样)有更好的方法来执行此操作。

As jamshidh indicates the lens package makes it simple to achieve this kind of task. 正如jamshidh所指出的那样, 镜头包装使完成此类任务变得简单。

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

This kind of operation works over any traversable, for example trees: 这种操作适用于所有可遍历的对象,例如树:

> 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

If you need random access to elements of a sequence, you may not want to use a list at all. 如果您需要随机访问序列的元素,则可能根本不需要使用列表。 You could, for example, use Data.Vector instead: 例如,您可以改用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

Example use: 使用示例:

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

If you don't want to dive into lenses, and prefer a simple solution, you can just use list comprehensions; 如果您不想涉足镜头,而是希望使用简单的解决方案,则可以使用列表推导; it runs on linear time, your list concatenations would degrade performance on large lists: 它以线性时间运行,您的列表串联会降低大型列表的性能:

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

So, doSomething would be: 因此, 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]

You can do this with some manual recursion pretty easily, and it will perform better than the splitAt version, as well as allocating fewer temporary objects than the list comprehension. 您可以使用一些手动递归很容易地做到这一点,它的性能将优于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

The cases are all pretty obvious: if the list is empty, you can't do anything, so return it. 情况很明显:如果列表为空,则您什么也不能做,因此将其返回。 If n is 0, then just call f on it and add that to the rest of the list. 如果n为0,则只需对其调用f并将其添加到列表的其余部分。 Otherwise, you can put the current x at the front, and recurse with a smaller n. 否则,可以将当前x放在最前面,然后递归一个较小的n。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM