Erlang lists:index_of/2函数是什么?

19
我正在寻找一个Erlang库函数,可以返回列表中特定元素的索引。 因此,如果:
X = [10,30,50,70]
lists:index_of(30, X)
会返回1,等等。就像java.util.ListindexOf()方法一样。 在Erlang标准库中是否存在这样的方法?我尝试在lists模块中查找,但没有找到。还是我应该自己编写它?

5
如果您要按索引访问项目,那么最好查看数组模块。 - Christian
6个回答

23

你需要自己定义它,像这样:

index_of(Item, List) -> index_of(Item, List, 1).

index_of(_, [], _)  -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).

请注意,访问列表的第N个元素是O(N)的,因此经常通过索引访问列表的算法将比顺序迭代列表的算法效率低。


4
如果返回一个像not_found这样的原子而不是-1,会更符合Erlang的风格。这样,如果你忘记测试这种情况,你会迅速得到一个错误提示。 - Alexey Romanov
2
索引也可以从1开始而不是0。 - Zed
3
由于lists:nth(Index, List)将1视为第一个项目,因此需要注意。 - Christian

16

正如其他人所指出的,有更有效的方法来解决这个问题。但是如果你正在寻找一些快速的方法,这个对我有用:

string:str(List, [Element]).

5

其他解决方案(请注意,这些是基于索引1的):

index_of(Value, List) ->
   Map = lists:zip(List, lists:seq(1, length(List))),
   case lists:keyfind(Value, 1, Map) of
      {Value, Index} -> Index;
      false -> notfound
   end.

index_of(Value, List) ->
   Map = lists:zip(List, lists:seq(1, length(List))),
   case dict:find(Value, dict:from_list(Map)) of
      {ok, Index} -> Index;
      error -> notfound
   end.

当你传递给这些函数的列表足够长时,构造额外列表或字典的开销会变得过高。如果您可以避免每次搜索列表时都进行构造,并将列表保持在这种格式之外,则可以消除大部分开销。

使用字典将哈希列表中的值,并有助于将索引查找时间降至O(log N),因此在处理大型单键列表时最好使用它。

总的来说,您作为程序员需要根据自己的使用情况组织数据结构。我猜想缺少内置的“index_of”是为了鼓励这种考虑。如果您正在进行单键查找,那么请使用字典。如果您正在进行多键查找,请使用元组列表与lists:keyfind()等。如果您的列表非常庞大,则可能需要更复杂的解决方案。


0
这个函数在Erlang中非常不常见,这可能是它没有被包含在标准库中的原因。有经验的Erlang程序员都不需要它,并且不鼓励使用使用此函数的算法。当有人需要它时,可以为自己的目的编写,但这种非常罕见的情况不足以将其包含在stdlib中。正确设计数据结构而不是寻求此函数通常是更好的选择。在大多数情况下,需要此函数表示设计上存在错误。

2
作为一名有经验的 Erlang 程序员,你认为 lists:nth 和 lists:nthtail 函数怎么样? - Zed
2
那只是一个荒谬的说法。你完全不知道我需要这个函数做什么。 - Justin
2
有经验的 Erlang 程序员都不需要它。猜想你的“有经验的 Erlang 程序员”不太处理实际问题? - Justin
2
在这种情况下,您可以执行Sorted = lists:sort(lists:zip(Randoms, lists:seq(1, length(Randoms)))),然后通过lists:keyfind(Item, 1, Sorted)获取项目的索引。 - Zed
3
我猜只是你说话的方式不同。 - Justin
显示剩余4条评论

-1

我认为作者提出了一个有力的论点。以下是我在日志应用程序中的使用案例。目标是将错误的严重程度与对各种错误响应级别执行的操作进行检查。

get_index(A,L) ->
    get_index(A,L,1).
get_index(A,[A|_],N) ->
    N;
get_index(A,[_|T],N) ->
    get_index(A,T,N+1).

get_severity(A) ->
    Severity=[debug,info,warn,error],
    get_index(A,Severity).

我刚刚写了一个非常类似的方法;最终我使用了类似于 get_severity(debug) -> 1; get_severity(info) -> 2; 等等的方式 - 这样应该会更快一些,而且 Dialyzer 会注意到如果我传递了无效的内容。 - Seb
嘿,我的解决方案与被接受的解决方案基本相同,并且遵循Erlang的“让它失败”的哲学。我承认被接受的解决方案使用了更好的参数名称(以及没有找到返回值)。也许结果应该是{ok,Index},但我已经有点厌倦了必须用okay({ok,X}) -> X提取我想要的内容,然后当我没有放置okay({error,X}) -> throw(X)时,Dialyzer会抱怨。 - tony wallace

-1
以下函数返回列表中给定元素的索引列表。结果可用于获取列表中重复元素的第一个或最后一个出现的索引。
indices_of(Element, L) ->                                                                                                                                                          
    Indices = lists:zip(lists:seq(1,length(L)), L),                                                                                                                                
    [ I || {I, E} <- Indices, E == Element ].   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接