如何编辑Haskell列表中的第n个元素?

问题描述 投票:12回答:4

我知道xs !! n给我列表中的第n个元素,但是我不知道如何编辑该列表中的第n个元素。您能告诉我如何编辑列表中的第n个元素或至少给出提示吗?例如,如何使第二个元素“ a”成为“ e”:['s,'t','a','c','k']?谢谢。

list haskell edit
4个回答
12
投票

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

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

但是,在Haskell中不建议使用。有关更多信息,请参见此文章:Haskell replace element in list


16
投票

更改第n个元素

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

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

lens程序包使用(.~)运算符提供此功能。尽管与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只是一个函数,而(&)运算符是lens程序包,只是反向功能应用程序。这是具有更常见功能的应用程序。

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

[Assignment再次与Traversable s的任意嵌套一起很好地工作。

> [[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类型类实例的任何数据类型一起使用。

例如以相同的技术对标准的trees起作用containers程序包。

 > 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

9
投票

您无法编辑列表的第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。)


0
投票

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

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))慢。

© www.soinside.com 2019 - 2024. All rights reserved.