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


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

Любая помощь была бы отличной

Спасибо

Марк.

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

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

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

[H,H,E,H,H]

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

Спасибо.

6 9

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]

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

В то время как использование функций из 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]. 

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

Правка: один (скорее плохое) решение 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 (из модуля 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]

Не очень красиво, но, бьюсь об заклад, вполне эффективно.