Project Euler (problem 2)

I’m just learning Elixir but find it really fun, nevertheless it’s easy to produce crappy code when there is no one to do some review. Thus i ask you nicely to tell me how can I improve it to be more readable, or maybe faster, better?

defmodule Two do
  def fibonacci_even_sum do
    Stream.unfold({1, 2}, fn({n, m}) -> if n > 4_000_000, do: nil, else: {n, {m, n + m}} end)
      |> Enum.reduce(0, fn(n, acc) -> if rem(n, 2) == 0, do: n + acc, else: 0 + acc end)
  end
end

IO.inspect Two.fibonacci_even_sum

I’ll be grateful for any review :slight_smile:

I almost forgot… original problem:

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
1 Like

I’m also just getting my feet wet with Elixir and I’ve been trying to approach problems like this as if I were going to be applying them to larger systems/had other team members looking at my code later.

Nice use of unfold! Lets pull it out into its own function.

defmodule Two do
  def fibonacci_even_sum do
    fibonacci_sequence
    |> Stream.take_while(&(&1 < 4_000_000))
    |> Enum.reduce(0, &sum_even/2)
  end

  def fibonacci_sequence do
    Stream.unfold({1,1}, fn {a,b} -> {a, {b, a + b}} end)
  end

  def sum_even(num, acc) do
    if rem(num, 2) == 0, do: acc + num, else: acc
  end
end

I’ve doubled the amount of lines your solution would take but it makes your intentions more obvious.
Piping into Stream.take_while allows us to turn your unfold into a more generally useful endless stream while only taking as many as we need.

I’m conflicted on pulling the reduce function out into its own function, I’m not really sure what the general best practice is there? I’d like to hear more from people on that.

Hope this helps!

2 Likes

If we’re going the Streaming route you may as well use Stream.filter.

def fib_even_sum(limit) do
  fibonacci_sequence
  |> Stream.take_while(&(&1 < limit))
  |> Stream.filter(&(rem(&1, 2) ==0))
  |> Enum.sum
end
4 Likes

Yeah, they are much more readable :slight_smile: So much more learning before me, but when i read how can my ugly code be transformed into something beautiful i think i’ll switch to Elixir in my Engineering thesis :smiley:

1 Like

I was looking for the Stream.take_while and i a bit confused how it work. I was putting :

a = [1,2,3,4,5,6,7,8,9,10,15,16,17,19,20]
and then
b = Stream.take_while(a,fn (a) -> a <10 end) but doesn’t return anything , but when i do

b |> Enum.sum , it return the correct value (45) .

Also look that enum and stream have a take_while function , what are the difference between this?

1 Like

Enum early operates on the input, streams do so lazily.

2 Likes

Streams are lazy by design. You need either an Enum call or a Stream.run at the end of the pipeline to actually cause it to execute.

2 Likes