list 替换 Haskell 中的单个列表元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5852722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:39:12  来源:igfitidea点击:

Replace individual list elements in Haskell?

listhaskellstate

提问by maclunian

I have a list of elements and I wish to update them:

我有一个元素列表,我希望更新它们:

from this: ["Off","Off","Off","Off"]

由此: ["Off","Off","Off","Off"]

to this: ["Off","Off","On","Off"]

对此: ["Off","Off","On","Off"]

As I am somewhat new to Haskell, I have been using (x:xs)!!yto extract and update individual components using the function:

由于我对 Haskell 有点陌生,因此我一直(x:xs)!!y使用该函数来提取和更新单个组件:

replace y z [] = []
replace y z (x:xs)
  | x==y           = z:replace y z xs
  | otherwise      = x:replace y z xs

and then entering the following in ghci: (replace "Off" "On" ["Off",'Off","Off","Off"]) !! 2

然后在ghci中输入以下内容: (replace "Off" "On" ["Off",'Off","Off","Off"]) !! 2

I get the following: "On"

我得到以下信息: "On"

I seem to be able to extract and convert elements of a list but I can't seem to get a list up with the single element converted.

我似乎能够提取和转换列表的元素,但我似乎无法通过转换的单个元素获得列表。

Any help regarding this matter would be appreciated.

任何有关此事的帮助将不胜感激。

采纳答案by Philip JF

I'm not sure what you are trying to do. If you only need to generate ["Off","Off","On","Off"] you can do it explicitly. Generally speaking, one should avoid modifying state in haskell.

我不确定你想做什么。如果你只需要生成 ["Off","Off","On","Off"] 你可以明确地做。一般来说,应该避免在haskell中修改状态。

Perhaps what you want is a function to "modify" (generate a new element with a different value) the nth element of a list? Don gives a very general approach to this kind of problem. You can also use explicit recursion:

也许您想要的是一个函数来“修改”(生成具有不同值的新元素)列表的第 n 个元素?唐对这类问题给出了一个非常通用的方法。您还可以使用显式递归:

 replaceNth :: Int -> a -> [a] -> [a]
 replaceNth _ _ [] = []
 replaceNth n newVal (x:xs)
   | n == 0 = newVal:xs
   | otherwise = x:replaceNth (n-1) newVal xs

Haskell provides excellent features for list manipulation. If you dont know them already filter, map, and foldr/foldlare all worth looking at, as are list comprehensions.

Haskell 为列表操作提供了出色的功能。如果您还不知道它们filtermap, 和foldr/foldl都值得一看,列表推导也是如此。

回答by Don Stewart

Typically, you modify elements of a list by splitting the list, replacing an element, and joining it back together.

通常,您可以通过拆分列表、替换元素并将其重新连接在一起来修改列表的元素。

To split a list at an index, we have:

要在索引处拆分列表,我们有:

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

which you can use to break up a list, like so:

您可以使用它来分解列表,如下所示:

 > splitAt 2 ["Off","Off","Off","Off"] 
 (["Off","Off"],["Off","Off"])

now you just need to pop the head element of the sndcomponent of the list. This is easily done with pattern matching:

现在你只需要弹出snd列表组件的头部元素。这可以通过模式匹配轻松完成:

 > let (x,_:ys) = splitAt 2 ["Off","Off","Off","Off"]
 > x
 ["Off","Off"]
 > ys
 ["Off"]

you can now join the list back together, with an "On":

您现在可以使用“On”将列表重新连接在一起:

 > x ++ "On" : ys
 ["Off","Off","On","Off"]

I'll leave it to you to put those pieces together into a single function.

我会让你把这些部分放在一个单一的函数中。



As a style note, I'd suggest using a new custom data type, instead of Stringfor your toggles:

作为样式说明,我建议使用新的自定义数据类型,而不是String用于切换:

 data Toggle = On | Off deriving Show

回答by Davorak

Changing the nth element

更改第 n 个元素

A common operation in many languages is to assign to an indexed position in an array. In python you might:

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

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

The lenspackage gives this functionality with the (.~)operator. Though unlike in python the original list is not mutated, rather a new list is returned.

镜头包给出与此功能(.~)操作。尽管与 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 .~ 9is just a function and the (&)operator, part of the lenspackage, is just reverse function application. Here it is with more common function application.

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

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

Assignment again works perfectly fine with arbitrary nesting of Traversables.

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

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

or

或者

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

Or if you want to effect multiple elements you can use:

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

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

Working with types other then lists

使用列表以外的类型

This is not just limited to lists however, it will work with any datatype that is an instance of the Traversabletypeclass.

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

Take for example the same technique works on treesform the standard containerspackage.

以标准 容器包对的相同技术为例。

 > 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

回答by Cristian Garcia

Here is a one liner that works perfectly

这是一个完美的单衬

replace pos newVal list = take pos list ++ newVal : drop (pos+1) list

I doesn't seem efficient to do this kind of things in haskell.

我在haskell中做这种事情似乎效率不高。

回答by mhwombat

Here's some code that I've been using:

这是我一直在使用的一些代码:

-- | Replaces an element in a list with a new element, if that element exists.
safeReplaceElement
  -- | The list
  :: [a]
  -- | Index of the element to replace.
  -> Int
  -- | The new element.
  -> a
  -- | The updated list.
  -> [a]
safeReplaceElement xs i x =
  if i >= 0 && i < length xs
    then replaceElement xs i x
    else xs


-- | Replaces an element in a list with a new element.
replaceElement
  -- | The list
  :: [a]
  -- | Index of the element to replace.
  -> Int
  -- | The new element.
  -> a
  -- | The updated list.
  -> [a]
replaceElement xs i x = fore ++ (x : aft)
  where fore = take i xs
        aft = drop (i+1) xs

回答by Zsolt Szatmari

Actually, for many cases (not always) where you'd use a List, a Data.Vector is a better choice.

实际上,对于使用 List 的许多情况(并非总是如此),Data.Vector 是更好的选择。

It comes with an update function, see Hackage, that does exactly what you need.

它带有一个更新功能,请参阅Hackage,这正是您所需要的。

回答by Thomas M. DuBuisson

I think you should consider using a data structure other than List. For example, if you just want to have a state of four on/off switches then:

我认为您应该考虑使用 List 以外的数据结构。例如,如果您只想拥有四个开/关开关的状态,则:

data State = St { sw1, sw2, sw3, sw4 :: Bool }

For a dynamic number of switches then consider a mapping from switch nameto Bool.

对于动态数量的开关,请考虑从switch name到的映射Bool

回答by Ziai

I believe this is more elegant way of replacing an individual element:

我相信这是替换单个元素的更优雅的方式:

setelt:: Int -> [a] -> a -> [a]

setelt i list newValue = 
  let (ys,zs) = splitAt i-1 list in  ys ++ newValue ++ tail zs

There is input error handling. So if index iis out of boundaries haskell will show wrong output. (Note: in Haskell indexing starts from 1 onwards)

有输入错误处理。因此,如果索引i超出边界,haskell 将显示错误的输出。(注意:在 Haskell 中索引从 1 开始)

Hugs would behave as follows:

拥抱的行为如下:

Main> setelt 1 [1,2,3] 9
[9,2,3]
Main> setelt 3 [1,2,3] 9
[1,2,9]
Main> setelt 0 [1,2,3] 9
[9,2,3]
Main> setelt 4 [1,2,3] 9
[1,2,3,9]
Program error: pattern match failure: tail []

Error handling at your service:

为您服务的错误处理:

setelt i y newValue = 
    if and [i>0, i<= length y]
    then let (ys,zs) = splitAt (i-1) y in  ys ++ [newValue] ++ tail zs
    else y

if index you give is wrong it returns original list.

如果您提供的索引错误,则返回原始列表。

回答by theideasmith

This answer arrives quite late, but I thought I'd share what I think is an efficientway of replacing the nthelement in a list in Haskell. I'm new to Haskell and thought I'd pitch in.

这个答案来得很晚,但我想我会分享我认为在 Haskell中替换列表中元素的有效方法nth。我是 Haskell 的新手,我想我会加入。

The setfunction setsthe nth element in a list to a given value:

set函数列表中的第 n 个元素设置为给定值:

set' :: Int -> Int -> a -> [a] -> [a]
set' _ _ _ [] = []
set' ind curr new arr@(x:xs)
    | curr > ind = arr
    | ind == curr = new:(set' ind (curr+1) new xs)
    | otherwise = x:(set' ind (curr+1) new xs)

set :: Int -> a -> [a] -> [a]
set ind new arr = (set' ind 0 new arr)

As settraverses a list, it breaks the list apart and if the current index is the given nit combines the previous element with the given new value, otherwise, it combines the previous element with the old value in the list for that index.

set遍历列表时,它将列表分开,如果当前索引是给定的n,它将前一个元素与给定的新值组合,否则,它将前一个元素与该索引的列表中的旧值组合。