[erlang-questions] If Condition vs. Multiple Function Clauses

45 views
Skip to first unread message

Lars Herbach

unread,
Jun 14, 2013, 9:06:33 AM6/14/13
to erlang-q...@erlang.org
Hi List,
I'm currently working myself through the "Études for Erlang" book [1] and in exercise it wks's to write a recursive function, calculating the greatest common divisor for two numbers N and M. The suggested solution is a single gcd/2 function with an If condition and recursion:

gcd(M, N) -> 
    if M == N -> M;
       M > N -> gcd(M - N, N;
       true -> gcd(M, N - M)
    end.

I by myself took another way, working with multiple function clauses (did I name it right?):

gcd(M, N) when M == N ->
    M;
gcd(M, N) when M > N ->
    gcd(M - N, N);
gcd(M, N) ->
    gcd(M,  N - M).

Now I've got two questions about that: 
1) Is my solution still recursive, since I practically call different functions?
2) Are there any benefits in regards of efficiancy for the first solution?

Thanks,
Lars.


Jachym Holecek

unread,
Jun 14, 2013, 10:04:23 AM6/14/13
to Lars Herbach, erlang-q...@erlang.org
# Lars Herbach 2013-06-14:
> I'm currently working myself through the "?tudes for Erlang" book [1] and in
> exercise it wks's to write a recursive function, calculating the greatest
> common divisor for two numbers N and M. The suggested solution is a single
> gcd/2 function with an If condition and recursion:
>
> gcd(M, N) ->
> if M == N -> M;
> M > N -> gcd(M - N, N;
> true -> gcd(M, N - M)
> end.

Not entirely correct: what happens if you invoke 'gcd(-3, 9)' for example?
Same applies to the version below.

> I by myself took another way, working with multiple function clauses (did I
> name it right?):

Yes, that's correct naming. Note that multiple clauses are just a convenient
way to write things, 'gcd/2' is still just one function. It's instructive to
have a look at the result of 'erlc +to_core xxx.erl', that gives you Core
Erlang translation of your source -- a slightly more lowlevel representation
with less sugar on top and more uniformity.

> gcd(M, N) when M == N ->
> M;
> gcd(M, N) when M > N ->
> gcd(M - N, N);
> gcd(M, N) ->
> gcd(M, N - M).

I'd prefer this style myself as well, and from history of this list I think
many people would agree.

'If' expressions are quite useful, but in different use cases -- some number of
simple checks on a few unrelated values, when you feel splitting the checks to
another mini-function doesn't improve clarity (and/or you can't be bothered to
invent yet another function name ;-).

> Now I've got two questions about that:
> 1) Is my solution still recursive, since I practically call different
> functions?

Certainly recursive, tail-recursive even (doesn't matter in this case though,
you have as much stack space as you like -- only relevant consideration is
'bounded' vs. 'unbounded'). You're not calling different function, there's
only one 'gcd/2' in your code with a case analysis in it -- just like in
the first case.

At this level ("what does my code compile down to") the result of running
'erlc +to_asm xxx.erl' is useful -- that gives you BEAM assembly dump, quite
readable even if you don't exactly know what some/most instructions mean.

> 2) Are there any benefits in regards of efficiancy for the first solution?

No, at least in this simple case they compile down to almost the same ASM,
I'd expect that to be true pretty much all the time.

Recommended to optimize for clarity and simplicity. In the systems Erlang
is typically used for efficiency comes from entirely different places than
messing about with individual lines, expressions or functions. Unless you
have a loadtest proving the contrary for your particular project ;-), and
even then optimization wouldn't probably take more than an afternoon, so
no reason to worry. Focusing on more global dataflows, load distribution,
points of contention, safety of various internal protocols, load mitigation,
fault isolation + propagation etc. is better use of your time, but also a
bit of an advanced topic -- not sure existing literature covers it much?

(Recent discussion between Max Lapshin and Tim Watson illustrates the
really important considerations very well, for instance. There were
many excellent posts by others as well over time -- sorry, couldn't
possibly excavate all the links. :-/)

But for completeness -- Here's Erlang efficiency guide:

http://www.erlang.org/doc/efficiency_guide/introduction.html

HTH,
-- Jachym
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Lars Herbach

unread,
Jun 14, 2013, 3:32:49 PM6/14/13
to erlang-q...@erlang.org
Thank you very much! I'm currently diving into Erlang, coming from Python and C and you really helped me getting a feeling for the language. I BTW really enjoy the atmosphere and tone in this mailing list, I know much worse. Thanks for that.

-Lars

On Jun 14, 2013, at 16:04, Jachym Holecek <fr...@circlewave.net> wrote

Steve Davis

unread,
Jun 14, 2013, 8:40:06 PM6/14/13
to erlang-pr...@googlegroups.com, erlang-q...@erlang.org
I strongly (personally) dislike use of "if" syntax, but only because of the counterintuitive "true" evaluation.

What if the language were to return the atom "else" instead of "true"?

I doubt that would be considered "better" but it would sure make things more readable...

/s

Mahesh Paolini-Subramanya

unread,
Jun 15, 2013, 4:43:00 AM6/15/13
to Steve Davis, erlang-pr...@googlegroups.com, erlang-q...@erlang.org
Via LYSE and ROK - and with the forlorn hope that this doesn't start up again :-)  --> 

It may be more FAMILIAR, but that doesn't mean 'else' is a good thing. I know that writing '; true ->' is a very easy way to get 'else' in Erlang, but we have a couple of decades of psychology-of-programming results to show that it's a bad idea. I have started to replace:

                          by
if X > Y -> a() if X > Y  -> a()
; true  -> b() ; X =< Y -> b()
end     end

if X > Y -> a() if X > Y -> a()
; X < Y -> b() ; X < Y -> b()
; true  -> c() ; X ==Y -> c()
end end


which I find mildly annoying when _writing_ the code but enormously helpful when _reading_ it.

Speaking for myself, I've pretty much gotten to the point where I don't notice it.  
Heck, I barely notice syntax anymore.  About the best analogy I can come up with is spoken languages (adjectives after nouns in Italian? The Horror! The Horror!).  It may seem strange at first, but you don't even notice it after a few days…

Cheers


That Tall Bald Indian Guy...
Google+  | Blog   | Twitter  | LinkedIn

_______________________________________________

Anthony Ramine

unread,
Jun 15, 2013, 4:51:12 AM6/15/13
to Steve Davis, erlang-questions@erlang.org Questions
Failed to reply to all…

--
Anthony Ramine

Le 15 juin 2013 à 10:29, Anthony Ramine a écrit :

> Hello Steve,
>
> No thanks.
>
> If something is to be done, it should be to allow a literal 'otherwise' atom in the last 'if' clause pattern. In Haskell, otherwise is an alias of true just for that reason.
>
> Regards,
>
> --
> Anthony Ramine

Steve Davis

unread,
Jun 15, 2013, 9:30:10 AM6/15/13
to Erik Søe Sørensen, Erlang Questions
Yep, that would be a better, more consistent way to go...

if  M == N -> a();
    M > N -> b();
    _ -> c()
end.

:-) ty Erik

/s

On Jun 15, 2013, at 2:42 AM, Erik Søe Sørensen <eri...@gmail.com> wrote:

Me too. One might even consider using "_" for such a catch-all.
(A reason in favour of that, apart from the syntactic similarity with "case"-catch-alls: 'else' is a valid expression like 'true', '_' isn't and therefore doesn't confuse by the apparent overlap.)

J David Eisenberg

unread,
Jun 15, 2013, 10:18:13 AM6/15/13
to erlang-q...@erlang.org
>
> Message: 8
> Date: Fri, 14 Jun 2013 13:06:33 +0000
> From: Lars Herbach <la...@freistil.cz>
> To: "erlang-q...@erlang.org" <erlang-q...@erlang.org>
> Subject: [erlang-questions] If Condition vs. Multiple Function Clauses
> Message-ID: <EF118D9F-6346-4CDB...@freistil.cz>
> Content-Type: text/plain; charset="iso-8859-1"
>
> Hi List,
> I'm currently working myself through the "?tudes for Erlang" book [1] and in exercise it wks's to write a recursive function, calculating the greatest common divisor for two numbers N and M. The suggested solution is a single gcd/2 function with an If condition and recursion:

>
> gcd(M, N) ->
> if M == N -> M;
> M > N -> gcd(M - N, N;
> true -> gcd(M, N - M)
> end.
>
> I by myself took another way, working with multiple function clauses (did I name it right?):
>
> gcd(M, N) when M == N ->
> M;
> gcd(M, N) when M > N ->
> gcd(M - N, N);
> gcd(M, N) ->
> gcd(M, N - M).
>

You're right. Your solution is much more in the spirit of Erlang. I
wrote the étude with "if" because I wanted to have an example of its
use somewhere in the book. I will update the book to put in your
solution as an alternate answer, with some words about using guards in
preference to "if"..

o...@cs.otago.ac.nz

unread,
Jun 15, 2013, 8:33:16 AM6/15/13
to Steve Davis, erlang-pr...@googlegroups.com, erlang-q...@erlang.org
> I strongly (personally) dislike use of "if" syntax, but only because of
> the
> counterintuitive "true" evaluation.

What's counterintuitive about it? It's instantly familiar
to any Lisp programmer (except for being spelled 'true' instead
of 'T') or to any Prolog programmer.
>
> What if the language were to return the atom "else" instead of "true"?

Now that _would_ be unintuitive. In any case, 'if' has no truck
with values of any kind, it is driven by guards that succeed or
fail, not by expressions that have values.


>> gcd(M, N) ->
>> if M == N -> M;
>> M > N -> gcd(M - N, N;
>> true -> gcd(M, N - M)
>> end.

That is indeed a common Erlang style, but in a language
that uses both commas and semicolons so heavily, and
especially in a language that is lacking an 'elif' or 'else'
keyword, I find it advisible to put semicolons in a place
where commas never go, namely at the beginning of lines.
It is also easier to read if the arrows are aligned.

gcd(M, N) ->
if M > N -> gcd(M - N, N)
; N < M -> gcd(M, N - M)
; true -> M
end.

>>
>> I by myself took another way, working with multiple function clauses
>> (did I name it right?):
>>
>> gcd(M, N) when M == N ->
>> M;
>> gcd(M, N) when M > N ->
>> gcd(M - N, N);
>> gcd(M, N) ->
>> gcd(M, N - M).
>>
>> Now I've got two questions about that:
>> 1) Is my solution still recursive, since I practically call different
>> functions?

Yes your solution is still recursive.
In all essentials, it is the *same* solution.
No, you are *NOT* calling different functions.
There is only one gcd/2 function, and you are
calling *it* (not *them*).
In fact you *can't* call a clause.

>> 2) Are there any benefits in regards of efficiency for the first
>> solution?

If you really want to know, measure them.
But it would not be surprising if the compiler
generated essentially the same code.
Anyone who cared about efficiency that much would
be using a M rem N rather than M - N, or would be
using a binary gcd.

First you write something that is obviously
correct. Then you test it and fix the mistakes.
Then you can measure it, and consider a rewrite
if it is too slow.

Richard A. O'Keefe

unread,
Jun 16, 2013, 7:19:31 PM6/16/13
to Steve Davis, Erlang Questions

On 16/06/2013, at 1:30 AM, Steve Davis wrote:

> Yep, that would be a better, more consistent way to go...
>
> if M == N -> a();
> M > N -> b();
> _ -> c()
> end.

Let's see, this says
"Should the test M == N succeed, compute a().
Otherwise, should the test M >N succeed, compute b().
Otherwise, take a new variable that has never been given
any value whatsoever; should that variable, contrary to
all sane expectation, somehow magically turn out to be
bound to a succeeding test, compute c()."

Just how is the "_" supposed to get a value here?

By the way, anyone who is really suffering from the lack of 'else'
can just
-define(else, true).
and then
if M < N -> c()
; M > N -> b()
; ?else -> a()
end
and be done with it, although putting M == N there instead would
make it more obvious to one's readers, should one _have_ readers.

Steve Davis

unread,
Jun 17, 2013, 9:06:29 AM6/17/13
to Richard A. O'Keefe, Erlang Questions
Hi Richard,

Yep, you changed my mind (again). I wasn't thinking about this correctly.

Thanks very much for your replies,

Steve
Reply all
Reply to author
Forward
0 new messages