5

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?

ErJab
  • 6,056
  • 10
  • 42
  • 54

5 Answers5

8

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
  • 39,593
  • 22
  • 116
  • 167
  • 1
    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 Nov 30 '09 at 17:27
  • In Python community there is a saying "this solution isn't very *Pythonic* ". Is there an equivalent expression in the Erlang Community? ;-) – jldupont Nov 30 '09 at 17:36
  • 1
    Exceptions should be used to indicate exceptional conditions, not for flow control – Dónal Nov 30 '09 at 17:47
  • 2
    `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 Nov 30 '09 at 18:36
  • 3
    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 Nov 30 '09 at 19:02
7
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
  • 93,734
  • 56
  • 203
  • 318
  • 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 Nov 30 '09 at 15:04
  • My sentiment too: once you've got comfortable with Erlang, this sort of expressiveness does wonders. – jldupont Nov 30 '09 at 15:20
  • I wonder if this ever returns anything other than [] (not counting no matching function clause :o)) – Zed Nov 30 '09 at 17:26
  • @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 Nov 30 '09 at 17:33
  • Good solution, but it took me some time to understand. If you don't know about Erlang function overloading, reviewing http://erlang.org/doc/reference_manual/functions.html is a must. –  Oct 12 '15 at 06:40
3

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
  • 26,174
  • 5
  • 52
  • 73
  • I need to stop processing the list furthur when I encounter the 1, and not just skip over 1. – ErJab Nov 11 '10 at 19:27
  • @ErJab: So RTFM http://erlang.org/doc/man/lists.html#takewhile-2 It is not http://erlang.org/doc/man/lists.html#filter-2 How do you think its working? Continue processing rest of list just for fun? – Hynek -Pichi- Vychodil Nov 13 '10 at 08:18
1

I meet the same problem and solve it by this way:

-module(foreach_until).
-export([foreach_until/3]).

foreach_until(Action, L, Judge)     ->
    lists:reverse(foreach_until(Action, L, Judge, []))
    .

foreach_until(_, [], _, Result) ->
    Result
    ;

foreach_until(Action, [H | T], Judge, Result)   ->
    case Judge(H) of 
        true    -> Result;
        false   -> foreach_until(Action, T, Judge, [Action(H) | Result])
    end
    .

Below is an example explained how to use:

60> foreach_until:foreach_until(fun(X) -> X*X end, [1,2,3,4], fun(X)-> X >= 3 end).
[1,4]
blkworm
  • 11
  • 2
1

lists:all?

do_something(A) ->
   case A of 
      1 ->
         false;
      _ ->
         true
   end.

IsCompleted = lists:all(do_something(A), [2, 4, 5, 1, 2, 5]),

Will break out whenever do_something returns false and return the results in IsCompleted.

john nowlin
  • 135
  • 10