14

我知道这xs !! n给了我列表中的第 n 个元素,但我不知道如何编辑该列表中的第 n 个元素。您能告诉我如何编辑列表中的第 n 个元素或至少给出提示吗?

例如,我怎样才能使第二个元素'a''e'这个:['s','t','a','c','k']

4

5 回答 5

21

更改第 n 个元素

许多语言中的常见操作是分配给数组中的索引位置。在 python 中,你可能:

>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]

镜头包为操作员提供 了此功能(.~)。尽管与 python 不同,原始列表没有发生突变,而是返回了一个新列表。

> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]

element 3 .~ 9只是一个函数,而(&)操作符是 镜头包的一部分,只是反向函数应用程序。这里是更常见的功能应用。

> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]

对于 s 的任意嵌套,赋值再次工作得很好Traversable

> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]

或者

> set (element 3) 9 [1,2,3,4,5,6,7]

或者,如果您想影响多个元素,您可以使用:

> over (elements (>3)) (const 99) [1,2,3,4,5,6,7]
> [1,2,3,4,99,99,99]

使用列表以外的类型

然而,这不仅限于列表,它适用于作为Traversable类型类实例的任何数据类型。

例如,相同的技术适用于标准 容器包的树。

 > import Data.Tree
 > :{
 let
  tree = Node 1 [
       Node 2 [Node 4[], Node 5 []]
     , Node 3 [Node 6 [], Node 7 []]
     ]
 :}
> putStrLn . drawTree . fmap show $ tree
1
|
+- 2
|  |
|  +- 4
|  |
|  `- 5
|
`- 3
   |
   +- 6
   |
   `- 7
> putStrLn . drawTree . fmap show $ tree & element 1 .~ 99
1
|
+- 99
|  |
|  +- 4
|  |
|  `- 5
|
`- 3
   |
   +- 6
   |
   `- 7
> putStrLn . drawTree . fmap show $ tree & element 3 .~ 99
1
|
+- 2
|  |
|  +- 4
|  |
|  `- 99
|
`- 3
   |
   +- 6
   |
   `- 7
> putStrLn . drawTree . fmap show $ over (elements (>3)) (const 99) tree
1
|
+- 2
|  |
|  +- 4
|  |
|  `- 5
|
`- 99
   |
   +- 99
   |
   `- 99
于 2013-03-20T18:30:11.190 回答
17

因为 Haskell 是一种函数式语言,你不能“编辑”列表中的元素,因为一切都是不可变的。相反,您可以使用以下内容创建一个新列表:

take n xs ++ [newElement] ++ drop (n + 1) xs

但是,在 Haskell 中不建议这样做。有关更多信息,您可以查看这篇文章:Haskell replace element in list

于 2013-03-20T17:36:14.167 回答
10

您不能编辑列表的第 n 个元素,值是不可变的。您必须创建一个新列表。但由于不可变性,它可以与原始列表共享更改元素之后的部分。

因此,如果您想对列表的第 n 个元素应用转换(并且前后部分相同),则需要三个部分

  • 有问题的元素之前的列表的前面,比如说front
  • 有问题的元素,说element
  • 在有问题的元素之后的列表后面,比如说back.

然后你会组装零件

front ++ transform element : back

所以仍然需要以一种很好的方式抓住有趣的部分。

splitAt :: Int -> [a] -> ([a],[a])

这样做,splitAt idx list返回列表的第一部分,在索引之前idx作为对的第一个组成部分,其余部分作为第二个组成部分,所以

changeNthElement :: Int -> (a -> a) -> [a] -> [a]
changeNthElement idx transform list
    | idx < 0   = list
    | otherwise = case spliAt idx list of
                    (front, element:back) -> front ++ transform element : back
                    _ -> list    -- if the list doesn't have an element at index idx

(注:我已经从0开始计数元素,如果要从1开始计数,需要调整和使用idx-1。)

于 2013-03-20T17:34:19.530 回答
2

我很惊讶以下方法尚未提及,因此我将添加它以供进一步参考:

replace index elem = map (\(index', elem') -> if index' == index then elem else elem') . zip [0..]

> replace 2 'e' "stack"
"steck"

它处理超出范围索引的情况。

> replace (-1) 'z' "abc"
"abc"
> replace 0 'z' "abc"
"zbc"
> replace 2 'z' "abc"
"abz"
> replace 3 'z' "abc"
"abc"

它并不比 splitAt 方法( O(2N) )慢。

于 2020-02-24T16:49:45.937 回答
0

也有可能编写一个简单的递归解决方案

基本思想是,为了替换列表中的元素#5,您只需替换该列表尾部的元素#4。

使用@DanielFisher 的答案中的符号,这给出了以下代码:

changeNthElement :: Int -> (a -> a) -> [a] -> [a]
changeNthElement n fn   []    =  []   -- nothing to change
changeNthElement n fn (x:xs)
    |  (n < 0)    =  x:xs             -- no change for a negative index
    |  (n == 0)   =  (fn x) : xs                          -- easy case
    |  otherwise  =  x : (changeNthElement (n-1) fn xs)   -- recursion

如果新值不依赖于旧值,则可以特化上述函数:

setNthElement :: Int -> a  -> [a] -> [a]
setNthElement n v xs = changeNthElement n (const v) xs

测试:

$ ghci
 GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 ...
 λ> 
 λ> :load q15530511.hs
 [1 of 1] Compiling Main             ( q15530511.hs, interpreted )
 Ok, one module loaded.
 λ> 
 λ> xs = replicate 10 7
 λ> 
 λ> xs
 [7,7,7,7,7,7,7,7,7,7]
 λ> 
 λ> changeNthElement  3 (+2) xs
 [7,7,7,9,7,7,7,7,7,7]
 λ> 
 λ> setNthElement 2 42 xs
 [7,7,42,7,7,7,7,7,7,7]
 λ> 

于 2022-01-25T18:39:25.857 回答