Try/Cond
Ulf Wiger (AL/EAB)
ulf.wiger@REDACTED
Thu Oct 23 13:41:37 CEST 2003
From: Francesco Cesarini [mailto:francesco@REDACTED]
>> There is no way of getting a non-local return from a function if you
>>do not use catch and throw.
>>
>Of course, as was mentioned yesterday, the main usage is when handling
>unreliable input in deeply nested functions, e.g. parsing. Other than
>that, it makes the code very hard to understand and debug. That, because
>the errors occur when they are used as a replacement to gotos, used
>across modules, and not with extreme care.
Personally, I practically never use catch and throw (well, I may use
catch every once in a while, but only related to exits, not throw.
Non-local return *is* possible without throw.
I've mentioned before a side project of mine, where I generate a recursive-
descent parser from a yecc grammar. There, I use a backtracking fun to
break out of a path as soon as I find that it doesn't match.
Example (simplified):
try_rule([Rule|Rs], Tokens, MatchF, BackF, Result, S) ->
NewMatchF =
fun(X, Ts1, S1) ->
%% so far so good, continue
try_rule(Rs, Ts1, Match, Back, set_var(X,Result), S1)
end,
(S#state.module):Pattern(Tokens, NewMatchF, BackF, Result, S);
try_rule([], Tokens, MatchF, BackF, Result, S) ->
%% no more rules to test -- we have a match.
MatchF(Result, Tokens, S).
match_rules([], Tokens, BackF, Result, S) ->
%% no branches matched -- backtrack.
Back();
...
match_rules([{RulePrefix, Branches}|Rules], Tokens, BackF, Result, S)... ->
NewBackF =
fun() ->
%% wrong track -- try the next rule
match_rule(Rules, Tokens, BackF, Result, S)
end,
NewMatchF =
fun(X, Ts1, S1) ->
%% a match -- continue down this path.
NewResult = set_var(X, Result),
match_rules(Branches, Ts1, NewBackF, NewResult, S1)
end,
(S#state.module):RulePrefix(Tokens, NewMatchF, NewBackF, S).
This has the (big) advantage of avoiding deep recursion in the
emulator. Logically, we're still doing the same thing, but this is
more efficient (except, it would seem, in Hipe, for unknown reasons).
Basically, for each step, you define two closures (or reuse the ones
that were passed to you): one to call when we know that we have a
match, and one to call when we know that we're on the wrong track.
One could of course argue that it's less intuitive than catch/throw.
/Uffe
More information about the erlang-questions
mailing list