【问题标题】:Why does my Enum.reduce based implementation of Enum.all? return an empty list?为什么我的 Enum.reduce 基于 Enum.all 的实现?返回一个空列表?
【发布时间】:2019-08-22 01:15:30
【问题描述】:

Enum.reduce/3 的 Elixir 帮助页面说,几乎所有的 Enum 函数都可以在 Enum.reduce/3 之上实现。我正在尝试通过编写一个使用Enum.reduce/3 的新函数来实现Enum.all?/1。它在大多数情况下都能正常工作,但在某些情况下它会返回一个空列表。

在一种情况下,我调用了传递1..7 范围的函数和一个测试值是否小于7 的函数。在另一种情况下,我通过了1..7 范围和一个函数测试值是否小于8

在第一种情况下,我的 all?/1 函数正确返回 false,因为所有值都不小于 7

在我期望的第二种情况下,所有值都小于8,而是返回一个空列表。

这是我对all?/1 函数的实现.....

defmodule ListsRec5 do

  def all?(enumerable, acc, fun \\fn x -> x end) do
    enumerable
      |> Enum.reduce([], fn x, acc -> fun.(x) and acc end)
  end

end

第一次测试……

ListsRec5.all?(1..7, true, fn x -> x < 7 end)
false

第二次测试....

ListsRec5.all?(1..7, true, fn x -> x < 8 end)
[]

我认为第二个测试应该返回true,而不是一个空列表。

【问题讨论】:

  • 为什么?你 anding true 有一个空数组,这将评估为空数组。 false and [] 短路所以你得到false

标签: elixir


【解决方案1】:

如果您要归约为布尔值(此处归约不是最好的方法,但可以做到),您的累加器将是一个布尔值,而不是一个列表,并且每次迭代都是一个布尔值表示“之前的一切都是真实的,而这一切都是真实的”。

Enum.reduce(enumerable, true, fn x, acc -> acc and fun.(x) end)

【讨论】:

    【解决方案2】:

    这里的问题是你错误地使用了传递给reduce/3的累加器

      def all?(enumerable, fun \\fn x -> x end) do
        enumerable
          |> Enum.reduce(true, fn x, acc -> fun.(x) and acc end)
      end
    
    

    all? 函数的元数是 2(不是 3)

    传递给reduce函数的初始值应该为真

    【讨论】:

      【解决方案3】:

      第二个测试....ListsRec5.all?(1..7, true, fn x -> x

      我认为第二个测试应该返回 true,而不是一个空列表。

      好吧,让我们看看:

      iex(3)> true and []  
      []
      iex(4)> true and []
      []
      iex(5)> true and []
      []
      iex(6)> true and []
      []
      iex(7)> true and []
      []
      iex(8)> true and []
      []
      iex(9)> true and []
      []
      

      是的,这是一个空列表。我读过:

      1. and 需要布尔参数并返回一个布尔值。

      2. and 要求第一个参数是布尔值并返回一个布尔值。

      上面的例子反驳了这两个不称职的说法。所以,让我们忽略 Elixir 作者徒劳地试图解释 and 是如何工作的,因为显然 Elixir 中的 and 等同于 Erlang 中的 andalso。所以让我们检查一下 Erlang docs:

       Expr1 andalso Expr2
      

      返回 Expr1 的值 (false) 或 Expr2 的值(如果计算了 Expr2)。

      因此,如果 Expr1 为真,则 andalso 返回 Expr2,否则,andalso 返回 false,即当 Expr1 为假时。

      从 Erlang/OTP R13A 开始,不再需要 Expr2 来评估 布尔值。

      这解释了为什么你得到一个空列表:

      ~/erlang_programs$ erl
      Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
      Eshell V9.3  (abort with ^G)
      
      1> true andalso false.
      false
      
      2> true andalso [].
      []
      
      3> true andalso 10.
      10
      
      4> false andalso "hello".
      false
      

      还要注意这里:

        def all?(enumerable, acc, fun \\fn x -> x end) do
          enumerable
          |> Enum.reduce([], fn x, acc -> fun.(x) and acc end)
        end
      

      acc 变量在所有? def 的参数列表未使用。函数应该这样定义:

         def all?(enumerable, acc, fun \\fn x -> x end) do
           enumerable
           |> Enum.reduce(acc, fn x, curr_acc -> fun.(x) and curr_acc end)
         end
      

      那么你可以这样称呼它:

      ~/elixir_programs$ iex a.ex
      Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
      Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
      
      iex(1)> A.all?(1..7, true, fn x -> x<8 end)
      true
      
      iex(2)> A.all?(1..7, true, fn x -> x<7 end)
      false
      

      在循环中的某处你会得到acc=true and false,它返回false,然后false and anything 将返回false。因此,如果谓词函数为枚举中的任何元素返回false,那么最终结果将是false

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-05
        • 2021-12-26
        • 1970-01-01
        • 2020-04-27
        • 2021-01-24
        相关资源
        最近更新 更多