Project Euler (problem 1)

I just started reading about Elixir few days ago, an I’m fascinated. I programmed in Python, Java, and i work in Ruby. Never before i wrote in functional language.

I read most of tutorial, did elixir koans, and now i tried project euler

defmodule One do

  def sum_of_multiples(n) do
    n = n - 1
    if n == 0 do
      0
    else
      if rem(n, 3) == 0 do
        sum_of_multiples(n, n - 1)
      else
        if rem(n, 5) == 0 do
          sum_of_multiples(n, n - 1)
        else
          sum_of_multiples(0, n - 1)
        end
      end
    end
  end

  def sum_of_multiples(accumulator, n) do
    if n == 0 do
      accumulator
    else
      if rem(n, 3) == 0 do
        sum_of_multiples(accumulator + n, n - 1)
      else
        if rem(n, 5) == 0 do
          sum_of_multiples(accumulator + n, n - 1)
        else
          sum_of_multiples(accumulator, n - 1)
        end
      end
    end
  end

end

IO.puts One.sum_of_multiples(1000)

Above is my working code. How can i rewrite this to use pattern matching instead of this ugly if else?

I tried like this also:

Enum.map(1..999, fn n -> if rem(n, 3) == 0 or rem(n, 5) == 0, do: n, else, do: 0, end) |> Enum.sum |> IO.puts

But first of all there is syntax error i’m unable to correct on my own :cry:

I somehowo managed to do that:

Enum.map(1..999, fn n -> if(rem(n, 3) == 0 or rem(n, 5) == 0, do: n, else: 0) end) |> Enum.sum |> IO.puts

:wink:

But still don’t know how to rewrite those functions

1 Like

There isn’t really much you can do or win by pattern matching in this exercise.

The real thing that would make your code functional is to consider functions as data and start passing them around.

I’d try to start from scratch, having open the docs of Enum, Range and perhaps Stream but the last one can get substituted by Enum easily.

If you want to become spoiled by the exercise, just say a word, and I will link to my solution of a similar exercism exercise.

1 Like

I’ve come with

Enum.map(1..999, fn n -> if(rem(n, 3) == 0 or rem(n, 5) == 0, do: n, else: 0) end) |> Enum.sum |> IO.puts

And i would really like to see how you did it in functional way. At this point of my leaning Elixir path, I believe there is still not many room for challenge, and learning by reading others code will give me much more then hours of trying to do it on my own.

1 Like

That’s a solid functional solution. I think using Enum.filter instead of Enum.map is slightly more intention-revealing though.

2 Likes

Yeah, my solution is pretty much like that, but a little bit more generic, as the exercism variant is not about multiples of 3 and 5, but about the sum of the multiples of a list of numbers.

I think now you are ready to compare a bit: http://exercism.io/submissions/4095800ec24b497cb3c11cf81be7e216

1 Like

Awesome! So Insightful with the &1 Enum.any? and Stream.filter :smiley:

1 Like

Im also starting Project Euler, great solutions, i need to get familiar with enum and streams functions . Here is my aproach , basic recursion :stuck_out_tongue:

defmodule Euler1 do   

    def suma(numero, acc) when numero>0 do
        if rem(numero,5) == 0 or rem(numero,3)==0 do
            acc=acc+numero
        end
        suma(numero-1,acc)
    end

    def suma(numero,acc) do
        acc
    end
end
1 Like

Here my recursive approach.

defmodule Euler1 do
  def sum(0), do: 0
  def sum(n) when rem(n-1,3) == 0 or rem(n-1,5) == 0, do: n-1 + sum(n-1)
  def sum(n), do: sum(n-1)
end
3 Likes

What you are dooing here is called an imperative assigenment and is deprecated in elixir 1.3 and will be removed. With other words, in a couple of versions no assignment will leak scope anymore. (Maybe I’m confusing some version numbers here, but imperative assignments will vanish)

Prefered way to do it from then on is the following:

acc = if rem(n, 3) == 0 or rem(n, 5) == 0, do: acc + n, else: acc

Even better would be an approach that uses top level guards and pattern match similar to @danseid’s solution. But even that is not perfect because it is not tail call recursive…

2 Likes

@NobbZ you are right, this would be a TCOed solution:

defmodule Euler1 do
  def sum(n), do: sum(n-1, 0)
  def sum(0,acc), do: acc
  def sum(n, acc) when rem(n,3) == 0 or rem(n,5) == 0, do: sum(n-1, n+acc)
  def sum(n, acc), do: sum(n-1,acc)
end
4 Likes

:thumbsup: good job! I believe your solution might be improved further by making all variants that take two parameters (in other words: the variants that are only used internally) private. In Erlang/Elixir, I believe the naming convention for such a private variant that takes more arguments is to prefix it with do_, e.g. do_sum:

defmodule Euler1 do
  def sum(n), do: do_sum(n-1, 0)
  defp do_sum(0,acc), do: acc
  defp do_sum(n, acc) when rem(n,3) == 0 or rem(n,5) == 0, do: do_sum(n-1, n+acc)
  defp do_sum(n, acc), do: do_sum(n-1,acc)
end

Have a nice day, :smile:

~Qqwy

2 Likes

Since I am reading a lot of erlang in different closed source projects (and only a couple of open source) for an universitary project, I can tell, that I do see everything there, for me it does not seem as if there is any community idiom on the naming on such functions.

I’ve seen the following:

  • Prefix do_
  • Prefix _ only
  • Use a completely different prefix
  • Just use the same name as in the function without accumulator(s)
  • Use a complete different name

OK, I have to admit, that I’ve seen especially the last “convention” exclusively in environments where also languages are used which do only identify functions by name, not by arrity or type.

Personally I do prefer to use exactly the same name for functions that do the same, so map/2 would call map/3 in my code.

Most of the code that has been evaluated by me so far do use one style consistent (except some legacy parts noone wants to touch unless really really necessary ;)). And I do think beeing consistent through out the project is more important than trying to follow some community guide lines which do constantly change and update according to the languages evolution.

3 Likes

I thought function clauses that take a different amount of parameters where still ‘linked somehow’, but this is not true. Therefore, it is entirely possible to define sum/2 as private while keeping sum/1 public.

And that’s how you learn something new every day. Thank you! :smile:


(P.S: I’m actually happy I’ve been proven wrong about the convention of do_ prefixing, as I like the _ prefix one better.)

I completely agree :slight_smile:.

1 Like

With .. (range) and list comprehension:

defmodule Euler do
  def euler1(range, x, y) do
    (for n <- 1..range, rem(n, x) === 0 or rem(n, y) === 0, do: n) 
    |> Enum.sum
  end
end
3 Likes
Enum.to_list(1..1000) 
|> Enum.filter(fn(x) -> (rem(x, 3) == 0) || (rem(x,5) == 0 ) end) 
|> Enum.sum

or even better

multiples_of_three_and_five = fn(x) -> (rem(x, 3) == 0) || (rem(x,5) == 0 ) end

Enum.to_list(1..1000) 
|> Enum.filter(multiples_of_three_and_five) 
|> Enum.sum

3 Likes