在Elixir中引用管道值

时间:2017-01-15 02:05:16

标签: elixir

我想计算字符串中出现的字数。实现是有问题的,但让我们用它来证明我的问题:

  def count(sentence) do
    words = String.split(sentence)
    occurrences = Enum.map(words, fn w -> {w, Enum.count(words, &(&1 == w))} end)
    Map.new(occurrences)
  end

我想获得与上面相同的结果,但使用管道而不是中间结果变量:

def count(sentence) do
    sentence
    |> String.split
    |> Enum.map(fn w -> {w, Enum.count(???)} end)
    |> Map.new
  end

是否可以在Enum.count函数中引用管道值?或者我必须使用中间变量吗?

2 个答案:

答案 0 :(得分:9)

您可以在管道中放置一个匿名函数:

def count(sentence) do
  sentence
  |> String.split
  |> (fn words -> Enum.map(words, fn w -> {w, Enum.count(words, &(&1 == w))} end) end).()
  |> Map.new
end
iex(1)> count("foo bar baz foo")
%{"bar" => 1, "baz" => 1, "foo" => 2}

答案 1 :(得分:6)

虽然@ Dogbert的答案是完全正确的,但我会添加一个旁注:似乎只要你需要两次输出值,你就可能做错了。以上示例可能会重写为:

def count(sentence) do
  sentence
  |> String.split
  |> Enum.reduce(%{}, fn e, acc ->
    Map.put(acc, e, (Map.get(acc, e) || 0) + 1)
  end)
end

或以其他许多方式减少所涉及的循环量(以及整个函数的 big-O )。

现代更新:v1.8开始,Kernel.SpecialForms.for/1理解有reduce: keyword parameter,这使得上述内容更容易掌握:

def count(sentence) do
  for word <- String.split(sentence), reduce: %{} do
    %{^word => count} = acc -> %{acc | word => count + 1}
    acc -> Map.put(acc, word, 1)
  end
end