Conditionally taking elements from a Stream

147 Views Asked by At

I've implemented the following function:

  def gaussian(center, height, width) do
    Stream.iterate(1, &(&1 + 1))
    |> Stream.map(fn (x) -> x - center end)
    |> Stream.map(fn (x) -> :math.pow(x, 2) end)
    |> Stream.map(fn (x) -> -x / (2 * :math.pow(width, 2))  end)
    |> Stream.map(fn (x) -> height * :math.exp(x) end)
    |> Stream.map(&Kernel.round/1)
    |> Stream.take_while(&(&1 > 0))
    |> Enum.to_list                                                            
  end

With the given args, an empty list is returned:

iex> gaussian(10, 10, 3)
[]

Removing the Stream.take_while/2

  def gaussian(center, height, width) do
    Stream.iterate(1, &(&1 + 1))
    |> Stream.map(fn (x) -> x - center end)
    |> Stream.map(fn (x) -> :math.pow(x, 2) end)
    |> Stream.map(fn (x) -> -x / (2 * :math.pow(width, 2))  end)
    |> Stream.map(fn (x) -> height * :math.exp(x) end)
    |> Stream.map(&Kernel.round/1)
    #|> Stream.take_while(&(&1 > 0))                                                   
    #|> Enum.to_list                                                                   
    |> Enum.take(20)
  end

gives this however:

iex> gaussian(10, 10, 3)
[0, 0, 1, 1, 2, 4, 6, 8, 9, 10, 9, 8, 6, 4, 2, 1, 1, 0, 0, 0]

Is there something wrong with my Stream.take_while/2 call, or am I missing something altogether here?

1

There are 1 best solutions below

3
On BEST ANSWER

Stream.take_while/2 stops evaluation on the first occurrence of the the function evaluating to false.

In your case, your function in:

|> Stream.take_while(&(&1 > 0))

with specified arguments like

gaussian(10, 10, 3)

in first iteration receives 0 thus it doesn't iterate further as your expression &1 > 0 evaluates to false.

You can check that yourself if you expand your code to something like:

|> Stream.take_while(fn (x) -> IO.inspect(x); x > 0 end)

Maybe its Stream.filter/2 you wanted to use?

Hope that helps you resolving your problem!