Как редактировать N-й элемент в списке Haskell?

Я знаю, что xs!!n дает мне n-й элемент в списке, но я не знаю как редактировать N-ый элемент в этом списке. Можете ли вы сказать мне, как я могу редактировать N-й элемент в списке или дать подсказку? Например, как я могу сделать второй элемент " a " и " e " в этом: ["s", "t", "a", "c", "k"]? Спасибо.

3 ответов


поскольку Haskell является функциональным языком, вы не можете редактировать элементы в списках, потому что все является неизменным. Вместо этого вы можете создать новый список с чем-то вроде:

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

однако в Haskell это не рекомендуется. Для получения дополнительной информации вы можете увидеть это сообщение:Haskell заменить элемент в списке


изменение 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]

назначение снова отлично работает с произвольной вложенностью Traversables.

> [[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]

работа с типами, отличными от списков

это не ограничивается только списки, однако, он будет работать с любым типом данных, который является экземпляром из проходимым typeclass.

возьмите, например, тот же метод работает на деревьев форма стандартная тара.

 > 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

вы не можете редактировать 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.)