Как изменить элемент в списке в erlang - PullRequest
10 голосов
/ 23 января 2011

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

любая помощь будет отличной

спасибо

Марк.

РЕДАКТИРОВАТЬ: Вот немного больше информации. Скажем, у меня был список L, представляющий строку текстовой сетки

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

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

[H,H,E,H,H]

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

Спасибо.

Ответы [ 6 ]

18 голосов
/ 23 января 2011

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

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]

РЕДАКТИРОВАТЬ: Сценарий немного необычный, хотя ... У вас есть конкретные проблемы под рукой? Возможно, это можно выразить лучше, например, списки: карта?

11 голосов
/ 24 января 2011

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

Вместо кода @D.Нибон Я бы написал функцию как:

%% 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, который будетвызывается со старым значением и возвращает новое значение.В библиотеке я, вероятно, имел бы и то и другое.

3 голосов
/ 24 января 2011
L = [H,H,H,H,H].

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

[H,H,E,H,H]

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

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

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

3 голосов
/ 23 января 2011

При работе со списками все элементы часто имеют одинаковый тип данных или значение.Вы редко видите списки типа ["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 выше, но, может быть, просто может быть проблемавсе еще находится, как описано выше, или здесь .

0 голосов
/ 14 марта 2015

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

0 голосов
/ 25 января 2011
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]

Не красиво, но держу пари, что это весьма эффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...