【问题标题】:Erlang, finding the number of occurrences of a number in a listErlang,查找列表中数字的出现次数
【发布时间】:2020-03-27 14:34:57
【问题描述】:

我是 Erlang 的新手,正在尝试编写一个程序,它将像这样的数字列表 [1,5,4,5,3,2,2,8,11] 作为函数的输入参数。该函数应该返回一个记录数量的元组列表以及它在列表中出现的次数,如下所示。

[{1,1},{2,2},{3,1},{4,1},{5,1},{8,1},{11,1}]。

我有以下代码

list([]) ->
  [];
list([First | Rest])  ->  
  [{First,+1} | list(Rest)].

但我不明白我是如何进行计数操作的?谢谢

【问题讨论】:

    标签: list erlang tail-recursion


    【解决方案1】:

    maps:update_with/4:

    frequencies(List) ->
        frequencies(List, #{}).
    
    frequencies([], Freqs) ->
        maps:to_list(Freqs);
    frequencies([H|T], Freqs) ->
        Incrementer = fun(Count) -> Count+1 end,
        NewFreqs = maps:update_with(H, Incrementer, _Default=1, Freqs),
        frequencies(T, NewFreqs).
    

    在外壳中:

    1> a:frequencies([1,5,4,5,3,2,2,8,11]).
    [{1,1},{2,2},{3,1},{4,1},{5,2},{8,1},{11,1}]
    

    你介意一步一步解释这段代码是如何工作的

    这部分:

    frequencies(List) ->
        frequencies(List, #{}).
    

    让我们通过传递一个列表来调用frequencies/1 函数。然后将该列表与一个空映射一起转发到frequencies/2 函数以存储结果。

    这部分:

    frequencies([H|T], Freqs) ->
        Incrementer = fun(Count) -> Count+1 end,
        NewFreqs = maps:update_with(H, Incrementer, _Default=1, Freqs),
        frequencies(T, NewFreqs).
    

    使用模式匹配从列表中删除第一个数字H,然后调用函数maps:update_with/4,将第一个数字作为应该在地图中更新的Key。 maps:update_with/4 的其他参数是要更新的映射Freqs 和一个函数Incrementer,它接收与映射中的键关联的值作为参数。 Incrementer 函数的返回值是应该为映射中的键插入的新值。如果映射中不存在该键,则将具有_Default 值的新键输入映射中。

    maps:update_with/4 返回更新后的映射 NewFreqs,它作为参数传递给递归函数调用:

    frequencies(T, NewFreqs).
    

    第一个参数T 是一个包含剩余数字的列表。当 List 中的所有数字都被删除后,递归函数调用将是:

    frequencies([], #{ results in this map })
    

    该函数调用将匹配该函数子句:

    frequencies([], Freqs) ->
        maps:to_list(Freqs);
    

    maps:to_list/1 将映射转换为{Key, Value} 元组的列表。因为该函数子句的主体中没有递归函数调用,所以递归结束,返回元组列表。

    也许不同的变量名会使代码更容易理解:

    frequencies(List) ->
        frequencies(List, _ResultsMap=#{}).
    
    frequencies([Key|Keys], ResultsMap) ->
        Incrementer = fun(Value) -> Value+1 end,
        NewResultsMap = maps:update_with(Key, Incrementer, _Default=1, ResultsMap),
        frequencies(Keys, NewResultsMap);
    frequencies([], ResultsMap) ->
        maps:to_list(ResultsMap).
    

    【讨论】:

    • 谢谢。你介意一步一步解释这段代码是如何工作的吗?
    【解决方案2】:

    你可以只做一个单行,但把它分解成几个步骤:

    List  = [1,5,4,5,3,2,2,8,11].
    Keys  = lists:usort(List).
    Count = fun(V,L) -> length(lists:filter(fun(E) -> E == V end, L)) end.
    
    > lists:map(fun(K) -> { K, Count(K,List) } end, Keys).
    

    结果:

    [{1,1},{2,2},{3,1},{4,1},{5,2},{8,1},{11,1}]
    

    【讨论】:

      【解决方案3】:

      可能不是最快的(如果考虑速度,请使用映射而不是元组列表):

      count([]) ->
          [];
      count([H|T]) ->
          count2([H|T], []).
      
      count2([H|T], L) ->
          case lists:keyfind(H, 1, L) of
              {H, X} ->
                  L2 = lists:append(lists:delete({H, X}, L), [{H,X+1}]) ;
              false ->
                  L2 = lists:append(L, [{H, 1}])
          end,
          count2(T, L2);
      count2([], L) ->
          L.
      

      使用地图,代码会更简单:

      count([]) ->
          #{};
      count(L) ->
          count2(L, #{}).
      
      count2([H|T], M) ->
          Y = case maps:is_key(H, M) of
                  true  -> #{H := X} = M,
                           X + 1;
                  false -> 1
              end,
          count2(T, M#{H => Y});
      count2([], M) ->
          M.
      

      【讨论】:

        猜你喜欢
        • 2021-11-28
        • 1970-01-01
        • 2021-06-11
        • 1970-01-01
        • 1970-01-01
        • 2018-05-08
        • 2011-06-14
        • 1970-01-01
        • 2023-01-20
        相关资源
        最近更新 更多