Как изменить элемент в списке в erlang

у меня есть список, в котором я использовал списки функций:nth() on для возврата значения элемента по определенному индексу. Кто-нибудь знает как я могу изменить это значение?

любая помощь будет большое

спасибо

Марк.

EDIT: вот немного больше информации. Скажем, у меня был список L, который представляет собой строку текстовой сетки

L = [H,H,H,H,H].

и я хочу получить доступ к указанному элементу, скажем, к третьему и изменить его на E. Тогда, если бы я снова использовал список L, это было бы

[H,H,E,H,H]

Я надеюсь, что это имеет больше смысла.

спасибо.

6 ответов


список неизменяем, поэтому вы не можете" изменить " элемент в списке. Если вы действительно хотите заменить элемент в данной позиции, вы должны добавить список перед элементом с (измененным) элементом и оставшимся списком:

1> L=[1,2,3,4,5].
[1,2,3,4,5]
2> lists:sublist(L,2) ++ [lists:nth(3,L)*100] ++ lists:nthtail(3,L).
[1,2,300,4,5]

EDIT: сценарий немного необычный, хотя... У вас есть конкретная проблема? Возможно, это можно выразить лучше, например, с помощью lists: map?


при использовании функции lists может привести к коду, который кажется более ясным, он менее эффективен как список элементов до элемент, который вы хотите изменить, будет скопирован дважды. Эффективнее написать функцию самостоятельно, и так как вы, вероятно, обернете код с помощью lists в функции, я не чувствую, что это будет менее понятно.

вместо кода @D. Nibon я бы написал функцию как:

%% setnth(Index, List, NewElement) -> List.

setnth(1, [_|Rest], New) -> [New|Rest];
setnth(I, [E|Rest], New) -> [E|setnth(I-1, Rest, New)].

%% Can add following caluse if you want to be kind and allow invalid indexes.
%% I wouldn't!
%% setnth(_, [], New) -> New.

порядок аргументов может быть обсуждается; к сожалению lists модуль не помогает здесь, поскольку он несовместим внутри модуля. Хотя это не хвостовая рекурсивная функция, я чувствую, что она яснее. Кроме того, разница в эффективности невелика или не существует, поэтому я бы пошел с ясностью. Дополнительные сведения об этой проблеме см.:

http://www.erlang.org/doc/efficiency_guide/myths.html#tail_recursive
http://www.erlang.org/doc/efficiency_guide/listHandling.html#id64759

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


при работе со списками a все элементы часто имеют одинаковый тип данных или значение. Вы редко видите такие списки, как ["John Doe","1970-01-01","London"] а #person{name="John Doe",...} или даже {"Джон Доу",...}. Чтобы изменить значение в записи и кортежем:

-record(person,{name,born,city}).
f(#person{}=P) -> P#person{city="New City"}. % record
f({_,_,_,}=Tuple) -> erlang:setelement(3,Tuple,"New City"). % tuple 

это может ничего не решить для вашей конкретной проблемы. Чтобы взять свой собственный пример в комментарии:

f1([H1,H2,_H3,H4,H5],E) -> [H1,H2,E,H4,H5]. 

если вы дадите более конкретное описание окружающей среды и проблемы, легче определить, под каким решением может работать лучший.

Edit: одно (довольно плохое) решение 1.

replacenth(L,Index,NewValue) -> 
 {L1,[_|L2]} = lists:split(Index-1,L),
 L1++[NewValue|L2].

1> replacenth([1,2,3,4,5],3,foo).
[1,2,foo,4,5]

или немного более эффективным в зависимости от длины ваших списков.

replacenth(Index,Value,List) ->
 replacenth(Index-1,Value,List,[],0).

replacenth(ReplaceIndex,Value,[_|List],Acc,ReplaceIndex) ->
 lists:reverse(Acc)++[Value|List];
replacenth(ReplaceIndex,Value,[V|List],Acc,Index) ->
 replacenth(ReplaceIndex,Value,List,[V|Acc],Index+1).

еще лучше моя функция f1 выше, но, возможно, просто, возможно, проблема все еще находится, как обсуждалось выше или здесь.


L = [H,H,H,H,H].

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

[H,H,E,H,H]

быть настоящим придурком. В Эрланге данные постоянные и неизменяемые. После определения L = ... часть L установлен в камне. Вы не можете изменить это с этого момента. Что вы можете сделать, это создать новое значение и привязать его к другой переменная, L1 Скажите, а затем прекратите использовать L. Затем сборщик мусора быстро сделает короткую работу L и переработать память, которую он использовал.

следовательно, несколько неправильно говорить, что использование L снова меняет свое содержание, так как это невозможно.

еще один момент стоит упомянуть, что если вы используете список, как если бы это был массив, то вы можете использовать массив (из array модуль) или использовать дикт (из dict модуль). Оно значительно увеличивает скорость поиска и обновления в вашем случае. Однако, если то, что вы делаете больше всего, - это обход списка для обработки всех элементов, список, вероятно, станет победителем.


Если вы сформируете свой список так, чтобы он был сделан из кортежей, вы можете использовать списки:keyreplace.


1> lists:reverse(element(2, lists:foldl(fun(E, {I, L}) -> {I + 1, [case I of 2 -> e; _ -> E end|L]} end, {0, []}, [h,h,h,h,h]))).

[h,h,e,h,h]

не очень, но я уверен, что это довольно эффективно.