Reputation: 39
The Elixir help page for Enum.reduce/3
says that almost all of the Enum
functions can be implemented on top of Enum.reduce/3
. I am trying to implement Enum.all?/1
by writing a new function that uses Enum.reduce/3
. It works correctly most of the time but in some cases it returns an empty list.
In one case I have invoked the function passing a 1..7
range and a function testing that a value is less than 7
. In another case I passed the 1..7
range and a function testing that a value is less than 8
.
In the first case my all?/1
function correctly returns false as all values are not less than 7
.
In the second case I am expecting true, all values are less than 8
but instead get back an empty list.
Here is my implementation of the all?/1
function .....
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
The first test......
ListsRec5.all?(1..7, true, fn x -> x < 7 end)
false
The second test ....
ListsRec5.all?(1..7, true, fn x -> x < 8 end)
[]
I think the second test should return true
, not an empty list.
Upvotes: 1
Views: 1001
Reputation: 56
The issue here is that you are using incorrectly the accumulator passed to reduce/3
def all?(enumerable, fun \\fn x -> x end) do
enumerable
|> Enum.reduce(true, fn x, acc -> fun.(x) and acc end)
end
The arity of all?
function is 2 (not 3)
The initial value passed to the reduce function should be true
Upvotes: 0
Reputation: 48659
The second test .... ListsRec5.all?(1..7, true, fn x -> x < 8 end) []
I think the second test should return true, not an empty list.
Well, let's see:
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 []
[]
Yup, that's an empty list. I've read that:
and
requires boolean arguments and returns a boolean.
and
requires the first argument to be a boolean and returns a boolean.
The example above disproves both of those incompetent claims. So, lets ignore the Elixir writer's vain attempts to explain how and
works because apparently and
in Elixir is equivalent to andalso
in Erlang. So let's check the Erlang docs:
Expr1 andalso Expr2
Returns either the value of Expr1 (false) or the value of Expr2 (if Expr2 is evaluated).
So, if Expr1 is true, then andalso returns Expr2, otherwise andalso returns false, i.e. when Expr1 is false.
From Erlang/OTP R13A, Expr2 is no longer required to evaluate to a Boolean value.
That explains why you are getting an empty list:
~/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
Also notice here:
def all?(enumerable, acc, fun \\fn x -> x end) do
enumerable
|> Enum.reduce([], fn x, acc -> fun.(x) and acc end)
end
that the acc
variable in the all? def's parameter list is unused. The function should be defined like this:
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
Then you can call it like this:
~/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
Somewhere in the looping you will get acc=true and false
, which returns false
, and thereafter false and anything
will return false
. As a result, if the predicate function returns false
for any element in the enumerable, then the end result will be false
.
Upvotes: -1
Reputation: 6003
If you're reducing into a boolean (reduce won't be the best approach here, but it can be done), your accumulator is going to be a boolean, not a list, and each iteration will be a boolean representing "everything prior was true and this one is true".
Enum.reduce(enumerable, true, fn x, acc -> acc and fun.(x) end)
Upvotes: 2