tags:

views:

323

answers:

3

I have a list of elements in Erlang, and I'm using lists:foreach to traverse through the elements in the list. Is there a way to break out of this "foreach loop" in the middle of the traversal. For eg: suppose I want to stop traversing the list further if I encounter a '1' in the list [2, 4, 5, 1, 2, 5]. How do I do this?

+7  A: 
traverse(Liste) ->
 traverse(Liste, []).

traverse([], Acc) ->
 Acc;    

traverse([1|_], Acc) ->
 Acc;

traverse([H|T], Acc) ->
 % do something useful here maybe?
 traverse(T, Acc).

Of course this is very rough example.

jldupont
Yes, I often find that it is easier to understand how something like lists:foreach works and then write your own version to cater for special cases. As you can see above the number of lines of code is trivial. Browsing the lists module source code will yield a lot of insight into the Erlang language and its use...
Alan Moore
My sentiment too: once you've got comfortable with Erlang, this sort of expressiveness does wonders.
jldupont
I wonder if this ever returns anything other than [] (not counting no matching function clause :o))
Zed
@zed: of course it depends on what you put in the section "do something useful here maybe". "Acc" serves as "Accumulator". Come-on Zed, it's late ;-)
jldupont
+4  A: 

Another way to do it is to use throw and catch:

catch lists:foreach(
        fun(1) ->
                throw(found_one);
           (X) ->
                io:format("~p~n", [X])
        end,
        [2, 4, 5, 1, 2, 5]).

When run in the shell, this outputs:

2
4
5
found_one

EDIT: By popular demand, a more precise version that catches only what you want to catch:

try lists:foreach(
        fun(1) ->
                throw(found_one);
           (X) ->
                io:format("~p~n", [X])
        end,
        [2, 4, 5, 1, 2, 5])
catch
    throw:found_one ->
        found_one
end.
legoscia
Using exception throw is supposed to be "non local return" from function. Usage in this answer is valid throw usage and I don't understand why it is down-voted then.
Hynek -Pichi- Vychodil
In Python community there is a saying "this solution isn't very *Pythonic* ". Is there an equivalent expression in the Erlang Community? ;-)
jldupont
Exceptions should be used to indicate exceptional conditions, not for flow control
Don
`throw` is not an exception per se. And it IS supposed to be used for flow control. Please refer to the Erlang's reference guide
gleber
How do you handle "unexpected" exceptions? In this simple example what do you do if io:format throws something? You will have to be ready for "this kind of throw" and "that kind of throw". Just rewrite this to case catch, or try catch, and it will not be so simple anymore...
Zed
+1  A: 

There are many nice functions in lists module:

lists:foreach(fun(E) -> do_something(E) end,
    lists:takewhile(fun(E) -> E =/= 1 end, List)).

or more effective but less nice

lists:takewhile(fun(1) -> false;
                   (E) -> do_something(E), true
                end, List)
Hynek -Pichi- Vychodil