【问题标题】:How to change an element in a list in erlang如何在erlang中更改列表中的元素
【发布时间】:2011-06-14 03:29:36
【问题描述】:

我有一个列表,我使用函数lists:nth() 返回某个索引处元素的值。有谁知道我可以如何编辑这个值?

编辑:这里有更多信息。

假设我有一个列表 L,它代表一行基于文本的网格

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

我想访问一个指定的元素,例如第三个元素并将其更改为 E。然后如果我要再次使用列表 L,它将是

[H,H,E,H,H]

我希望这更有意义。

谢谢。

【问题讨论】:

标签: list erlang element edit


【解决方案1】:

列表是不可变的,因此您不能“更改”列表中的项目。如果您确实想替换给定位置的项目,则应在具有(已更改)元素的元素和剩余列表的元素之前附加列表:

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]

编辑:不过,这种情况有点不寻常……您手头有具体问题吗?也许它可以用例如更好地表达。列表:地图?

【讨论】:

  • 说我有列表 L=[H,H,H,H,H]。我想将第三个元素更改为 E。
  • 索引=3。列表:子列表(L,Index-1)++“E”++列表:nthtail(索引,L)。
【解决方案2】:

使用列表时,所有元素通常具有相似的数据类型或含义。您很少看到像 ["John Doe","1970-01-01","London"] 这样的列表,而是 #person{name="John Doe",...} 甚至 {"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 更好,但也许问题仍然存在,如上所述或here

【讨论】:

  • 我编辑了问题以提供更多详细信息。我希望这有帮助。谢谢。
【解决方案3】:

虽然使用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,它将使用旧值调用并返回新值。在图书馆里,我可能两者都有。

【讨论】:

    【解决方案4】:
    L = [H,H,H,H,H].
    

    我想访问一个指定的元素,例如第三个元素并将其更改为 E。然后如果我要再次使用列表 L,它将是

    [H,H,E,H,H]
    

    做一个真正的挑剔者。在 Erlang 中,数据是持久的不可变的。一旦定义了L = ... 部分,L 就一成不变。你不能从那里改变它。您可以做的是创建一个新值并将其绑定到另一个变量L1,然后停止使用L。然后垃圾收集器将快速处理L 的工作并回收它使用的内存。

    因此,说再次使用L 会更改其内容有些错误,因为那是不可能的。

    另一点值得一提的是,如果您将列表用作数组,那么您可以使用数组(来自array 模块)或使用字典(来自dict 模块)。在您的案例中,它极大地提高了查找和更新的速度。但是,如果您最常做的是遍历列表以处理所有元素,那么列表可能会胜出。

    【讨论】:

      【解决方案5】:
      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]
      

      不漂亮,但我打赌效率​​很高。

      【讨论】:

      • fun(R, N, L) -> case lists:split(N-1, L) of {F, [_|T]} -> F ++ [R|T] end end(e,3,[h,h,h,h,h]) 是同样的替代方案,但效率很高,除非列表非常小,否则这些实现并不高效。
      • 哇,没有意识到列表:map/foldl 与列表相比太慢了:split 甚至列表:reverse
      【解决方案6】:

      如果您的列表由元组组成,您可以使用lists:keyreplace

      【讨论】:

        【解决方案7】:
        replace(List, Index, Element) ->
            replace(List, _Current=0, Index, Element, []).
                                                                   
        replace([], _, _, _, Acc) -> lists:reverse(Acc);
        replace([_H|T], Current, Index=Current, Element, Acc) ->
            replace(T, Current + 1, Index, Element, [Element|Acc]);
        replace([H|T], Current, Index, Element, Acc) ->
            replace(T, Current + 1, Index, Element, [H|Acc]).
        

        【讨论】:

        • 欢迎来到 SO!你能解释一下这对 OP 和未来的访问者是如何工作的吗?谢谢。
        猜你喜欢
        • 2018-04-10
        • 2021-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-10
        • 2021-07-07
        • 2021-06-26
        相关资源
        最近更新 更多