Ruby’s #each_with_index for Erlang

Ruby has a great method for enumerations called #each_with_index. It’s a handy way to iterate over a list of elements and and know where you’re at while doing it.

Erlang has a group of highly optimized list operations in the lists module (Try erl -man lists to see them all). We’re going to be using those to build our Erlang version of #each_with_index.

There are two methods from list we’re going to use: lists:seq and lists:zip. lists:seq simply returns a list of integers between a given range. For instance:

1> Seq = lists:seq(0, 9).
[0,1,2,3,4,5,6,7,8,9]

lists:zip takes two lists (of equal length) and returns a list of tuples. Like so:

2> lists:zip([a,b,c], [1,2,3]).
[{a,1},{b,2},{c,3}]

The last thing we need is a list comprehension, which is similar to each with a block in Ruby. For instance, in Ruby:

>> [:a, :b, :c].each { |element| puts element }
a
b
c
=> [:a, :b, :c]

In Erlang we would do the following:

3> [io:format("~p~n", [Element]) || Element <- [a,b,c]].
a
b
c
[ok,ok,ok]

There is one difference here: the Erlang list comprehension returns a new list which is the value of each call to io:format. In this way an Erlang list comprehension is closer to Ruby’s #collect rather than #each.

We now have all the pieces to build our Erlang version of #each_with_index.

4> EachWithIndex = fun(L, X) -> [
  X(Element, Index) || 
  {Element, Index} <- lists:zip(L, lists:seq(1, length(L)))
   ] end.

Whats going on here?

lists:zip(L, lists:seq(1, length(L)))

creates a list of tuples with each tuple containing an element of L and a companion integer.

[X(Element, Index) || 
  {Element, Index} <- lists:zip(L, lists:seq(1, length(L)))]

tells us to take that list of tuples and do a list comprehension on each of the elements, calling fun X in turn.

EachWithIndex = fun(L, X) -> ... end.

creates a fun (think lambda) and assigns it to EachWithIndex. You could just as easily create this as a regular function in a .erl file somewhere.

Now we can call our new EachWithIndex function by passing in a list and a fun:

5> EachWithIndex([a,b,c,d,e], fun(Element, Index) -> io:format("~p at ~p~n", [Element, Index]) end).
a at 1
b at 2
c at 3
d at 4
e at 5
[ok,ok,ok,ok,ok]
Share:
  • del.icio.us
  • Reddit
  • Technorati
  • Twitter
  • Facebook
  • Google Bookmarks
  • HackerNews
  • PDF
  • RSS
This entry was posted in erlang and tagged . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • http://ivan.bodunov.com Ivan

    In all list comprehensions you forgot to put ||

  • Nate Murray

    Re: Ivan. Interesting, they are being converted by something to in the markup. I’ll look into this and fix it.