简体   繁体   English

"如何在 Haskell 列表的第 n 个元素中设置值?"

[英]How to set value in nth element in a Haskell list?

I know that xs !! n我知道xs !! n xs !! n gives me nth element in a list, but I don't know how to edit nth element in that list. xs !! n给了我列表中的第 n 个元素,但我不知道如何编辑该列表中的第 n 个元素。 Can you tell me how can I edit nth element in a list or give a hint at least?您能告诉我如何编辑列表中的第 n 个元素或至少给出提示吗?

For example how can I make the second element 'a' an 'e' in this: ['s','t','a','c','k'] ?例如,我怎样才能使第二个元素'a'成为'e'['s','t','a','c','k']

Changing the nth element更改第 n 个元素<\/h2>

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]<\/code><\/pre>

The lens<\/a> package gives this functionality with the (.~)<\/code> operator. lens<\/a>包通过(.~)<\/code>运算符提供了此功能。 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]<\/code><\/pre>

element 3 .~ 9<\/code> is just a function and the (&)<\/code> operator, part of the lens<\/a> package, is just reverse function application. element 3 .~ 9<\/code>只是一个函数,而(&)<\/code>运算符(镜头<\/a>包的一部分)只是反向函数应用程序。 Here it is with more common function application.这里是更常见的功能应用。

 > (element 3 .~ 9) [1,2,3,4,5] [1,2,3,9,5]<\/code><\/pre>

Assignment again works perfectly fine with arbitrary nesting of Traversable<\/code> s.对于Traversable<\/code>的任意嵌套,赋值再次工作得很好。

 > [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9 [[1,9,3],[4,5,6]]<\/code><\/pre>

or或者

> set (element 3) 9 [1,2,3,4,5,6,7]<\/code><\/pre>

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]<\/code><\/pre>

Working with types other then lists使用列表以外的类型<\/h2>

This is not just limited to lists however, it will work with any datatype that is an instance of the Traversable<\/a> typeclass.然而,这不仅限于列表,它适用于作为Traversable<\/a>类型类实例的任何数据类型。

Take for example the same technique works on trees<\/a> form the standard containers<\/a> package.例如,相同的技术适用于标准容器<\/a>包的树<\/a>。

 > 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<\/code><\/pre>"

Because Haskell is a functional language, you cannot 'edit' elements in lists, because everything is immutable.因为 Haskell 是一种函数式语言,你不能“编辑”列表中的元素,因为一切都是不可变的。 Instead, you can create a new list with something like:相反,您可以使用以下内容创建一个新列表:

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

You can't edit the nth element of a list, values are immutable.您不能编辑列表的第 n 个元素,值是不可变的。 You have to create a new list.您必须创建一个新列表。 But due to the immutability, it can share the part after the changed element with the original list.但由于不可变性,它可以与原始列表共享更改元素之后的部分。

So if you want to apply a transformation to the nth element of a list (and have the parts before and after identical), you have three parts因此,如果您想对列表的第 n 个元素应用转换(并且前后部分相同),则需要三个部分

  • the front of the list before the element in question, say front有问题的元素之前的列表的前面,比如说front
  • the element in question, say element有问题的元素,比如说element
  • the back of the list after the element in question, say back .在有问题的元素之后的列表后面,说back

Then you'd assemble the parts然后你会组装零件

front ++ transform element : back

so it remains to get a hold on the interesting parts in a nice way.所以仍然需要以一种很好的方式抓住有趣的部分。

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

does that, splitAt idx list gives back the first part of the list, before the index idx as the first component of the pair, and the rest as the second, so这样做, 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

(Note: I have started counting elements at 0, if you want to start counting at 1, you need to adjust and use idx-1 .) (注:我已经从0开始计数元素,如果要从1开始计数,需要调整并使用idx-1 。)

I' surprised the following method was not yet mentioned, so I will add it for further reference:我很惊讶以下方法尚未提及,因此我将添加它以供进一步参考:

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

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

There is also the possibility of writing a simple recursive solution<\/strong> .也有可能编写一个简单的递归解决方案<\/strong>。

The basic idea is that, in order to substitute element #5 in a list, you just have to substitute element #4 in the tail<\/em> of that list.基本思想是,为了替换列表中的元素#5,您只需替换该列表尾部<\/em>的元素#4。

Using the notations from the answer of @DanielFisher, this gives the following code:使用@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

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

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