Referencing piped value in Elixir

最后都变了- 提交于 2019-12-10 22:09:17

问题


I want to count the number of word-occurrences in a string. The implementation is questionable, but lets use it to demonstrate my problem:

  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

I would like to achieve the same result as above, but using pipes instead of intermediate result variables:

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

Is it possible to reference the piped in value in the Enum.count function? Or do i have to use an intermediate variable?


回答1:


You can put an anonymous function in the pipeline:

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}



回答2:


Though the @Dogbert’s answer is perfectly correct, I would add a sidenote: it seems that as soon as you need the pipe-out value twice, you’re likely doing it wrong. The above example might be rewritten as:

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

or in many other ways, that reduce an amount of loops involved (and, hence, the big-O of the whole function.)


Modern era update: starting with v1.8, Kernel.SpecialForms.for/1 comprehension has a reduce: keyword parameter, that makes the above even easier to grasp:

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



回答3:


Map.update achieves this in a pretty elegant way. I saw Jose Valim himself use this at Lambda Days 2017.

  def count_words(words) do
    words
    |> String.split()
    |> Enum.reduce(%{}, &count/2)
  end

 def count(word, map) do
  Map.update(map, word, 1, & &1 + 1)
 end

iex(1)> Word.count_words("I know we can yes we can can")
%{"I" => 1, "can" => 3, "know" => 1, "we" => 2, "yes" => 1}


来源:https://stackoverflow.com/questions/41657088/referencing-piped-value-in-elixir

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!