为什么我基于Enum.reduce的Enum.all实现?返回一个空列表?

时间:2019-08-12 21:12:32

标签: elixir

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

在一种情况下,我调用了传递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,而不是空列表。

3 个答案:

答案 0 :(得分:2)

如果您要简化为布尔值(在这里减少并不是最好的方法,但是可以做到),则累加器将是布尔值,而不是列表,并且每次迭代都将是一个布尔值表示“所有先验都是真实的,而这一切都是真实的。”

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

答案 1 :(得分:0)

这里的问题是您使用的累加器传递给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函数的初始值应为

答案 2 :(得分:-1)

  

第二个测试.... ListsRec5.all?(1..7,true,fn x-> x <8结尾)[]

     

我认为第二个测试应该返回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为true,则and还返回Expr2,否则,and也返回false,即Expr1为false时。

  

从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