3 Lists and Simple Data

3.1 Lists

Lists in Erlang and LFE are straight-forward; those coming from another programming language will not find anything surprising about them. Lists are generally good for storing and iterating over data that is of a similar type. There are other types one can use for more structured or complex data type combos.

You can create lists in LFE in the following ways:

> (list 1 3 9 27)
(1 3 9 27)
> '(1 3 9 27)
(1 3 9 27)
> (== '(1 3 9 27) (list 1 3 9 27))
true
> (=:= '(1 3 9 27) (list 1 3 9 27))
true
>

To get the length of a list, you'll need to use the length function from the erlang module:

> (length '(1 2 3 4 5 6 7))
7

Later, we will discuss Lisp-specific functions that have been implemented in LFE, but this is a good time to mention a few classic functions:

> (car '(1 2 3 4 5 6))
1
> (cdr '(1 2 3 4 5 6))
(2 3 4 5 6)
> (cadr '(1 2 3 4 5 6))
2
> (cddr '(1 2 3 4 5 6))
(3 4 5 6)
> (cons '(1 2 3) '(4 5 6))
((1 2 3) 4 5 6)
>

There is an Erlang module dedicated to handling lists that we can take advantage of:

> (lists:append '(1 2) '(3 4))
(1 2 3 4)
> (lists:append (list '(1 2) '(3 4) '(5 6)))
(1 2 3 4 5 6)
>

You can also use the ++ operator to combine two lists:

> (++ '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
>

Here's a map example that generates the same list we manually created above:

> (lists:map
    (lambda (x)
      (trunc
        (math:pow 3 x)))
    '(0 1 2 3))
(1 3 9 27)
>

Another one is filter, but before we use it, let's first define a predicate that returns true for even numbers:

> (set evenp
    (lambda (x)
      (== 0 (rem x 2))))
#Fun<lfe_eval.10.53503600>
>

Not let's try out filter with our new predicate:

> (lists:filter evenp '(1 2 3 4 5 6))
(2 4 6)
>

There are many, many more highly useful functions in the lists module -- be sure to give the docs a thorough reading, lest you miss something fun!

3.1.1 I/O Lists

There is another type of list that is used for such things as file and network operations; it's called an IoList. An IoList is a list whose elements are either * integers ranging from 0 to 255, * binaries, * other IoLists, or * a combination of these.

Here's an example for you:

> (list "hoopy" 42 #b("frood" 210) (list #b(42 84 126) 168 252))
("hoopy" 42 #B(102 114 111 111 100 210) (#B(42 84 126) 168 252))
>

You don't need to flatten IoLists; they get passed as they are to the various low-level functions that accept an IoList and Erlang will flatten them efficiently for you.

We saw an example of this in a previous section when we were playing with strings as binaries. We ended up calling a function that accepted an IoList as a parameter and this saved us from having to flatten the list of binaries ourselves. If you recall, data was a long string and the split function returned a list of binaries:

> (unicode:characters_to_list
    (binary:split data (binary "who really knows ")))
"There's a frood where his towel is."
>

3.2 Tuples

Tuples are the data melting pot for Erlang: you can combine any of Erlang's data types (including lists and other tuples) into a single composite data type. This comes in very handy with pattern matching, but in general, makes passing data around much easier.

Creating a tuple can be as simple as:

> (tuple)
#()
>

But perhaps more useful:

> (tuple 'odds "5 to 1 against")
#(odds "5 to 1 against")
>

You could also have done this:

> #(odds "5 to 1 against")
#(odds "5 to 1 against")
>

Here's a simple data structure:

> (set data
    (tuple
      '|things to see|
      (list "moons of Jaglan Beta"
            "beaches of Santraginus V"
            "desert world of Kakrafoon"
            "heavy river Moth")
      '|things to avoid|
      (list "Ravenous Bugblatter Beast of Traal"
            "small piece of fairy cake")))
#(|things to see|
  ("moons of Jaglan Beta"
   "beaches of Santraginus V"
   "desert world of Kakrafoon"
   "heavy river Moth")
  |things to avoid|
  ("Ravenous Bugblatter Beast of Traal" "small piece of fairy cake"))
>

Now let's poke around at our new data structure:

> (tuple_size data)
4
> (element 1 data)
|things to see|
> (element 3 data)
|things to avoid|
>

Using the erlang module's function is one way to get our tuple data, but you'll probably not use that as much as the next method we show you.

We're going to sneak ahead here a bit, and touch on patterns again; we'll explain in more detail in the actual section on patterns! For now, though, just know that extracting data from structures such as our tuple is very easy with patterns. Take a look:

> (set (tuple key1 val1 key2 val2) data)
#(|things to see|
  ("moons of Jaglan Beta"
   "beaches of Santraginus V"
   "desert world of Kakrafoon"
   "heavy river Moth")
  |things to avoid|
  ("Ravenous Bugblatter Beast of Traal" "small piece of fairy cake"))
>

To be clear: had we needed to do this in a function, we would have used let ;-)

Now we can references our data by the variables we bound when we extracted it with the pattern in our set call:

> key1
|things to see|
> key2
|things to avoid|
> val1
("moons of Jaglan Beta"
 "beaches of Santraginus V"
 "desert world of Kakrafoon"
 "heavy river Moth")
> val2
("Ravenous Bugblatter Beast of Traal" "small piece of fairy cake")
>