Introduction to Mathematica Lists

- George Beck, 8/19/99

Introduction

My plan for this talk was to use the Help Browser for all the nifty Further Examples that so many developers have helped to write. That would save me from typing and was going to be the way to organize my talk. But things started happening and I only remembered to look at the browser every once in a while. As a consequence, I skipped some fundamental operations, certainly making this talk incomplete. If you want to learn about lists, though, I think that browsing through the Further Examples is still a pretty good way to do it.

Four problems

Our goal is to be able to construct the following by the end of today's talk. Keep these problems in mind as we go through the talk.

problem 1

{a, 0, a, 0, a, 0, a, 0, a}

problem 2

{{a, 0, 0, 0}, {0, a, 0, 0}, {0, 0, a, 0}, {0, 0, 0, a}} // 
      MatrixForm

What if one of the lists had one less element?

{{a, 0, 0, 0}, {0, 0, 0}, {0, 0, a, 0}, {0, 0, 0, a}} // 
      MatrixForm

Compare TableForm.

{{a, 0, 0, 0}, {0, 0, 0}, {0, 0, a, 0}, {0, 0, 0, a}} // 
      TableForm

problem 3

{{0, 0, 0, a}, {0, 0, a, 0}, {0, a, 0, 0}, {a, 0, 0, 0}} // 
      MatrixForm

problem 4

{{a, 0, 0, 0}, {b, a, 0, 0}, {0, b, a, 0}, {0, 0, b, a}} // 
      MatrixForm

What is a list?

Go to the Help Browser and under the Built-in Functions, find the entry for List. The Further Examples are at the bottom.

Lists collect objects. A list starts with a brace and ends with a brace. What better list to start with than the empty list?

{}

The entries (or components, or arguments, or parts) are separated by commas.

{a, b, c}

If you forget an argument, Mathematica calls the empty thing that should be there Null.

{a, , c}

Lists don't do much. They sit there and hold things. They are interesting because there are many functions that work on lists. That's what this talk is about.

Mathematica will do what it can to the entries inside the list, though.

{1 + 2, 2, 3}

Mathematica would add 1 + 2 anywhere, so List isn't responsible for that.

1 + 2

You can put numbers, functions (including List!), strings, or even another list into a list.

{5440, List, Reverse, Plot, "I am George", {a, b, c}}

Recall that % means the previous result. The quotes around a string get dropped in output, but you can see they are still there by using FullForm.

FullForm[%]

The FullForm of a list starts with List — that's the head of the expression — and the arguments are enclosed in square brackets. Notice that applying FullForm, we wrap the argument % with square brackets. That's how Mathematica functions are applied.

FullForm[{1, 23}]

Braces are used instead of the FullForm way because lists are so common. Other cases like that are sums, powers, and products.

a + 1

FullForm[1 + a]

FullForm[(x^2)]

FullForm[this is a multiplication]

I entered the power in (x^2) with Ctrl-^. You can enter it as InputForm with x^2, but the default output is in StandardForm.

x^2

What if you do FullForm on a FullForm?
That is not particularly interesting.

What is the relationship between FullForm, StandardForm, and TraditionalForm?
Sorry, that topic doesn't belong in this talk. We have a time crunch.

What are lists used for?

Lists allow you to get more than one result out of a function. For instance, you get all the solutions of an equation in a list.

Let's look up Solve. Type it in and hit F1 to go to the Help Browser.

Solve

Let's copy this solution to the quadratic equation we see there. You can get two possible answers.

{{x -> -b - (Sqrt[(b^2) - c])}, {x -> -b + (Sqrt[(b^2) - c])}}

Why so many braces?  Why isn't it simpler, like this?

{x -> -b - (Sqrt[(b^2) - c]), 
      x -> -b + (Sqrt[(b^2) - c])}

Well, if we had two variables to solve for, we'd have two sets of solutions. The list entries are the different sets of solutions. Each sublist has one entry per variable.

Solve[{2  x^2 + y == 10, x^2 - y == 2}, {x, y}]

List provides a way for Mathematica to provide multiple answers. Some other languages cannot provide multiple answers as conveniently.

Look at how lists are used to gather up the iterator notation for Table in the next section.

Building lists

Let's go back to the Help Browser to look at Range. Range is a fast way to build lists.

Range[12]

Range[120]

Range[1]

Aha! The empty list again.

Range[0]

Length counts the number of items in a list. Length[Range[n]] is n.

Length[Range[120]]

If you want to start at something other than 1, give the starting and ending values.

Range[4, 12]

The third argument is the step size. The next number would be 13, but it is bigger than 12 so it isn't put in.

Range[4, 12, 3]

This is copied from the Further Examples. It shows lists of lists can be staggered by iterating up to another iterator.

Table[Binomial[i, j], {i, 0, 8}, {j, 0, i}]

Notice how lists are used to group the iterator notation in Table. 

Except for the explicit iterator variable at the beginning of the list (i in the table here), the remaining entries are like the arguments of Range.

({{notation, start, end, step}, {{i, n}, 1, n, 1}, {{i, m, n}, m, n, 1}, {{i, m, n, s}, m, n, s}})

TableForm[Table[Binomial[i, j], {i, 0, 8}, {j, 0, i}]]

MatrixForm[Table[Binomial[i, j], {i, 0, 8}, {j, 0, i}]]

The solution to problem 2

Let's solve one of the problems from the top of this notebook.

{{a, 0, 0, 0}, {0, a, 0, 0}, {0, 0, a, 0}, {0, 0, 0, a}} // 
      TableForm

Let's skip Array, which is next in the Help Browser. We're looking at IdentityMatrix, famous as the analogue of the number 1 in linear algebra. IdentityMatrix puts a diagonal of ones into a square matrix of zeros. Multiplying by a gives as on the diagonal.

a IdentityMatrix[4] // MatrixForm

Be careful with MatrixForm; it can get in the way when applied as an afterthought with //.

i4 = a IdentityMatrix[4] // MatrixForm

i4 looks okay, but appearances are deceiving.

i4

We expect that when we add 1000 to a matrix, it will get added to all the entries.

a IdentityMatrix[4] + 1000 // MatrixForm

But MatrixForm is interfering with that, as you can see when we try it on i4.

i4 + 1000

FullForm[%]

Let's redefine i4 using parentheses for grouping.

(i4 = a IdentityMatrix[4]) // MatrixForm

That's better.

i4 + 1000 // MatrixForm

The reason 1000 gets added to each entry of a nested list (like the matrix here) is that first it gets added to each of the four parts. Then it goes down into the next level.

i4

1000 + {a, 0, 0, 0}

i4 + 1000

Listable versus Map

Arithmetic operations and numerical functions go down into the entries. They are Listable.

{1. , 2. , 3. } + 100

{1, 2, 3}^2

Sin[{1. , 2. , 3. }]

{Sin[1. ], Sin[2. ], Sin[3. ]}

Exp[{1. , 2. , 3. }]

{Exp[1. ], Exp[2. ], Exp[3. ]}

You can operate on lists as if they were numbers.

{1, 2, 3}^2 + 100 + 100000 {1, 2, 3}

Undefined functions, user-defined functions that aren't built up directly from Listable functions, and Mathematica built-in functions without the attribute Listable don't go down to hit the entries of a list.

f[{1. , 2. , 3. }]

Point[{1. , 2. , 3. }]

?Listable

Let's ask for information on Exp.

?Exp

I need a little more, so I add another question mark. See the first attribute?

?? Exp

I can force f to be Listable.

SetAttributes[f, Listable]

?? f

See?  f goes inside lists now.

f[{1. , 2. , 3. }]

Here's a more temporary way. g, like f was, is not Listable.

g[{1, 2, 3}]

Map makes g run through the list as if it were Listable. Map makes a function infiltrate a list, you might say.

Map[g, {1, 2, 3}]

But g needs Map to do it. g is still not Listable.

g[{1, 2, 3}]

What can we map?

Someone asked what happens when I map List onto something. It depends on what the something is.

It doesn't make much sense to map List onto a scalar. (A scalar is another word for a number.) Nothing happens because a number doesn't have entries.

FullForm[100]

Map[List, 100]

Mapping List onto a list is fine.

Map[List, {a, b, c}]

Usually, you map functions onto objects that have a structure. The structure doesn't have to be a list.

Map[Sin, SomeHead[a, b, c]]

You can't meaningfully Map an object like a number or a list {a,b,c}, but it works. It looks backwards, but 10[Sin] might have its uses.

Map[10, {a, b, c}]

Here we Map a function onto something that is not a list, namely a polynomial.

Map[f, x^2 + 2  x + 1]

Unfortunately, this fails when the polynomial has only one term. (We want f[2  x] and f[(x^2)] as it was when there were three terms.)

Map[f, 2  x]

Map[f, x^2]

Map has a nice Further Example that fixes this problem. The function defined there is called PlusMap. Take a look.

Join and DiagonalMatrix

Someone asked how to join up two lists. Use Join. Look up Union, too.

Join[{1, 2}, {3, 4, 5}]

DiagonalMatrix is similar to IdentityMatrix.

MatrixForm[{{a, 0, 0, 0}, {0, b, 0, 0}, {0, 0, c, 0}, {0, 0, 0, 
          d}}]

MatrixForm[DiagonalMatrix[{a, b, c, d}]]

The solution to problem 1, interrupted

We wanted to construct this without having to type it in.

{a, 0, a, 0, a, 0, a, 0, a}

Let's start with something else.

{a, a, a, a, a, a, a, a}

Here are two ways.

Table[a, {i, 12}]

Range[12]

1^Range[12]

a 1^Range[12]

This happened when I added a to the beginning of 1^Range[12] without adding a space. a1 is a variable.

a1^Range[12]

We'll come back to problem 1 after a short interruption.

Many simple functions

I want to show you a bunch of functions, so let's construct a 4 by 4 matrix. (I am skipping the next built-in function in the Help Browser, Part; please look it up.) Let's start with a square number of entries.

Range[16]

Partition is a way to get a matrix. Here's the matrix.

my16 = Partition[Range[16], 4]

% // TableForm

Examine the following sequence of list gymnastics carefully.

First[my16]

Last[my16]

Rest[my16] // TableForm

Drop[my16, 1] // TableForm

Take[my16, 2] // TableForm

Minuses mean work from the end.

Drop[my16, -1] // TableForm

Take[my16, -3] // TableForm

Select and Cases are a little more complicated; Cases uses patterns. Let's skip them, even though they are very useful.

Now let's move on to other List operations. One of my favorites is Reverse.

Reverse[Range[13]]

Reverse[my16] // TableForm

Recall the function Map.

Map[f, {a, b, c}]

Try to understand each of the following.

Map[Reverse, my16] // TableForm

Reverse[Map[Reverse, my16]] // TableForm

Partition[Reverse[Range[16]], 4] // TableForm

The solution to  problem 3

This solves problem 3.

a Reverse[IdentityMatrix[4]] // MatrixForm

Three solutions to problem 1

solution 1

Flatten is the opposite or inverse of Partition. It destroys structure by getting rid of braces.

Flatten[{{a, 0}, {a, 0}, {a, 0}}]

Table[{a, 0}, {4}]

Flatten[%]

solution 2

a 1^Range[12]

This uses substitution. We replace a by {a, 0} and then Flatten. (The slash-dot notation /. stands for ReplaceAll.)

a 1^Range[12] /. a -> {a, 0}

Flatten[%]

solution 3a

Here is a more mathematical solution.

(-1)^Range[12]

(-1)^Range[12] + 1

% /.  2 -> a

solution 3b

((-1)^Range[12] + 1)/2

a %

What is Apply?

Apply switches in the head of an expression, as if you were copying and
pasting. It "recapitates" or "reheads" an expression. It might have been called ReplaceHead. 

The Head of a list is List.

FullForm[{1, 2, 3}]

You don't see it without FullForm, but the head of a sum is Plus.

a + b + c

FullForm[a + b + c]

Here are heads you do see.

f[b, x]

Apply[newhead, oldhead[arguments]]

You can write the list and the sum showing their heads, but they disappear on evaluation.

List[a, b, c]

Plus[1, 2, 3]

This replaces the head Plus with the head List.

Apply[Plus, {1, 2, 3}]

This replaces the head List with the head Plus.

Apply[List, a + b + c]

This changes the head Plus from Power to Plus.

Apply[Plus, (x^2)]

Anonymous solutions to problem 4

More than a dozen different solutions were sent in following the talk. You can become a good beginner at Mathematica programming by studying them to understand how they work. If you don't understand a built-in function, look in the Further Examples under Built-in Functions. 

The problem was to construct this banded matrix.

{{b, 0, 0, 0}, {a, b, 0, 0}, {0, a, b, 0}, {0, 0, a, b}} // 
      MatrixForm

a student

The ReplacePart section seems awfully long and cumbersome — do you have a
better suggestion?

IdentityMatrix[4]

% /.  1 -> a

MatrixForm[%]

Flatten[%%]

ReplacePart[{a, 0, 0, 0, 0, a, 0, 0, 0, 0, a, 0, 0, 0, 0, a},  
      b,  {5, 10, 15}]

ReplacePart[{a, 0, 0, 0, 0, a, 0, 0, 0, 0, a, 0, 0, 0, 0, a},  b,  
      5]

ReplacePart[%,  b, 10]

ReplacePart[%, b, 15]

Partition[{a, 0, 0, 0, b, a, 0, 0, 0, b, a, 0, 0, 0, b, a},  
      4]

another student

mya  =  a IdentityMatrix[4]

myb  =  b IdentityMatrix[3]

myb  =  PadRight[myb,  {3, 4}]

myb  = Prepend[myb,  Table[0,  {i,  4}]]

mya  +  myb  // MatrixForm

a beginner

This solution seems a bit verbose.

Transpose[
      Delete[Transpose[
NestList[RotateRight, 
            PadRight[{a, b}, 5], 3]
], 1]]

another beginner

PadRight[{a, b}, 4, 0, #] & /@ Range[-1, 2]

an intern

solution 1

Table[
      ReplacePart[ReplacePart[{0, 0, 0, 0}, a, Fibonacci[i]], b, i], {i, 1, 
        4}]

solution 2

Reverse[
      Table[Which[x + y == 5, b, x + y == 4, a, 1 == 1, 0 ]  , {x, 
          1, 4}, {y, 1, 4}]]

an intermediate

first try

First replace 1 by {a,b} along the diagonal.

IdentityMatrix[4] /.  1 -> {a, b} // MatrixForm

Get rid of the list in each row.

Map[Flatten, %] // MatrixForm

Drop none of the rows, but drop the first column.

Drop[%, None, 1] // MatrixForm

Putting it together we get this.

Drop[Map[Flatten, IdentityMatrix[4] /.  1 -> {a, b}], None, 1] // 
      MatrixForm

a better way

Rotate an extended row over and over again by one position.

NestList[RotateRight, {a, b, 0, 0, 0}, 3] // MatrixForm

Get rid of the first column, in this case by taking all the rows and all but the first column.

Take[%, All, {2, 5}] // MatrixForm

an expert

Constructing out of what — what's given? I'll assume {a, b} is given.Why the asymmetry? Change '-2' below to '-1' for symmetry.

[There are b's on the main diagonal, a's on the subdiagonal. It is symmetric about the other diagonal. — George]

Partition[{a, b}, 4, 1, {-2, 1}, 0] // Reverse

Partition[{a, b}, 4, 1, {-1, 1}, 0] // Reverse

MatrixForm[%]

another expert

Hi George, I really enjoyed your talk, especially the idea of just looking
  at the "Further Examples" section in order not to get confused. :)

Come on! That's not an easy problem! Using list operations makes it even more complicated, as I would usually attack these matrix construction problems with If statements.

Anyway, I came up with this solution:

Take[Partition[Flatten[Table[{b, Table[0, {4 - 1}], a}, {4}]], 4], 
      4]

There are a couple of things I like about this solution: b and a (and 0) appear only once, and it can be used for any dimension by simply replacing the 4s by the dimension you want.

Take[Partition[Flatten[Table[{b, Table[0, {5 - 1}], a}, {5}]], 5], 
      5]

yet another expert

solution 1

Here's a newfangled way to do it:

Reverse@Partition[{a, b}, 4, 1, {3, 1}, 0]

solution 2 and 3

Here are two more methods using new-age functions ListCorrelate and PadLeft, and one using only old-fashioned functions. I've used the following definitions to make it clear what parameters are used where in the new age functions, so that things don't appear quite so magical, and so that the reader can see how these might generalize.

main = {a, b};
m = Length[main];
k = 4;

kernel = Table[1, {k}];

Reverse@
      ListCorrelate[kernel, main, {-m, 1}, 0, Times, List]

Table[PadLeft[main, k, 0, i], {i, k - 1, 0, -1}]

padded = Join[#, main, #] &@Table[0, {k}];

Table[
      Take[padded, {i, i + (k - 1)}], {i, k + m, 
        k + m - (k - 1), -1}]

If you want to see how ListCorrelate or ListConvolve lines up the the two lists in arguments 1 and 2, giving you a window view into the main list, then define these functions:

t[a_] := {MakeBoxes[
          a], "↕", "■"}

t[a_, b_] := {MakeBoxes[a], "↕", 
        StyleBox[MakeBoxes[b], Background -> GrayLevel[0.8]]}

p[columns__] := 
      DisplayForm@
        GridBox[Transpose[{columns}], GridFrame -> True, 
          ColumnLines -> True]

and then always specify them in arguments 5 and 6 (which forces you to use arguments 3 and 4, sorry). Do this in a front end and it'll help you visualize what's going on. It sure helped me.

ListCorrelate[kernel, main, {-m, 1}, 0, t, p]

[If you like this kind of visual explanation take a look at the Further Examples for ListConvolve and ListCorrelate. — George]

4

Here's a solution that views the lists as digits of a moving "window" to a ternary (base-3) number. We use base 3  because there are three distinct elements in all your lists: 0, a, and b. Represent 'a' by the ternary digit 1, and 'b' by the digit 2.

r = 3^^12000;

len = 4;

window = 3^(len - 1);

Table[
        Take[IntegerDigits[Mod[r, 3^i*window], 3, (len - 1) + i], 
          len], {i, 1, len}] /. {1 -> a, 2 -> b}

I'm sure this can be generalized to use base B if there are B distinct elements in the lists, and to take some sort of specifier of where to start and end, but I'm too tired right now and will have to look into this later if I have time. Also, I wouldn't be surprised if my code above can be made more succinct.

a developer

Map[Function[PadRight[{a, b}, 4, 0, #]], Range[-1, 2]]

just returned from vacation

Reverse[
      ListCorrelate[{0, 0, 0, 0}, {a, b}, {3, 1}, 0, Plus, List]]

another late submission

A simple solution would be to use RotateLeft to generate the off-diagonal component, but...

a IdentityMatrix[4]  +  
      b Map[RotateLeft, IdentityMatrix[4]]

i.e., we get an extraneous ``b'' at the (1,4) position of the matrix, since RotateLeft ``wraps around.''  What we really want is a ShiftLeft, that just drops elements of the list that are shifted off the end:

ShiftLeft[mm_]  :=  
      PadLeft[RotateLeft[PadLeft[mm, Length[mm] + 1]], Length[mm]]

(Yes, I read ahead ;-).  Let's test it:

ShiftLeft[{1, 2, 3, 4}]

As you can see, the ``1'' vanishes off the left edge, and the right is padded by zeroes.  This should do what we want:

a IdentityMatrix[4]  +  
      b Map[ShiftLeft, IdentityMatrix[4]]