【问题标题】:Referencing piped value in Elixir在 Elixir 中引用管道值
【发布时间】:2019-05-20 12:17:15
【问题描述】:

我想计算一个字符串中单词出现的次数。实现是有问题的,但让我们用它来演示我的问题:

  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 函数中引用管道输入的值?还是我必须使用中间变量?

【问题讨论】:

    标签: elixir


    【解决方案1】:

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

    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】:

      虽然@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
      

      【讨论】:

      • (我没有在我的答案中提出更好的算法,因为看起来 OP 已经知道这一点并且只是使用该代码作为示例,因为他说“实现是有问题的,但让我们用它来证明我的问题”。)
      • 我的观点是:在任何情况下都可能不可避免地需要使用管道 var 两次。
      • 我的最终解决方案也使用了 reduce,但令我感兴趣的是 Dogbert 所解决的问题。话虽如此,我认为匿名函数方法令人困惑,绝对应该用类似您的解决方案的方法代替。
      【解决方案3】:

      Map.update 以一种非常优雅的方式实现了这一点。我在 2017 年的 Lambda Days 上看到 Jose Valim 自己使用了这个。

        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}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-02
        • 2021-03-08
        • 1970-01-01
        • 2020-04-10
        • 1970-01-01
        • 2017-05-25
        相关资源
        最近更新 更多