【问题标题】:Elixir: Reduce list to map counting occurrences (pattern match a map key on another argument)Elixir:减少列表以映射计数出现(模式匹配另一个参数上的映射键)
【发布时间】:2017-08-17 20:39:58
【问题描述】:

我有一个可能包含重复项的列表。我想计算列表中每个项目有多少个实例。我的计划是:

list
|> Enum.reduce(%{}, fn
  item, %{item => count}=acc -> %{acc | item => count + 1}
  item, acc -> Map.put(acc, item, 1)
end)

但是,编译失败并出现错误illegal use of variable item inside map key match, maps can only match on existing variable by using ^item

所以我将第一个模式更改为item, %{^item => count}=acc。此时,错误变为unbound variable ^item

我不确定在这里做什么。我知道可以基于另一个参数对一个参数进行模式匹配(如fn a, a -> true 用于比较函数的一个头),但显然不是在这种情况下。我试过用警卫做,但Map.has_key?/2 不能放在警卫里。我在这里发现了很多关于在地图上进行匹配的一般问题,但当匹配的值来自另一个参数时,我不会这样做。

【问题讨论】:

  • 有什么理由不使用Map.update/4list |> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, &(&1 + 1)) end).
  • ... 因为我没有像我想象的那样彻底阅读文档,显然。请给出答案,以便我接受。

标签: dictionary count duplicates pattern-matching elixir


【解决方案1】:

修改 Map 中键的值并在它不存在时插入,这正是 Map.update/4 所做的。要计算频率,默认值为 1,更新 fn 只需将值加 1 (&(&1 + 1)):

iex(1)> [1, 2, :a, 2, :a, :b, :a] |>
...(1)> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, &(&1 + 1)) end)
%{1 => 1, 2 => 2, :a => 3, :b => 1}

【讨论】:

    【解决方案2】:

    嗯,在写问题的时候发现了这个。想我会分享它,但如果其他人有更清洁的解决方案,欢迎使用:

    我发现的最佳解决方案是......在内部对其中一个参数进行柯里化,以便它被绑定,纯粹是为了句法目的。

    list
    |> Enum.reduce(%{}, fn item, acc ->
        f = fn %{item => count}=acc -> %{acc | item => count + 1}
                acc when is_map(acc) -> Map.put(acc, item, 1)
            end
        f.(acc)
      end)
    

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 2018-11-14
      相关资源
      最近更新 更多