oreilly.comSafari Books Online.Conferences.


An Introduction to Erlang
Pages: 1, 2, 3, 4

List Manipulations

Erlang comes with a bunch of handy ways to iterate over a list and do something with it. We'll take a look at how to loop over each element and perform an action, how to filter a list based on a boolean function, and also how to transform a list into a new list by applying a function to it element by element. From there, we'll take a look at List Comprehensions, which provide a more concise way to do several of these operations at once.

Basic Lists Module Features

Coming from any language with iterators, it's nice to be able to avoid manually traversing lists with indices. Erlang is no exception and makes such a task quite simple:

1> Printer = fun(E) -> io:format("E: ~p~n",[E]) end.
2> lists:foreach(Printer,[1,2,3,4,5]).              
E: 1
E: 2
E: 3
E: 4
E: 5

Here we simply create a Fun that prints out the elements one by one, and we pass this and the list we want to iterate over to lists:foreach.

But what if we only wanted to print a subset of the list, such as the odd numbers only? This is where filters come in handy.

3> lists:filter(Odd,[1,2,3,4,5]).    
4> lists:foreach(Printer,lists:filter(Odd,[1,2,3,4,5])).
E: 1
E: 3
E: 5

Of course, this is somewhat inefficient because it iterates the list twice, but it gives you a general idea of how filters can be used to reduce a list based on a Boolean function.

Sometimes you'll want to transform a list based on a function that is run for each element, this is where lists:map comes in handy.

5> lists:map(Odd,[1,2,3,4,5]).                          
6> lists:map(fun(X) -> X + 1000 end,[1,2,3,4,5]).

This essentially says for each element X in the list, give me the result of f(X). Many tasks will involve mapping a list of data based on a series of transformations, and this feature comes in handy for that.

We'll now take a look at a very powerful and syntactically elegant approach to the same problems, called List Comprehensions. These can help simplify your code and make it easier for you to quickly filter and transform a list.

List Comprehensions

A list comprehension allows us to simultaneously transform our data and filter it. Here are a few examples:

1> L = [1,2,3,4,5].                            
2> [E +1 || E <-  L].                          
3> [E || E <-  L, E < 3].                      
4> [E + 1000 || E <-  L, E =/= 1, E < 3 orelse E > 4].

In each of these, you can see the general form: "f(E) for each E in L where the conditions are satisfied." The result of this is a filtered down and transformed list that doesn't require explicit creation of Funs or clever ways of constructing your code for efficient list processing.

List comprehensions are actually even smarter than what you see here, you can use a large range of predicates that help constrain your datasets. A simple example is shown here, but see the Erlang documentation for more details:

1> [E + 1 || E <- [1,"Kitten","Batman",5], is_integer(E) ].

You can actually also use the pattern for elements as a filter, as you can see here:

1> [ A || {A,B} <- [{a,b},{c,d},1,tomato,{e,f}] ].

All values that don't match the pattern are simply ignored in your list comprehension code.

Now that we've covered the basics of Erlang's elemental constructs as well as simple ways to traverse and manipulate lists, we're ready to take a look at a simple concurrent program.

Concurrency and Erlang

Though we've taken the express lane to get here, this article has covered all of the language constructs necessary to build simple concurrent programs. In a moment, we'll take a quick look at a simple chat program, which consists of two modules, a room, and users who log in to the room where they can communicate with others. Before we dive into those details however, we should talk about the basics behind Erlang's concurrency model.

Unlike state based languages which have shared memory and other messy issues which require things like mutex locks, Erlang's relatively strict functional model makes concurrency inherently more simple.

This is combined with a very pleasant way of handling events, which are interpreted in Erlang as messages in queue. Lightweight processes communicate with each other via messages, with all of the lower-level scheduling being done under the hood.

The most simple concurrent applications in Erlang simply spawn a process which runs in parallel to the other running processes. These processes then typically will enter a receive() loop, attempting to match patterns against messages that have been sent to the process.

A trivial example of this is a simple process which just prints out the messages it receives:


start() -> 
  spawn(fun() -> loop() end).

loop() ->
    Any ->
      io:format("~nI got the message: ~p~n",[Any]),

In this code, our start() function spawns a process which kicks off the loop() function. It then waits to receive any message, which it then prints out. After it has completed printing the message, it starts the loop again, allowing it to process further messages.

Let's fire up the shell, create a process, and send some messages to it:

1> c(trivial_process).
2> Pid = trivial_process:start().
3> Pid ! something.
I got the message: something

4> Pid ! [something,else].
I got the message: [something,else]

It's really this simple to spawn a process that listens for messages and responds accordingly. Of course, we're not putting any restrictions on who gets to send messages to the process or doing any error handling, but the simplicity of this code is still quite impressive.

Let's use these same basic ideas to take a look at and dissect my simple chat program, listed below:

Pages: 1, 2, 3, 4

Next Pagearrow

Sponsored by: