Old style vs. new style boolean expressions

Bjorn Gustavsson bjorn@REDACTED
Wed Oct 9 10:58:32 CEST 2002


James Hague <jamesh@REDACTED> writes:

> Here is a function written in classic Erlang style:
> 
> is_alphanum(N) when N >= $A, N =< $Z -> true;
> is_alphanum(N) when N >= $a, N =< $z -> true;
> is_alphanum(N) when N >= $0, N =< $9 -> true;
> is_alphanum($_) -> true;
> is_alphanum(_) -> false.

In this case, all relational tests are actual beam instructions.

> 
> This can also be written using boolean expression guards, which were
> introduced in (I think) R8:
> 
> is_alphanum(N) ->
> 	((N >= $A) and (N =< $Z)) or
> 	((N >= $a) and (N =< $z)) or
> 	((N >= $0) and (N =< $9)) or
> 	(N == $_).
> 

This is equivalent to:

is_alphanum(N) ->
	erlang:'or'(erlang:'and'(erlang:'>='(N, $A), erlang:'=<'(N, $Z)), ....)

where erlang:'or', erlang:'>=', and so on are real BIFs.

The compiler currently makes no attempt to optimize this code.

> This isn't as efficient as the first method, because the boolean expression
> in the second code snippet is always fully evaluated.  To fix this, use
> "andalso" and "orelse":
> 
> is_alphanum(N) ->
> 	((N >= $A) andalso (N =< $Z)) orelse
> 	((N >= $a) andalso (N =< $z)) orelse
> 	((N >= $0) andalso (N =< $9)) orelse
> 	(N == $_).


This example is equivalent to 

is_alphanum(N) ->
	case begin case erlang:'>='(N, $A) of
		false -> false;
		true -> erlang:'=<'(N, $Z)
		end
	     end of
		true -> true;
		false ->
		   case begin .....
	end.

and, again, the compiler makes no attempt to optimize it.

> 
> Is this just because boolean expressions are a relatively new addition, and
> so the R8 compiler doesn't deal with them well, or is it deeper than that?
> 

A future release of the compiler will probably have better optimization
(rewriting, if possible, to equivalent expressions using guards).

But in R8 and R9, you will get the best code if you place relational expressions
within guards.

'andalso' and 'orelse' are useful when one or both of the expressions are not
possible to write in a guard (such as function calls).

Here is a (slightly simplified) example from Wings:

is_all_inside_rect([P|Ps], Rect) ->
    is_inside_rect(P, Rect) andalso is_all_inside_rect(Ps, Rect);
is_all_inside_rect([], _Rect) -> true.

Here is_inside_rect/2 (not shown) is relatively expensive, so we want to terminate
with 'false' as soon as a call returns 'false'. Before R8, you either had to write
a nested case (efficient bug ugly)

is_all_inside_rect([P|Ps], Rect) ->
    case is_inside_rect(P, Rect) of
	false -> false;
	true -> is_all_inside_rect(Ps, Rect)
    end;
is_all_inside_rect([], _Rect) -> true.

or use 'and' instead of 'andalso' (clean but less efficient).

/Bjorn
-- 
Björn Gustavsson            Ericsson Utvecklings AB
bjorn@REDACTED      ÄT2/UAB/F/P
			    BOX 1505
+46 8 727 56 87 	    125 25 Älvsjö



More information about the erlang-questions mailing list