views:

373

answers:

5

I'm writing an Erlang function which prints every even number up to a given parameter.

So far, I've written the function with guards like this:

printEven(I,N) when I < N ->
  if
    I rem 2 == 0 -> io:format("~p~n",[I]), printEven(I+1,N);
    I rem 2 == 1 -> printEven(I+1,N)
  end;
printEven(I,N) ->
   io:format("Done").

I'd really like to have the last case just exit automatically and not have to print anything in the function. I've tried just removing it, but then an error occurs since when the recursion is done, an error is thrown.

How can I do this? Is there something like a 'pass' or 'yield' keyword in erlang?

A: 

I believe the keyword is "ok"

eduffy
+3  A: 

just return an atom.

printEven(I,N) -> done.

should do it.

Ben Hughes
I don't want an atom to print though, just the numbers in the text.
samoz
an atom won't print. it will just stop the loop.
Ben Hughes
I should clarify, you should notice that the return value of printEven is never used, so you can have it return anything that doesn't include a recursive call.
Ben Hughes
+1  A: 

You could also combine the test for even in the guard clause. I also prefer the done atom trick - it shows in your code that it's the function clause that will stop the "recursion".

printEven(I, N) when I<N, I rem 2 == 0 ->
    io:format("~p is even~n", [I]),
    printEven(I+1, N);
printEven(I,N) when I<N ->
    printEven(I+1, N);
printEven(I,N) ->
    done.
Alan Moore
Is there a way to exit silently though? Without an atom?
samoz
No - all functions return a value. You're not testing the value of the function so I'm not sure why it matters for you?
Alan Moore
+7  A: 

OK, first define the function:

printEven(I,N) when I >= N -> ok;
printEven(I,N)             -> 
   if
    I rem 2 == 0 -> io:format("~p~n",[I]), printEven(I+1,N);
    I rem 2 == 1 -> printEven(I+1,N)
  end.

Erlang is a functional programming language and (by definition) functions 'have' a value so you are going to get 'something' back. By convention the thing you get back on completion of a function that you are using for side-effects is the atom 'ok', that's the best to use here.

You can 'silently discard' the return value if you want. You do that when you invoke the function by pattern matching to the 'don't care' variable (which is underscore):

_ = printEven(3,9),

or by calling the function without a pattern match:

printEven(3,9),

However, you are much better to always check return values by pattern matching when you invoke a function:

ok = printEven(3,9),

This is a a really good habit to get into because you will be using a lot of library functions that return error codes as you can see from their specs:

@spec funky(X) -> [ok | {error, bad_op} | {error, wig_out}]

If funky has side effects you want to know it has failed now by invoking it with a pattern match so it will crash here and now if funky fails:

ok = funky(99),

If you match it to '_' or ignore the return value it will crash 268 lines later when your mojo expects funky to have done his thang, and then it is much harder to find.

This is happy path programming which is the done thing in Erlang. "Let it crash" is the motto. If you are new to Erlang you will find this very disconcerting - like walking about naked. Don't worry embrace it, it is a good thing. It leads to lots of code 'not being written'.

(You should also get in the habit of putting the clause that ends the recursion as the top clause as shown here - it makes reading the code sooo much easier when you have a multi-clause function.)

Gordon Guthrie
Actually this code is not pretty enough, the second clause should be written as:printEven(I,N) -> _ = if I rem 2 == 0 -> io:format("~p~n",[I]); I rem 2 == 1 -> ok % print nothing end, printEven(I+1,N);Bear in mind that the if clause returns a value - you probably wouldn't match it in this case but I stuck in the match to remind you that it does :)
Gordon Guthrie
+2  A: 

Gordon: I usually put the terminating clause last, especially with lists. :-)

rvirding