An Introduction To Programming With Mathematica PDF
An Introduction To Programming With Mathematica PDF
e
n
v
n
4 Functional programming 85
4.3 Iterating functions
A commonly performed task in computer science and mathematics is to repeatedly apply a
function to some expression. Iterating functions has a long and rich tradition in the history
of computing. Perhaps the most famous example is Newtons method for root finding.
Chaos theory rests on studying how iterated functions behave under small perturbations of
their initial conditions or starting values. In this section, we will introduce several func-
tions available in Mathematica for function iteration. In later chapters we will apply these
and other programming constructs to look at some applications of iteration, including
Newtons method.
The Nest function is used to iterate functions. Here, g is iterated (or applied to a)
four times.
In[1]:= Nest g, a, 4
Out[1]= g g g g a
The NestList function displays all of the intermediate values of the Nest operation.
In[2]:= NestList g, a, 4
Out[2]= a, g a , g g a , g g g a , g g g g a
Using a starting value of 0.85, this generates a list of ten iterates of the Cos function.
In[3]:= NestList Cos, 0.85, 10
Out[3]= 0.85, 0.659983, 0.790003, 0.703843, 0.76236, 0.723208,
0.749687, 0.731902, 0.743904, 0.73583, 0.741274
The list elements above are the values of 0.85, Cos[0.85], Cos[Cos[0.85]], and so
on.
In[4]:= 0.85, Cos 0.85 , Cos Cos 0.85 , Cos Cos Cos 0.85
Out[4]= 0.85, 0.659983, 0.790003, 0.703843
In fact, the iterates of the cosine function tend towards a fixed point which can be
obtained with FixedPoint. This function is particularly useful when you do not know
how many iterations to perform on a function whose iterations eventually settle down.
In[5]:= FixedPoint Cos, 0.85
Out[5]= 0.739085
Whereas Nest and NestList operate on functions of one variable, Fold and
FoldList generalize this notion by iterating a function of two arguments. In the follow-
ing example, the function f is first applied to a starting value x and the first element from a
86 An Introduction to Programming with Mathematica
list, then this result is used as the first argument of the next iteration, with the second
argument coming from the second element in the list, and so on.
In[6]:= Fold f, x, a, b, c
Out[6]= f f f x, a , b , c
If FoldList is used, then you will see all of the intermediate results of the Fold
operation.
In[7]:= FoldList f, x, a, b, c
Out[7]= x, f x, a , f f x, a , b , f f f x, a , b , c
It is easy to see what is going on with the FoldList function by working with an arith-
metic operator. This generates running sums.
In[8]:= FoldList Plus, 0, a, b, c, d
Out[8]= 0, a, a b, a b c, a b c d
In[9]:= FoldList Plus, 0, 1, 2, 3, 4, 5
Out[9]= 0, 1, 3, 6, 10, 15
Exercises
1. Determine the locations after each step of a ten-step one-dimensional random walk.
(Recall that you have already generated the step directions in Exercise 3 at the end of
Section 3.2.)
2. Create a list of the step locations of a ten-step random walk on a square lattice.
3. Using Fold, create a function fac[n] that takes in an integer n as argument and
returns the factorial of n; that is, n n 1 n 2 3 2 1.
4 Functional programming 87
4.4 Programs as functions
A computer program is a set of instructions (a recipe) for carrying out a computation.
When a program is evaluated with appropriate inputs, the computation is performed and
the result is returned. In this sense, a program is a mathematical function and the inputs to
a program are the arguments of the function. Executing a program is equivalent to apply-
ing a function to its arguments or, as it is often referred, making a function call.
User-defined functions
While there are a great many built-in functions in Mathematica that can be used to carry
out computations, we invariably find ourselves needing customized functions. For exam-
ple, once we have written a program to compute some values for some particular inputs,
we might want to perform the same set of operations on different inputs. We would
therefore like to create our own user-defined functions that we could then apply in the same
way as we call a built-in function by entering the function name and specific argument
values. We will start with the proper syntax (or grammar) to use when writing a function
definition.
The function definition looks very much like a mathematical equation: a left-hand
side and a right-hand side separated by a colon-equal sign.
name[arg
1
_,arg
2
_,,arg
n
_]:= body
The left-hand side starts with a symbol. This symbol is referred to as the function
name (or sometimes just as the function, as in the sine function). The function name is
followed by a set of square brackets, inside of which are a sequence of symbols ending with
blanks. These symbols are referred to as the function argument names, or just the function
arguments.
The right-hand side of a user-defined function definition is called the body of the
function. The body can be either a single expression (a one-liner), or a series of expressions
(a compound function), both of which will be discussed in detail shortly. Argument names
from the left-hand side appear on the right-hand side without blanks. Basically, the right-
hand side is a formula stating what computations are to be done when the function is
called with specific values of the arguments.
When a user-defined function is defined with a delayed assignment (:=), nothing is
returned. Thereafter, calling the function by entering the left-hand side of the function
definition with specific values of the arguments causes the body of the function to be
88 An Introduction to Programming with Mathematica
computed with the specific argument values substituted where the argument names occur.
In other words, when using delayed assignments, the body of your function is only evalu-
ated when the function is called, not when it is first defined.
A simple example of a user-defined function is square which squares a value (it is a
good idea to use a function name that indicates the purpose of the function).
In[1]:= square x_ : x
2
After entering a function definition, you call the function in the same way that a
built-in function is applied to an argument.
In[2]:= square 5
Out[2]= 25
Building up programs
The ability to use the output of one function as the input of another is one of the keys to
functional programming. A mathematician would call this composition of functions. In
Mathematica, this sequential application of several functions is known as a nested function
call. Nested function calls are not limited to using a single function repeatedly, such as with
the built-in Nest and Fold functions.
In[3]:= Cos Sin Tan 4.0
Out[3]= 0.609053
To see the above computation more clearly, we can step through the computation.
In[4]:= Tan 4.0
Out[4]= 1.15782
In[5]:= Sin %
Out[5]= 0.915931
In[6]:= Cos %
Out[6]= 0.609053
Wrapping the Trace function around the computation lets us see all of the intermediate
expressions that are used in this evaluation.
In[7]:= Trace Cos Sin Tan 4.0
Out[7]= Tan 4. , 1.15782 , Sin 1.15782 , 0.915931 ,
Cos 0.915931 , 0.609053
4 Functional programming 89
You can read nested functions in much the same way that they are created, starting
with the innermost functions and working towards the outermost functions. For example,
the following expression determines whether all of the elements in a list are even numbers.
In[8]:= Apply And, Map EvenQ, 2, 4, 6, 7, 8
Out[8]= False
Let us step through the computation much the same as Mathematica does, from the
inside out.
1. Map the predicate EvenQ to every element in the list {2,4,6,7,8}.
In[9]:= Map EvenQ, 2, 4, 6, 7, 8
Out[9]= True, True, True, False, True
2. Apply the logical function And to the result of the previous step.
In[10]:= Apply And, %
Out[10]= False
Finally, here is a definition that can be used on arbitrary lists.
In[11]:= setEvenQ lis_ : Apply And, Map EvenQ, lis
In[12]:= setEvenQ 11, 5, 1, 18, 16, 6, 17, 6
Out[12]= False
Another, more complicated, example returns the elements in a list of positive num-
bers that are bigger than all of the preceding numbers in the list.
In[13]:= Union Rest FoldList Max, 0, 3, 1, 6, 5, 4, 8, 7
Out[13]= 3, 6, 8
The Trace of the function call shows the intermediate steps of the computation.
In[14]:= Trace Union Rest FoldList Max, 0, 3, 1, 6, 5, 4, 8, 7
Out[14]= FoldList Max, 0, 3, 1, 6, 5, 4, 8, 7 ,
Max 0, 3 , 3 , Max 3, 1 , Max 1, 3 , 3 ,
Max 3, 6 , 6 , Max 6, 5 , Max 5, 6 , 6 ,
Max 6, 4 , Max 4, 6 , 6 , Max 6, 8 , 8 ,
Max 8, 7 , Max 7, 8 , 8 , 0, 3, 3, 6, 6, 6, 8, 8 ,
Rest 0, 3, 3, 6, 6, 6, 8, 8 , 3, 3, 6, 6, 6, 8, 8 ,
Union 3, 3, 6, 6, 6, 8, 8 , 3, 6, 8
90 An Introduction to Programming with Mathematica
This computation can be described as follows:
The FoldList function is first applied to the function Max, 0, and the list
{3,1,6,5,4,8,7} (look at the Trace of this computation to see what Fold
List is doing here).
In[15]:= FoldList Max, 0, 3, 1, 6, 5, 4, 8, 7
Out[15]= 0, 3, 3, 6, 6, 6, 8, 8
The Rest function is then applied to the result of the previous step to remove the
first element of the list.
In[16]:= Rest %
Out[16]= 3, 3, 6, 6, 6, 8, 8
Finally, the Union function is applied to the result of the previous step to remove
duplicates.
In[17]:= Union %
Out[17]= 3, 6, 8
Here is the function definition.
In[18]:= maxima x_ : Union Rest FoldList Max, 0, x
Applying maxima to a list of numbers produces a list of all those numbers that are
larger than any number that comes before it.
In[19]:= maxima 4, 2, 7, 3, 4, 9, 14, 11, 17
Out[19]= 4, 7, 9, 14, 17
Notice that in each of the nested functions described here, the argument of the first
function was explicitly referred to, but the expressions that were manipulated in the
succeeding function calls were not identified other than as the results of the previous steps
(that is, as the results of the preceding function applications).
Here is an interesting application of building up a program with nested functions
the creation of a deck of cards. (Hint: The suit icons are entered by typing in \[ClubSuit
], \[DiamondSuit], etc.)
4 Functional programming 91
In[20]:= cardDeck Flatten
Outer List, , , , , Join Range 2, 10 , J, Q, K, A , 1
Out[20]= , 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 , , 9 , , 10 ,
, J , , Q , , K , , A , , 2 , , 3 , , 4 , , 5 , , 6 ,
, 7 , , 8 , , 9 , , 10 , , J , , Q , , K , , A , , 2 ,
, 3 , , 4 , , 5 , , 6 , , 7 , , 8 , , 9 , , 10 ,
, J , , Q , , K , , A , , 2 , , 3 , , 4 , , 5 , , 6 ,
, 7 , , 8 , , 9 , , 10 , , J , , Q , , K , , A
You might think of cardDeck as a name for the expression given on the right-hand side
of the immediate definition, or you might think of cardDeck as defining a function with
zero arguments.
To understand what is going on here, we will build up this program from scratch.
First we form a list of the number and face cards in a suit by combining a list of the num-
bers 2 through 10, Range[2,10], with a four-element list representing the jack, queen,
king, and ace, {J,Q,K,A}.
In[21]:= Join Range 2, 10 , J, Q, K, A
Out[21]= 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A
Now we pair each of the 13 elements in this list with each of the four elements in the list
representing the card suits { , , , }. This produces a list of 52 ordered pairs represent-
ing the cards in a deck, where the king of clubs, for example, is represented by { ,K}).
In[22]:= Outer List, , , , , %
Out[22]= , 2 , , 3 , , 4 , , 5 , , 6 , , 7 ,
, 8 , , 9 , , 10 , , J , , Q , , K , , A ,
, 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 ,
, 9 , , 10 , , J , , Q , , K , , A ,
, 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 ,
, 9 , , 10 , , J , , Q , , K , , A ,
, 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 ,
, 9 , , 10 , , J , , Q , , K , , A
While we now have all of the cards in the deck, they are grouped by suit in a nested list.
We therefore un-nest the list:
In[23]:= Flatten %, 1
Out[23]= , 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 , , 9 , , 10 ,
, J , , Q , , K , , A , , 2 , , 3 , , 4 , , 5 , , 6 ,
, 7 , , 8 , , 9 , , 10 , , J , , Q , , K , , A , , 2 ,
, 3 , , 4 , , 5 , , 6 , , 7 , , 8 , , 9 , , 10 ,
, J , , Q , , K , , A , , 2 , , 3 , , 4 , , 5 , , 6 ,
, 7 , , 8 , , 9 , , 10 , , J , , Q , , K , , A
Voila!
92 An Introduction to Programming with Mathematica
The step-by-step construction that we used here, applying one function at a time,
checking each function call separately, is a very efficient way to prototype your programs in
Mathematica. We will use this technique again in the next example.
We will perform what is called a perfect shuffle, consisting of cutting the deck in half
and then interleaving the cards from the two halves. Rather than working with the large
list of 52 ordered pairs during the prototyping, we will use a short made-up list. A short list
of an even number of ordered integers is a good choice for the task.
In[24]:= d Range 6
Out[24]= 1, 2, 3, 4, 5, 6
We first divide the list into two equal-sized lists.
In[25]:= Partition d, Length d 2
Out[25]= 1, 2, 3 , 4, 5, 6
We now want to interleave these two lists to form {1,4,2,5,3,6}. The first step is to
pair the corresponding elements in each of the two lists above. This can be done using the
Transpose function.
In[26]:= Transpose %
Out[26]= 1, 4 , 2, 5 , 3, 6
We now un-nest the interior lists using the Flatten function. We could flatten our
simple list using Flatten[], but, since we know that ultimately we will be dealing with
ordered pairs rather than integers, we will use Flatten[,1] as we did in creating the
card deck.
In[27]:= Flatten %, 1
Out[27]= 1, 4, 2, 5, 3, 6
That does the job. Given this prototype, it is easy to write the actual function to
perform a perfect shuffle on a deck of cards. Notice we have generalized this shuffle to lists
of arbitrary length.
In[28]:= shuffle lis_ :
Flatten Transpose Partition lis, Length lis 2 , 1
In[29]:= shuffle cardDeck
Out[29]= , 2 , , 2 , , 3 , , 3 , , 4 , , 4 , , 5 , , 5 , , 6 ,
, 6 , , 7 , , 7 , , 8 , , 8 , , 9 , , 9 , , 10 ,
, 10 , , J , , J , , Q , , Q , , K , , K , , A , , A ,
, 2 , , 2 , , 3 , , 3 , , 4 , , 4 , , 5 , , 5 , , 6 ,
, 6 , , 7 , , 7 , , 8 , , 8 , , 9 , , 9 , , 10 ,
, 10 , , J , , J , , Q , , Q , , K , , K , , A , , A
4 Functional programming 93
Let us take this example one step further and construct a function that deals cards
from a card deck. We will construct this function in stages using the prototyping method
we showed earlier.
First we need to define a function that removes a single element from a randomly chosen
position in a list.
In[30]:= removeRand lis_ :
Delete lis, Random Integer, 1, Length lis
The function removeRand first uses the Random function to randomly choose an integer
k between 1 and the length of the list, and then uses the Delete function to remove the
kth element of the list. For example, if a list has 10 elements, an integer between 1 and 10,
say 6, is randomly determined and the element in the sixth position in the list is then
removed from the list.
In[31]:= lis 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ;
removeRand lis
Out[32]= 2, 3, 4, 5, 6, 7, 8, 9, 10
Now we want to make a function call that applies the removeRand function to the
cardDeck list, then applies the removeRand function to the resulting list, then applies
the removeRand function to the resulting list, and so on, a total of n times. The way to
carry out this operation is with the Nest function.
Nest[removeRand, cardDeck, n]
Lastly, we want the cards that are removed fromcardDeck rather than those that remain.
Complement[cardDeck, Nest[removeRand, cardDeck, n]]
Now, we write this up formally into the user-defined deal function.
In[33]:= deal n_ : Complement cardDeck, Nest removeRand, cardDeck, n
Let us try it out.
In[34]:= deal 5
Out[34]= , 3 , , K , , 2 , , K , , J
Not a bad hand!
94 An Introduction to Programming with Mathematica
Exercises
1. One of the games in the Illinois State Lottery is based on choosing n numbers, each
between 0 and 9, with duplicates allowed; in practice, a selection is made from
containers of numbered ping pong balls. We can model this game using a simple
user-defined function, which we will call pick (after the official lottery names of Pick
3 and Pick 4).
In[1]:= pick n_ : Table Random Integer, 0, 9 , n
In[2]:= pick 4
Out[2]= 0, 9, 0, 4
This program can be generalized to performrandom sampling with replacement on any
list. Write a function chooseWithReplacement[lis,n], where lis is the list, n is
the number of elements being chosen and the following is a typical result.
In[3]:= chooseWithReplacement a, b, c, d, e, f, g, h , 3
Out[3]= h, b, f
2. Write your own user-defined functions using the ToCharacterCode and From
CharacterCode functions to perform the same operations as StringInsert and
StringDrop.
3. Create a function distance[a,b] that finds the distance between two points a and
b in the plane.
4. Write a user-defined function interleave2 that interleaves the elements of two
lists of unequal length. (You have already seen how to interleave lists of equal length
using Partition earlier in this section.) Your function should take the lists
{1,2,3} and {a,b,c,d} as inputs and return {1,a,2,b,3,c,d}.
5. Write a nested function call that creates a deck of cards and performs a perfect
shuffle on it.
6. Write nested function calls using the ToCharacterCode and FromCharacter
Code functions to perform the same operations as the built-in StringJoin and
StringReverse functions.
4 Functional programming 95
4.5 Auxiliary functions
There are several major drawbacks to the deal function created in the previous section. In
order to use deal, the definition of removeRand and the value of cardDeck must be
entered before calling deal. It would be much more convenient if we could incorporate
these functions within the deal function definition itself. In the next section, we will show
how this can be done.
Compound functions
The left-hand side of a compound function is the same as that of any user-defined function.
The right-hand side consists of consecutive expressions enclosed in parentheses and
separated by semicolons.
name arg
1
_, arg
2
_, , arg
n
_ : expr
1
; expr
2
; ; expr
m
The expressions can be user-defined functions (also known as auxiliary functions),
value declarations, and function calls. When a compound function is evaluated with
particular argument values, these expressions are evaluated in order and the result of the
evaluation of the last expression is returned (by adding a semicolon after expr
n
, the display
of the final evaluation result can also be suppressed).
We will work with the deal function to illustrate how a compound function is
created. We need the following three expressions.
In[1]:= cardDeck Flatten Outer List,
, , , , Join Range 2, 10 , J, Q, K, A , 1 ;
In[2]:= removeRand lis_ :
Delete lis, Random Integer, 1, Length lis
In[3]:= deal n_ : Complement cardDeck, Nest removeRand, cardDeck, n
The conversion to a compound function is easily done. We will first remove the old
definitions.
In[4]:= Clear deal, cardDeck, removeRand
96 An Introduction to Programming with Mathematica
Now we can create and enter the new definition.
In[5]:= deal n_ :
cardDeck Flatten Outer List,
, , , , Join Range 2, 10 , J, Q, K, A , 1 ;
removeRand lis_ : Delete lis,
Random Integer, 1, Length lis ;
Complement cardDeck, Nest removeRand, cardDeck, n
Let us check that this works.
In[6]:= deal 5
Out[6]= , 3 , , 2 , , 3 , , 4 , , Q
A couple of things should be pointed out about the right-hand side of a compound
function definition. Since the expressions on the right-hand side are evaluated in order,
value declarations and auxiliary function definitions should be given before they are used
and the argument names used on the left-hand side of auxiliary function definitions must
differ from the argument names used by the compound function itself.
Finally, when we enter a compound function definition, we are entering not only the
function but also the auxiliary functions and the value declarations. If we then remove the
function definition using Clear, the auxiliary function definitions and value declarations
remain. This can cause a problem if we subsequently try to use the names of these auxiliary
functions and values elsewhere.
So how does the global rule base treat compound functions? When a compound
function definition is entered, a rewrite rule corresponding to the entire definition is
created. Each time the compound function is subsequently called, rewrite rules are created
from the auxiliary function definitions and value declarations within the compound
function.
In[7]:= ?cardDeck
Global`cardDeck
cardDeck , 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 ,
, 9 , , 10 , , J , , Q , , K , , A , , 2 , , 3 , , 4 ,
, 5 , , 6 , , 7 , , 8 , , 9 , , 10 , , J , , Q , , K ,
, A , , 2 , , 3 , , 4 , , 5 , , 6 , , 7 , , 8 , , 9 ,
, 10 , , J , , Q , , K , , A , , 2 , , 3 , , 4 , , 5 ,
, 6 , , 7 , , 8 , , 9 , , 10 , , J , , Q , , K , , A
It is considered bad programming practice to leave auxiliary definitions in the global
rule base that are not explicitly needed by the user of your function. In fact, it could
interfere with a users workspace and cause unintended problems.
4 Functional programming 97
To prevent these additional rewrite rules from being placed in the global rule base,
you can localize their names by using the Module construct in the compound function
definition. This is what we discuss next.
Localizing names: Module
When a user-defined function is written, it is generally a good idea to isolate the names of
values and functions defined on the right-hand side from the outside world in order to
avoid any conflict with the use of a name elsewhere in the session (for example, cardDeck
might be used elsewhere to represent a pinochle deck). This can be done by wrapping the
right-hand side of the function definition in the built-in Module function.
name arg
1
_, arg
2
_, , arg
n
_ : Module name
1
, name
2
value, ,
expr
The first argument of the Module function is a list of the names we want to localize. If we
wish, we can assign values to these names, as is shown with name
2
above (the assigned value
is only an initial value and can be changed subsequently). The list is separated from the
right-hand side by a comma and so the parentheses enclosing the right-hand side of a
compound function are not needed.
We can demonstrate the use of Module with the deal function.
In[8]:= Clear deal
In[9]:= deal n_ : Module cardDeck, removeRand ,
cardDeck Flatten Outer List,
, , , , Join Range 2, 10 , J, Q, K, A , 1 ;
removeRand lis_ : Delete lis,
Random Integer, 1, Length lis ;
Complement cardDeck, Nest removeRand, cardDeck, n
Briefly, when Module is encountered, the symbols that are being localized (card
Deck and removeRand in the above example) are temporarily given new and unique
names, and all occurrences of those symbols in the body of the Module are given those
new names as well. In this way, these unique and temporary names, which are local to the
function, will not interfere with any functions outside of the Module.
98 An Introduction to Programming with Mathematica
It is generally a good idea to wrap the right-hand side of all compound function
definitions in the Module function. Another way to avoid conflicts in the use of names of
auxiliary function definitions is to use a function that can be applied without being given a
name. Such functions are called pure functions, which we discuss in Section 4.6.
Localizing values: Block
Occasionally, you will need to localize a value associated with a symbol without localizing
the symbol name itself. For example, you may have a recursive computation that requires
you to temporarily reset the system variable $RecursionLimit. You can do this with
Block, thereby only localizing the value of $RecursionLimit during the evaluation
inside the Block.
In[10]:= Block $RecursionLimit 20 ,
x g x
$RecursionLimit::reclim :
Recursion depth of 20 exceeded. More
Out[10]= g g
g g g g g g g g g g g g g g g g Hold g x
Notice the global value of $RecursionLimit is unchanged.
In[11]:= $RecursionLimit
Out[11]= 256
This construct is similar to what is done for the iterators in Table, Do, Sum, and Prod
uct.
Module, on the other hand, would create an entirely new symbol, $Recursion
Limit$nn that would have nothing to do with the global variable $RecursionLimit,
and so Module would be inappropriate for this particular task.
Localizing constants: With
Another scoping construct is available when you simply need to localize constants. If, in
the body of your function, you use a variable that is assigned a constant once and never
changes, then With is the preferred means to localize that constant.
This sets the global variable y to have the value 5.
In[12]:= y 5;
4 Functional programming 99
Here is a simple function that initializes y as a local constant.
In[13]:= f x_ : With y x 1 ,
y
We see the global symbol is unchanged and it does not interfere with the local symbol y
inside the With.
In[14]:= y
Out[14]= 5
In[15]:= f 2
Out[15]= 3
Using With, you can initialize local constants with the values of global symbols. For
example:
In[16]:= With y y ,
g x_ : x y
This shows that the global value for y was inserted inside g.
In[17]:= ?g
Global`g
g x$_ : x$ 5
Resetting the global value of y has no effect on the localized y inside the With.
In[18]:= y 1;
In[19]:= g 5
Out[19]= 10
Exercises
1. Write a compound function definition for the location of steps taken in an n-step
random walk on a square lattice. Hint: Use the definition for the step increments of
the walk as an auxiliary function.
2. The PerfectSearch function defined in Section 1.1 is impractical for checking
large numbers because it has to check all numbers from 1 through n. If you already
100 An Introduction to Programming with Mathematica
know the perfect numbers below 500, say, it is inefficient to check all numbers from
1 to 1,000 if you are only looking for perfect numbers in the range 500 to 1,000.
Modify searchPerfect so that it accepts two numbers as input and computes all
perfect numbers between the inputs. For example, PerfectSearch[a,b] will
produce a list of all perfect numbers in the range froma to b.
3. Overload the PerfectSearch function to compute all 3-perfect numbers. A 3-per-
fect number is such that the sum of its divisors equals three times the number. For
example, 120 is 3-perfect since it is equal to three times the sum of its divisors.
In[1]:= Apply Plus, Divisors 120
Out[1]= 360
Find the only other 3-perfect number under 1,000.
You can overload PerfectSearch as defined in Exercise 2 above by defining a
three-argument version PerfectSearch[a,b,3].
4. Overload PerfectSearch to find the three 4-perfect numbers less than 2,200,000.
5. Redefine PerfectSearch so that it accepts as input a number k, and two numbers
a and b, and computes all k-perfect numbers in the range froma to b. For example,
PerfectSearch[1,30,2] would compute all 2-perfect numbers in the range
from 1 to 30 and, hence, would output {6,28}.
6. If n is defined to be the sum of the divisors of n, then n is called superperfect if
n 2 n. Write a function SuperPerfectSearch[a,b] that finds all super-
perfect numbers in the range froma to b.
7. Often in processing files you will be presented with expressions that need to be
converted into a format that can be more easily manipulated inside Mathematica. For
example, a file may contain dates in the form 20030515 to represent May 15, 2003.
Mathematica represents its dates as a list {year,month,day,hour,minutes,seconds}.
Write a function convertToDate[n] to convert a number consisting of eight
digits such as 20030515 into a list of the form {2003,5,15}.
In[2]:= convertToDate 20030515
Out[2]= 2003, 5, 15
4 Functional programming 101
4.6 Pure functions
A pure function is a function that does not have a name and that can be used on the spot;
that is, at the moment it is created. This is often convenient, especially if the function is
only going to be used once or as an argument to a higher-order function, such as Map,
Fold, or Nest. The built-in function Function is used to create a pure function.
The basic form of a pure function is Function[x,body] for a pure function with a
single variable x (any symbol can be used for the variable), and
Function[{x,y,},body] for a pure function with more than one variable. The body
looks like the right-hand side of a user-defined function definition, with the variables x, y,
, where argument names would be.
As an example, the square function we created earlier can be written as a pure
function.
In[1]:= Function z, z
2
Out[1]= Function z, z
2
There is also a standard input form that can be used in writing a pure function which is
easier to write than the Function notation but can be a bit cryptic to read. The right-
hand side of the function definition is rewritten by replacing the variable by the pound
symbol (#) and ending the expression with the ampersand symbol (&) to indicate that this
is a pure function.
#
2
&
If there is more than one variable, #1, #2, and so on are used.
A pure function can be used exactly like more conventional looking functions, by
following the function with the argument values enclosed in square brackets. First we show
the pure function using Function.
In[2]:= Function z, z
2
6
Out[2]= 36
Here is the same thing, but using the more cryptic shorthand notation (the parentheses in
the following example are purely for readability and can be omitted if you wish).
In[3]:= #
2
& 6
Out[3]= 36
We can, if we wish, give a pure function a name and then use that name to call the function
later. This has the same effect as defining the function in the more traditional manner.
In[4]:= squared #
2
&;
102 An Introduction to Programming with Mathematica
In[5]:= squared 6
Out[5]= 36
Pure functions are very commonly used with higher-order functions like Map and
Apply, so, before going further, let us first look at a few simple examples of the use of
pure functions.
Here is a list of numbers.
In[6]:= lis 2, 5, 6.1 ;
Now suppose we wished to square each number and then add 1 to it. The pure function
that does this is: #
2
1 &. So that is what we need to map across this list.
In[7]:= Map #
2
1 &, lis
Out[7]= 5, 26, 38.21
In the next example we will create a set of data and then use the Select function to
filter out outliers.
In[8]:= data 24.39001, 29.669, 9.321, 20.8856,
23.4736, 22.1488, 24.7434, 22.1619, 21.1039,
24.8177, 27.1331, 25.8705, 39.7676, 24.7762
Out[8]= 24.39, 29.669, 9.321, 20.8856, 23.4736, 22.1488, 24.7434,
22.1619, 21.1039, 24.8177, 27.1331, 25.8705, 39.7676, 24.7762
A plot of the data shows there are two outliers.
In[9]:= ListPlot data, PlotStyle PointSize .02 ;
2 4 6 8 10 12 14
10
15
20
25
30
35
40
The Select function takes two arguments the first is the expression from which it will
select elements, and the second argument is a function that must return True or False.
Select[expr,test] will then select those elements from expr that return True when test is
applied to them.
4 Functional programming 103
Suppose we wish to exclude all data points that lie outside of the range 20 to 30.
Then we need a function that returns True if its argument is in that range.
In[10]:= Select data, 20 # 30 &
Out[10]= 24.39, 29.669, 20.8856, 23.4736, 22.1488, 24.7434,
22.1619, 21.1039, 24.8177, 27.1331, 25.8705, 24.7762
A good way to become comfortable with pure functions is to see them in action, so
we will convert some of the functions we defined earlier into pure functions, showing both
the (#)& and the Function forms so that you can decide which you prefer to use.
This function tests whether all the elements of a list are even.
In[11]:= areEltsEven lis_ : Apply And, Map EvenQ, lis
In[12]:= areEltsEven 2, 4, 5, 8
Out[12]= False
Here it is written using pure functions.
In[13]:= Function lis, Apply And, Map EvenQ, lis 2, 4, 5, 8
Out[13]= False
In[14]:= Apply And, Map EvenQ, #1 & 2, 4, 5, 8
Out[14]= False
This function returns each element in the list greater than all previous elements.
In[15]:= maxima[x_] := Union[Rest[FoldList[Max, 0, x]]]
In[16]:= maxima 2, 6, 3, 7, 9, 2
Out[16]= 2, 6, 7, 9
Here it is written using pure functions.
In[17]:= Function x, Union Rest FoldList Max, 0, x 2, 6, 3, 7, 9, 2
Out[17]= 2, 6, 7, 9
In[18]:= Union Rest FoldList Max, 0, # & 2, 6, 3, 7, 9, 2
Out[18]= 2, 6, 7, 9
We can also create nested pure functions. For example, this maps the pure squaring
function over the three-element list {3,2,7}.
In[19]:= Map #
2
&, 3, 2, 7
Out[19]= 9, 4, 49
104 An Introduction to Programming with Mathematica
When dealing with nested pure functions, the shorthand notation can be used for
each of the pure functions but care needs to be taken to avoid confusion as to which #
variable belongs to which pure function. This can be avoided by using Function, in
which case different variable names can be used.
In[20]:= Function y, Map Function x, x
2
, y 3, 2, 7
Out[20]= 9, 4, 49
Exercises
1. Write a function to sum the squares of the elements of a numeric list.
2. Write a function to sum the digits of any integer. You will need the IntegerDig
its function (use ?IntegerDigits, or look up IntegerDigits in the Help
Browser to find out about this function).
3. Using the definition of the distance function from Exercise 3 of Section 4.4, write
a new function diameter[pts] that, given a set of points in the plane, finds the
maximum distance between all pairs of points. Try to incorporate the distance
function into diameter without naming it explicitly; that is, use it as a pure func-
tion. Consider using Distribute to get the set of all pairs of points.
In[1]:= pts p
1
, p
2
, p
3
;
In[2]:= Distribute pts, pts , List
Out[2]= p
1
, p
1
, p
1
, p
2
, p
1
, p
3
, p
2
, p
1
,
p
2
, p
2
, p
2
, p
3
, p
3
, p
1
, p
3
, p
2
, p
3
, p
3
4. Take the removeRand function defined in Section 4.4 and rewrite it as a pure
function.
In[3]:= removeRand[lis_] :=
Delete[lis, Random[Integer, {1, Length[lis]}]]
5. Convert the deal function developed earlier into one that uses pure functions. Use
the pure function version of the removeRand function from the previous exercise in
your new deal function definition.
6. Create a function RepUnit[n] that generates integers of length n consisting
entirely of 1s. For example RepUnit[7] should produce 1111111.
4 Functional programming 105
7. Create a function chooseWithoutReplacement[lis,n] that is a generalization
of the deal function in that it will work with any list.
8. Write a pure function that moves a random walker from one location on a square
lattice to one of the four adjoining locations with equal probability. For example,
starting at {0,0}, the function should return either {0,1}, {0,-1}, {1,0} or
{-1,0} with equal likelihood. Now, use this pure function with NestList to
generate the list of step locations for an n-step random walk starting at {0,0}.
9. Create a function WordsStartingWith[lis,char] that outputs all those words in
lis that begin with the character char. As a sample list, you can use the dictionary.dat
file that comes with Mathematica.
Here is a platform-independent path to the dictionary file.
In[4]:= wordfile ToFileName $InstallationDirectory, "Documentation",
"English", "Demos", "DataFiles" , "dictionary.dat"
Out[4]= C:\Program Files\Wolfram Research\Mathematica\5.1\
Documentation\English\Demos\DataFiles\dictionary.dat
This reads in the file using ReadList, specifying the type of data we are reading in
as a Word.
In[5]:= words ReadList wordfile, Word ;
10. Modify Exercise 9 above so that WordsStartingWith accepts a string of arbitrary
length as its second argument.
11. A naive approach to polynomial arithmetic would require three additions and six
multiplies to carry out the arithmetic in the expression a x
3
b x
2
c x d. Using
Horners method for fast polynomial multiplication, this expression can be repre-
sented as d x c x b a x , where there are now half as many multiplies. In general,
the number of multiplies for an n-degree polynomial is given by:
In[6]:= Binomial n 1, 2
Out[6]=
1
2
n 1 n
This, of course, grows quadratically with n, whereas Horners method grows linearly.
Create a function Horner[lis,var] that implements Horners method for polyno-
mial multiplication. Here is some sample input and the corresponding output that
your function should generate.
106 An Introduction to Programming with Mathematica
In[7]:= Horner a, b, c, d , x
Out[7]= d x c x b a x
In[8]:= Expand %
Out[8]= d c x b x
2
a x
3
4.7 One-liners
In the simplest version of a user-defined function, there are no value declarations or
auxiliary function definitions; the right-hand side is a single nested function call whose
arguments are the names of the arguments on the left-hand side, without the blanks. These
one-liners are fantastically useful and so we will discuss them in the context of three
examples, one from electrical engineering (computing Hamming distance), one from
ancient history (the Josephus problem), and the last a simple and practical problem
(counting change).
Hamming distance
When a code is transmitted over a channel in the presence of noise, errors will often occur.
The task of channel coding is to represent the source information in a manner that mini-
mizes the error probability in decoding. Hamming distance is used in source coding to
represent an information source with the minimum number of symbols. For two lists of
binary symbols, the Hamming distance is defined as the number of nonmatching elements
and so gives a measure of the how well these two lists match up.
Let us first think about how we might determine if two binary symbols are identical.
SameQ[x,y] will return True if x and y are identical.
In[1]:= SameQ 0, 0 , SameQ 1, 0 , SameQ 1, 1
Out[1]= True, False, True
So we need to thread SameQ over the two lists of binary numbers
In[2]:= MapThread SameQ, 1, 0, 0, 1, 1 , 0, 1, 0, 1, 0
Out[2]= False, False, True, True, False
4 Functional programming 107
and then count up the occurrences of False.
In[3]:= Count %, False
Out[3]= 3
So a first definition of HammingDistance could be accomplished by putting these last
two pieces together.
In[4]:= HammingDistance lis1_, lis2_ :
Count MapThread SameQ, lis1, lis2 , False
In[5]:= HammingDistance 1, 0, 0, 1, 1 , 0, 1, 0, 1, 0
Out[5]= 3
We might try to solve this problem by a more direct approach. Since we are dealing
with binary information, we could use some of the logical binary operators built into
Mathematica.
Here is our transposed list again.
In[6]:= lis Transpose 1, 0, 0, 1, 1 , 0, 1, 0, 1, 0
Out[6]= 1, 0 , 0, 1 , 0, 0 , 1, 1 , 1, 0
BitXor[x,y] returns the bitwise XOR of x and y. So if x and y can only be among
the binary integers 0 or 1, BitXor will return 0 whenever they are the same and will
return 1 whenever they are different.
In[7]:= Apply BitXor, 0, 0 , 1, 0 , 1, 1 , 1
Out[7]= 0, 1, 0
Here then is BitXor applied to lis.
In[8]:= Apply BitXor, lis, 1
Out[8]= 1, 1, 0, 0, 1
And here are the number of 1s that occur in that list.
In[9]:= Apply Plus, %
Out[9]= 3
Summing up, our function HammingDistance2 first pairs up the lists (Transpose),
then determines which pairs contain different elements (apply BitXor), and finally counts
up the number of 1s (Apply[Plus,]).
In[10]:= HammingDistance2 lis1_, lis2_ : Apply Plus,
Apply BitXor, Transpose lis1, lis2 , 1
108 An Introduction to Programming with Mathematica
In[11]:= HammingDistance2 1, 0, 0, 1, 1 , 0, 1, 0, 1, 0
Out[11]= 3
Let us compare the running times of these implementations using a large data set, in
this case two lists consisting of one million 0s and 1s.
In[12]:= data1 Table Random Integer , 10
6
;
In[13]:= data2 Table Random Integer , 10
6
;
In[14]:= Timing HammingDistance data1, data2
Out[14]= 1.162 Second, 499801
In[15]:= Timing HammingDistance2 data1, data2
Out[15]= 1.392 Second, 499801
Although these times do not look too bad, they are in fact too slow for any serious
work with signal processing. The exercises ask you to write an implementation of Hamming
Distance that runs about two orders of magnitude faster than those presented here.
As an aside, the above computations are not a bad check on the built-in random
number generator we would expect that about one half of the paired up lists would
contain different elements.
The Josephus problem
Flavius Josephus was a Jewish historian during the RomanJewish war of the first century
AD. Through his writings comes the following story:
The Romans had chased a group of ten Jews into a cave and were about to attack. Rather than
die at the hands of their enemy, the group chose to commit suicide one by one. Legend has it
though, that they decided to go around their circle of ten individuals and eliminate every other
person until only one was left.
Who was the last to survive? Although a bit macabre, this problem has a definite
mathematical interpretation that lends itself well to a functional style of programming. We
will start by changing the problem a bit (the importance of rewording a problem can
hardly be overstated; the key to most problem-solving resides in turning something we can
not work with into something we can work with). We will restate the problem as follows: n
people are lined up. The first person is moved to the end of the line, the second person is
removed from the line, the third person is moved to the end of the line, and so on until
only one person remains in the line.
4 Functional programming 109
The statement of the problem indicates that there is a repetitive action, performed
over and over again. It involves the use of the RotateLeft function (move the person at
the front of the line to the back of the line) followed by the use of the Rest function
(remove the next person from the line).
In[16]:= Rest RotateLeft # & a, b, c, d
Out[16]= c, d, a
At this point it is already pretty clear where this computation is headed. We want to take a
list and, using the Nest function, perform the pure function call (Rest[Rotate
Left[#])& on the list until only one element remains. A list of n elements will need n 1
calls. So we can now write the function, to which we give the apt name survivor.
In[17]:= survivor lis_ :
Nest Rest RotateLeft # &, lis, Length lis 1
Trying out the survivor function on a list of ten, we see that the fifth position will be
the position of the survivor.
In[18]:= survivor Range 10
Out[18]= 5
Tracing the applications of RotateLeft in this example gives a very clear picture of what
is going on. The following form of TracePrint shows only the results of the applica-
tions of RotateLeft that occur during evaluation of the expression survivor[
Range[6]].
In[19]:= TracePrint survivor Range 6 , RotateLeft
RotateLeft
2, 3, 4, 5, 6, 1
RotateLeft
4, 5, 6, 1, 3
RotateLeft
6, 1, 3, 5
RotateLeft
3, 5, 1
RotateLeft
1, 5
Out[19]= 5
110 An Introduction to Programming with Mathematica
Pocket change
As another example, we will write a program to perform an operation most of us do every
day: calculating how much change we have in our pocket. Suppose we have the following
collection of coins.
In[20]:= coins p, p, q, n, d, d, p, q, q, p
Out[20]= p, p, q, n, d, d, p, q, q, p
Assume p, n, d, and q represent pennies, nickels, dimes, and quarters, respectively. Let us
start by using the Count function to determine the number of pennies we have.
In[21]:= Count coins, p
Out[21]= 4
This works. So let us do the same thing for all of the coin types.
In[22]:= Count coins, p , Count coins, n ,
Count coins, d , Count coins, q
Out[22]= 4, 1, 2, 3
Looking at this list, it is apparent that there ought to be a more compact way of
writing the list. If we Map a pure function involving Count and coins on to the list
{p,n,d,q}, it should do the job.
In[23]:= Map Count coins, #1 & , p, n, d, q
Out[23]= 4, 1, 2, 3
Now that we know how many coins of each type we have, we want to calculate how much
change we have. We first do the calculation manually to see what we get for an answer (so
we will know when our program works).
In[24]:= 4 1 1 5 2 10 3 25
Out[24]= 104
From the above computation we see that the lists {4,1,2,3} and {1,5,10,25} are
first multiplied together element-wise and then the elements of the result are added. This
suggests a few possibilities.
In[25]:= Apply Plus, 4, 1, 2, 3 1, 5, 10, 25
Out[25]= 104
In[26]:= 4, 1, 2, 3 . 1, 5, 10, 25
Out[26]= 104
4 Functional programming 111
Either of these operations are suitable for the job (to coin a phrase, theres not a penny,
nickel, quarter, or dimes worth of difference). We will write the one-liner using the first
method.
In[27]:= pocketChange x_ :
Apply Plus, Map Count x, # & , p, n, d, q 1, 5, 10, 25
In[28]:= pocketChange coins
Out[28]= 104
Exercises
1. Write a function to compute the Hamming distance of two binary lists (assumed to
be of equal length), using Select and an appropriate predicate function.
2. All of the implementations of Hamming distance discussed so far are a bit slow for
large datasets. You can get a significant speedup in running times by using functions
that are optimized for working with numbers (a topic we discuss in detail in Chapter
8). Write an implementation of Hamming distance using the Total function and
then compare running times with the other versions discussed in this chapter.
3. One of the best ways to learn how to write programs is to practice reading code. We
list below a number of one-liner function definitions along with a very brief explana-
tion of what these user-defined functions do and a typical input and output. Decon-
struct these programs to see what they do and then reconstruct them as compound
functions without any pure functions.
a. Determine the frequencies with which distinct elements appear in a list.
In[1]:= frequencies lis_ : Map #, Count lis, # &, Union lis
In[2]:= frequencies a, a, b, b, b, a, c, c
Out[2]= a, 3 , b, 3 , c, 2
b. Divide up a list into parts each of whose lengths are given by the second
argument.
In[3]:= split1 lis_, parts_ :
Inner Take lis, #1, #2 &, Drop #1, 1 1,
Rest #1 , List & FoldList Plus, 0, parts
112 An Introduction to Programming with Mathematica
In[4]:= split1 Range 10 , 2, 5, 0, 3
Out[4]= 1, 2 , 3, 4, 5, 6, 7 , , 8, 9, 10
This is the same as the previous program, done in a different way.
In[5]:= split2[lis_, parts_] :=
Map[(Take[lis, # + {1, 0}])&,
Partition[FoldList[Plus, 0, parts], 2, 1]]
c. Another game in the Illinois State Lottery is based on choosing n numbers, each
between 0 and s with no duplicates allowed. Write a user-defined function called
lotto (after the official lottery names of Little Lotto and Big Lotto) to perform
sampling without replacement on an arbitrary list. (Note: The difference between
this function and the function chooseWithoutReplacement is that the order
of selection is needed here.)
In[6]:= lotto1 lis_, n_ : Flatten
Rest MapThread Complement, RotateRight # , # , 1 &
NestList Delete #, Random Integer, 1, Length # &,
lis, n
In[7]:= lotto1 Range 10 , 5
Out[7]= 10, 3, 2, 7, 6
This is the same as the previous program, done in a different way.
In[8]:= lotto2 lis_, n_ : Take Transpose Sort
Transpose Table Random , Length lis , lis 2 , n
As the split and lotto programs illustrate, user-defined functions can be written
in several ways. The choice as to which version of a program to use has to be based
on efficiency. A program whose development time was shorter and which runs faster
is better than a program which took more time to develop and which runs more
slowly. Although concise Mathematica programs tend to run fastest, when execution
speed is a primary concern (when dealing with very large lists) it is a good idea to
take various programming approaches and perform Timing tests to determine the
fastest program.
4. Use the Timing function to determine when (in terms of the relative sizes of the list
and the number of elements being chosen) it is preferable to use the different ver-
sions of the lotto function.
5. Rewrite the pocketChange function in two different ways one, using Dot, and
the other using Inner.
4 Functional programming 113
6. Make change with quarters, dimes, nickels, and pennies using the fewest coins.
In[9]:= makeChange 119
Out[9]= 4, 1, 1, 4
7. Write a one-liner to create a list of the step locations of a two-dimensional random
walk that is not restricted to a lattice. Hint: Each step length must be the same, so the
sum of the squares of the x- and y-components of each step should be equal to 1.
8. Write a one-liner version of convertToDate as described in Exercise 7 from
Section 4.5. Consider the built-in function FromDigits.
114 An Introduction to Programming with Mathematica
5 Procedural programming
Conventional programming languages like C and Fortran embody a style of program-
ming that has roots in the early days of computing when resource constraints forced
programmers to write their code in a step-by-step manner. These procedures, as they
came to be known, typically involved certain basic elements: looping over an array,
conditional statements that controlled the flow of execution, logical constructs to build
up tests, and functions to jump around from one place in a program to another.
Although newer languages have introduced many new programming paradigms,
procedural programming continues to be used and remains an appropriate style for
certain kinds of problems. In this chapter we will look at how procedural program-
ming is used in Mathematica, discuss what types of problems it is most appropriate for,
and compare Mathematicas implementation with other languages.
5.1 Introduction
A procedure is a series of instructions that are evaluated in a definite order. The following
program is a procedure.
In[1]:= mat a, b, c , d, e, f , g, h, k ;
newmat mat;
Do newmat i, j mat j, i ,
i, Length mat , j, Length mat ;
newmat
Out[4]= a, d, g , b, e, h , c, f, k
In[5]:= MatrixForm %
Out[5]//MatrixForm=
a d g
b e h
c f k
We could look at this procedure as a compound expression consisting of a sequence
of four expressions: the first assigns the symbolic 3 3 matrix to the symbol mat; the
second is also an assignment copying the matrix to another symbol, newmat; the third
expression loops through the matrix, interchanging columns and rows of the original and
putting them into the new matrix essentially performing a transpose operation; the final
expression simply outputs the new matrix.
Procedural programs also typically involve some flow control. What this means is that,
depending upon a certain condition, different steps in the procedure will be followed.
Perhaps the simplest example of this is an If statement.
In[6]:= f x_ : If 20 x 30, x,
Print "The number ", x, " is outside the range."
In[7]:= f 23
Out[7]= 23
In[8]:= f 66
The number 66 is outside the range.
The value of the first argument of the If function determines the direction of the
rest of the evaluation. This is a control structure.
These are typical components of procedural programs a series of expressions to
evaluate in some order and functions to control the flow of execution. In this chapter we
will explore these topics in addition to conditional definitions which are another form of
flow control. All of these features will greatly expand what we can do with Mathematica and
we will find many applications of these techniques in later chapters on recursion and
numerics.
5.2 Loops and iteration
Newtons method
One of the most famous of all numerical algorithms is Newtons method for finding the
roots of a function. Even though Mathematica includes a built-in function, FindRoot, that
implements this method, this is a classic use of iteration and so central to numerical
analysis that it is well worth your time learning how to implement it.
Throughout this section we will use the function x
2
50, whose root is, of course,
the square root of 50. Here is the computation using the built-in FindRoot.
In[1]:= FindRoot x
2
50 0, x, 50
Out[1]= x 7.07107
The number 50 in {x,50} is the initial guess of the root.
116 An Introduction to Programming with Mathematica
So why should you learn to program a root-finder yourself? As we stated above, it is a
classical algorithm and the basis of many more advanced root-finding techniques in
numerical analysis. But also, with many numerical problems, the built-in operations do not
always give you optimal results. This is because the built-in functions are designed to work
for the broadest possible set of situations, but might have occasional trouble with certain
exceptional cases. An example is the function f x x
1 3
.
In[2]:= FindRoot x
1 3
0, x, 0.1
FindRoot::lstol :
The line search decreased the step size to within tolerance
specified by AccuracyGoal and PrecisionGoal but was
unable to find a sufficient decrease in the merit
function. You may need more than MachinePrecision digits
of working precision to meet these tolerances. More
Out[2]= x 0.000405502 2.29415 10
15
Although this particular functions root can be better approximated using an option
(DampingFactor) to FindRoot, we will find it very instructive to program our own
root-finding functions that can solve this problem and, in the process, learn about the
structure of iterative programming.
In[3]:= FindRoot x
1 3
0, x, 0.1 , DampingFactor 2
Out[3]= x 8.93553 10
17
Do loops
Suppose we are given a function f and can compute its derivative, f . Then Newtons
algorithm works as follows:
give an initial estimate of the root, say x
0
keep generating better estimates, x
1
, x
2
, , using the following rule until you are
done (we will discuss this later):
x
i 1
x
i
f x
i
f x
i
The method is illustrated in Figure 5.1. Under the favorable circumstances pictured there
the estimates get closer and closer to the root.
5 Procedural programming 117
f x
0
x
0
x
1
Figure 5.1: Illustration of Newtons method
We will discuss in a moment when to stop, but first let us look at an example. For the
function f x x
2
50, the derivative is f x 2 x. This specific case is shown in Figure
5.2, with 50 itself as the initial estimate. Let us see what happens after five iterations of this
procedure.
In[4]:= f x_ : x
2
50
In[5]:= x0 50;
In[6]:= x1 N x0
f x0
f x0
Out[6]= 25.5
In[7]:= x2 N x1
f x1
f x1
Out[7]= 13.7304
In[8]:= x3 N x2
f x2
f x2
Out[8]= 8.68597
In[9]:= x4 N x3
f x3
f x3
Out[9]= 7.22119
In[10]:= x5 N x4
f x4
f x4
Out[10]= 7.07263
118 An Introduction to Programming with Mathematica
f 50
50 25.5 13.7 8.7
Figure 5.2: Newtons method for f x x
2
50
As you can see, these values are getting closer and closer to the real square root of 50,
which is approximately 7.07107.
We need to discuss how to decide when we are satisfied with the answer we have
computed. First, though, note one thing: Wherever we decide to stop, say at the fifth
iteration, all the previous values we computed are of no interest. So we could have avoided
introducing those new names by instead just writing the following:
In[11]:= a 50;
In[12]:= a N a
f a
f a
Out[12]= 25.5
In[13]:= a N a
f a
f a
Out[13]= 13.7304
In[14]:= a N a
f a
f a
Out[14]= 8.68597
In[15]:= a N a
f a
f a
Out[15]= 7.22119
In[16]:= a N a
f a
f a
Out[16]= 7.07263
5 Procedural programming 119
To return to the question of when to terminate the computation, one simple answer
is: repeat it ten times.
In[17]:= Do a N a f a f' a , 10
In general, Do[expr,{n}], evaluates expr n times. So, in this case, we can initialize a and
perform the ten evaluations as follows:
In[18]:= a 50;
Do a N a
f a
f a
, 10
In[20]:= a
Out[20]= 7.07107
Note that the Do loop itself yields no value (or rather, it yields the special value Null,
which is a symbol Mathematica uses when there is no result from an evaluation; nothing is
printed). But the important thing is that the Do loop assigns a value to a that is very close
to the square root of 50.
The arguments of Do are the same as those of Table (see Section 3.2; see also
Exercise 3 at the end of this section).
Do[expr,{i,imin,imax,di}]
This form repeats expr with variable i having values imin, imin di, and so on, as long
as the value of imax is not exceeded. The loop is repeated a total of imax imin di
times. Furthermore, if di is omitted, it is assumed to be 1; and if only i and imax are given,
both imin and di are assumed to be 1. For example, if we wanted to print each approxima-
tion and label it with a number, we could do that by using a compound expression inside
the body of the Do loop, in this case, adding a Print statement.
In[21]:= a 50;
Do a N a f a f' a ;
Print "approximation ", i, ": ", a , i, 1, 6
approximation 1: 25.5
approximation 2: 13.7304
approximation 3: 8.68597
approximation 4: 7.22119
approximation 5: 7.07263
approximation 6: 7.07107
120 An Introduction to Programming with Mathematica
Example: Random permutations
Let us look at another example of a Do loop. We will create a function random
Permutation[lis] that will take a list as an argument and generate a random permuta-
tion of its elements.
To build this function up step by step, we first start with a small list of ten elements.
In[23]:= lis Range 10
Out[23]= 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
The idea will be to choose a position within the list at random and remove the element in
that position and put it into a new list res.
In[24]:= rand : Random Integer, 1, Length lis
In[25]:= x Part lis, rand
Out[25]= 1
In[26]:= res ;
res Append res, x
Out[27]= 1
We then repeat the above process on the remaining elements of the list.
In[28]:= lis Complement lis, x
Out[28]= 2, 3, 4, 5, 6, 7, 8, 9, 10
In[29]:= x lis rand
res Append res, x
lis Complement lis, x
Out[29]= 8
Out[30]= 1, 8
Out[31]= 2, 3, 4, 5, 6, 7, 9, 10
In this example we know explicitly how many iterations to perform in our Do loop: n
times, where n is the length of the list that is being worked on.
First we clear some symbols.
In[32]:= Clear lis, res, x, rand ;
5 Procedural programming 121
Now we just put the pieces of the previous computations together in one input.
In[33]:= lis Range 10 ;
res ;
Do
x Part lis, Random Integer, 1, Length lis ;
res Append res, x ;
lis Complement lis, x ,
i, 1, 10
When we are done, the result is left in the new list res.
In[36]:= res
Out[36]= 7, 1, 2, 5, 8, 10, 4, 3, 9, 6
Here then is our function randomPermutation that takes a list as an argument
and generates a random permutation of that lists elements.
In[37]:= Clear res, rand, x, lis
In[38]:= randomPermutation lis_ : Module res , x, l2 lis ,
Do
x Part l2, Random Integer, 1, Length l2 ;
res Append res, x ;
l2 Complement l2, x ,
i, 1, Length lis ;
res
Here is a permutation of the list consisting of the first 20 integers.
In[39]:= randomPermutation Range 20
Out[39]= 7, 20, 16, 8, 19, 10, 15, 17,
13, 3, 5, 12, 1, 11, 2, 4, 6, 18, 9, 14
And here is a random permutation of the lowercase letters of the English alphabet.
In[40]:= alphabet Map FromCharacterCode , Range 97, 122
Out[40]= a, b, c, d, e, f, g, h, i, j, k,
l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
In[41]:= randomPermutation alphabet
Out[41]= i, l, c, s, t, d, j, q, y, f, e,
k, x, a, h, r, o, g, u, z, v, n, p, w, b, m
122 An Introduction to Programming with Mathematica
While loops
Let us return to Newtons method for finding roots and see how we can use a different
control structure for the iteration. In the previous section on Do loops, we explicitly
stopped the iteration after ten times through the loop. Ten times is okay for f x x
2
50,
but not always. Consider the function x sin x .
In[42]:= g x_ : x Sin x
It has a root at 0.
In[43]:= g 0
Out[43]= 0
However, ten iterations of Newtons algorithm does not get us very close to it.
In[44]:= xi 1.0;
Do xi N xi
g xi
g xi
, 10
In[46]:= xi
Out[46]= 0.0168228
Twenty-five iterations does a bit better.
In[47]:= xi 1.0;
Do xi N xi
g xi
g xi
, 25
In[49]:= xi
Out[49]= 0.0000384172
In truth, no fixed number of iterations is going to do the trick for all functions. We
need to iterate repeatedly until our estimate is close enough to stop. When is that? There
are a number of ways to answer that question, none always best, but here is an easy one:
when f x
i
is very close to zero. So, choose to be a very small number, and iterate until
f x
i
.
But how can we write a loop that will test some condition and stop when the condi-
tion is no longer met? The looping construct Do iterates a number of times that is fixed
when the loop is begun. We need a new kind of iterative function. It is While, and it has
the following form.
While[test,expr]
5 Procedural programming 123
The first argument is the test or condition, the second the body. It works like this: evaluate
the test; if it is true then evaluate the body and then the test again. If it is true again, then
again evaluate the body and the test. Continue this way until the test evaluates to False.
Note that the body may not be evaluated at all (if the test is false the first time), or it may
be evaluated once, or a thousand times.
This is just what we want: if the estimate is not yet close enough, compute a new
estimate and try again.
In[50]:= f x_ : x
2
50
In[51]:= .0001;
xi 50;
While Abs f xi ,
xi N xi
f xi
f xi
In[54]:= xi
Out[54]= 7.07107
To wrap things up, let us put this all into a function.
In[55]:= findRoot fun_, init_, _ : Module xi init ,
While Abs fun xi ,
xi N xi
fun xi
fun xi
;
xi
In[56]:= findRoot f, 50, .0001
Out[56]= 7.07107
Instead of setting a global variable to the final estimate, this function returns that
estimate as its value. (For an explanation of why we introduced the local variable xi, see
the end of this subsection.)
Let us work with this example a little more. Suppose we would like to know how
many iterations were needed to find the answer. One possibility is to insert a Print to
show the value of xi each time through the loop.
In[57]:= findRoot fun_, init_, _ : Module xi init ,
While Abs fun xi ,
Print "x ", xi ;
xi N xi
fun xi
fun xi
;
xi
124 An Introduction to Programming with Mathematica
In[58]:= findRoot f, 50, 0.001
x 50
x 25.5
x 13.7304
x 8.68597
x 7.22119
x 7.07263
Out[58]= 7.07107
Counting the lines shows that the function converged after six iterations (note that we
were seeing the value of xi at the beginning of each execution of the body). A better idea
would be to have the function actually count the number of iterations and return it as part
of its answer.
In[59]:= findRoot fun_, init_, _ :
Module xi init, count 0 , While Abs fun xi ,
count count 1;
xi N xi
fun xi
fun xi
;
xi, count
In[60]:= findRoot f, 50, 0.001
Out[60]= 7.07107, 6
Here is another question: in all these versions of findRoot, f[xi] is computed
two times at each iteration, once in the condition and once in the body. In many circum-
stances, calls to f are very time consuming, and should be minimized. Can we arrange that
f[xi] only be computed once in each iteration?
The solution to this is to create a new local variable, funxi, which always contains
the value of fun[xi] for the current value of xi. We can ensure that it does so by recom-
puting it whenever xi is reassigned.
In[61]:= findRoot fun_, init_, _ :
Module xi init, funxi fun init ,
While Abs funxi ,
xi N xi
funxi
fun xi
;
funxi fun xi ;
xi
5 Procedural programming 125
In all our examples, we used Module to introduce a local variable to which we
assigned values in the body of the While. We did this to avoid a common error in the use
of iteration: attempting to assign to a functions argument. For example, the following version
of findRoot does not work.
In[62]:= findRoot fun_, x_, _ :
While Abs fun x ,
x N x
fun x
fun x
;
x
In[63]:= findRoot Sin, .1, .01
Set::setraw : Cannot assign to raw object 0.1`. More
General::stop : Further output of Set::setraw will
be suppressed during this calculation. More
Out[63]= $Aborted
What happened can be seen from the trace (of which we have only shown some) of the
output.
In[64]:= TracePrint findRoot Sin, .1, .01 , findRoot
findRoot
While Abs Sin 0.1 0.01, 0.1 N 0.1
Sin 0.1
Sin 0.1
; 0.1
Set::setraw : Cannot assign to raw object 0.1`. More
General::stop : Further output of Set::setraw will
be suppressed during this calculation. More
Out[64]= $Aborted
The x in the body of findRoot is replaced by the argument .1, which is perfectly
normal, leaving an expression of the form 0.1 = something, which is not possible. There
is a way around this, using the HoldFirst attribute, but introducing local variables is
much better style. It is very disconcerting, after all, to call a function and find, when it is
done, that your global variables have changed values.
126 An Introduction to Programming with Mathematica
NestWhile and NestWhileList
Let us look again at the last version of the findRoot function we just created.
In[65]:= findRoot fun_, init_, _ :
Module xi init, funxi fun init ,
While Abs funxi ,
xi N xi
funxi
fun' xi
;
funxi fun xi ;
xi
The While loop evaluates the body of this function (the two assignments, one to xi and
the other to funxi) until the test fails. There is another function we could use to simplify
this calculation it is NestWhile.
NestWhile[f,init,test]
This function iterates f with initial value init, while test continues to be true.
Let us rewrite findRoot using NestWhile. The first argument is the function we
are iterating. Here we will use a pure function that represents the Newton iteration. The
second argument is the initial guess, the initial value for the iteration. The third argument
to NestWhile is the test that will be performed each time through the loop until it
returns False. In this case, we are setting an explicit value for of 0.001 and so our test is
f x .001.
In[66]:= f x_ : x
2
50
In[67]:= findRoot fun_, init_ :
NestWhile #
fun #
fun' #
&, N init , Abs fun # .001 &
This computes the square root of 50 with an initial guess of 10.
In[68]:= findRoot f, 10
Out[68]= 7.07108
We can easily write a function findRootList based on NestWhileList that will
output all the intermediate computed values.
In[69]:= findRootList fun_, init_ :
NestWhileList #
fun #
fun' #
&, N init , Abs fun # .001 &
5 Procedural programming 127
In[70]:= findRootList f, 10
Out[70]= 10., 7.5, 7.08333, 7.07108
Note: the functions introduced in this section are rather simplistic implementations of
Newtons algorithm. At this stage, we are only interested in learning about how to use
some of Mathematicas procedural functions to implement the iterations here. In their
current form, they have some serious limitations regarding accuracy and precision that we
will address in Chapter 8, where we will discuss numerical issues in detail. The exercises at
the end of this section also walk the reader through several improvements to these
functions.
Exercises
1. Compute the square roots of 50 and 60 simultaneously, that is, with a single Do loop.
2. Compare the use of a Do loop with using the function Nest (see Section 4.3). In
particular, compute the square root of 50 using Nest.
3. Do is closely related to Table, the main difference being that Do does not return any
value, whereas Table does. Use Table instead of Do in your solution to Exercise 1.
What do you get?
4. Compute Fibonacci numbers iteratively. You will need to have two variables, say
this and prev, giving the two most recent Fibonacci numbers, so that after the ith
iteration, this and prev have the values F
i
and F
i 1
, respectively.
5. One additional improvement can be made to the findRoot program developed in
this section. Notice that the derivative of the function fun is computed each time
through the loop. This is quite inefficient. Rewrite findRoot so that the derivative
is computed only once and that result is used in the body of the loop.
6. Another termination criterion for root-finding is to stop when x
i
x
i 1
; that
is, when two successive estimates are very close. The idea is that if we are not getting
much improvement, we must be very near the root. The difficulty in programming
this is that we need to remember the two most recent estimates computed. (It is
similar to computing Fibonacci numbers iteratively, as in Exercise 4.) Program
findRoot this way.
7. The built-in FindRoot function is set up so that you can monitor intermediate
computations using the option EvaluationMonitor.
128 An Introduction to Programming with Mathematica
In[1]:= xintermed ;
FindRoot x
2
50, x, 50 ,
EvaluationMonitor AppendTo xintermed, x ;
In[3]:= xintermed
Out[3]= 50., 25.5, 13.7304, 8.68597,
7.22119, 7.07263, 7.07107, 7.07107
Modify each of the versions of findRoot presented in the text that use a Do or
While loop to produce a list of all the estimates computed.
f x_ : x
2
50;
findRootList f, 50, 0.001
50, 25.5, 13.7304, 8.68597, 7.22119, 7.07263, 7.07107, 7.07107
8. To guard against starting with a poor choice of initial value, modify findRootList
to take, as an argument, a list of initial values, and simultaneously compute approxima-
tions for each until one converges; then return that one.
9. The bisection method is quite useful for finding roots of functions. If a continuous
function f x is such that f a 0 and f b 0 for two real numbers a and b, then, as a
consequence of the Intermediate Value Theorem of calculus, a root of f must occur
between a and b. If f is now evaluated at the midpoint of a and b, and if
f a b 2 0, then the root must occur between a b 2 and b; if not, then it
occurs between a and a b 2. This bisection can be repeated until a root is found
to any specified tolerance.
Define bisect[f ,{a,b, }] to compute a root of f , within , using the bisection
method. You should give it two initial values a and b and assume that f a f b 0;
that is, one of f a and f b is positive and the other is negative.
10. Using a While loop, write a function gcd[m,n] that computes the greatest com-
mon divisor of m and n. The Euclidean algorithm for computing the gcd of two
numbers m and n, assumed to be positive integers, sets m n, and n mmod n. It
iterates this process until n 0, at which point the gcd of m and n is left in the value
of m.
11. Create a procedural definition for each of the following functions, first by creating a
new list and filling in the elements. For each function, create a definition using a Do
loop and another using Table. For example, the following function first creates an
array of the same dimension as mat, but consisting of 0s. Then inside the Do loop it
assigns the element in position {j,i} in mat to position {i,j} in matA, effectively
5 Procedural programming 129
performing a transpose operation. Finally, it returns matA, since the Do loop itself
does not return a value.
In[4]:= transpose mat_ :
Module matA Table Table 0, n Length mat ,
m Length mat 1 , Do matA i, j mat j, i ,
i, 1, m ,
j, 1, n ;
matA
In[5]:= mat1 a, b, c , d, e, f , h, k, l ;
In[6]:= MatrixForm mat1
Out[6]//MatrixForm=
a b c
d e f
h k l
In[7]:= MatrixForm transpose mat1
Out[7]//MatrixForm=
a d h
b e k
c f l
Note this same computation could be performed with what is referred to as a struc-
tured iteration using Table.
In[8]:= transposeStruc mat_ :
Module
matA Table 0, n Length mat , m Length mat 1 ,
Table matA i, j mat j, i , i, m , j, n
In[9]:= transposeStruc mat1 MatrixForm
Out[9]//MatrixForm=
a d h
b e k
c f l
a. Create the function reverse[vec], which reverses the elements in the list vec.
b. Create a function rotateRight[vec,n], where vec is a vector and n is a
(positive or negative) integer.
130 An Introduction to Programming with Mathematica
c. Create a procedural implementation of rotateRows, which could be defined in
this functional way:
In[10]:= rotateRows[mat_] := Map[(rotateRight[mat[[#]], #-1])&,
Range[1, Length[mat]]]
That is, it rotates the ith row of mat i 1 places to the right.
d. Create a procedural function rotateRowsByS, which could be defined in this
functional way:
In[11]:= rotateRowsByS mat_, S_ ; Length mat Length S :
Map rotateRight mat #1 , S #1 & , Range 1, Length mat
That is, it rotates the ith row of matA by the amount S[[i]].
e. Create a function compress[lisA, lisB], where lisA and lisB are lists of equal
length, and lisB contains only Boolean values (False and True), selects out of
lisA those elements corresponding to True in lisB. For example, the result of
compress[{a,b,c,d,e},{True,True,False,False,True}] should
be {a,b,e}. To know what size list to create, you will first need to count the
occurrences of True in lisB.
5.3 Flow control
Conditional functions
In this section we will look at functions that control the flow of execution of an evaluation.
Perhaps the simplest and easiest to understand of these class of functions is the If state-
ment. Here is a rather simplistic implementation of the absolute value function, using If.
In[1]:= abs x_ : If x 0, x, x
In[2]:= abs 4
Out[2]= 4
The If function takes three arguments: the first is a test; if the test evaluates to
True, then the second argument is evaluated; if the test evaluates to False, then the third
argument of the If is evaluated.
5 Procedural programming 131
If can also be used in conjunction with the higher-order functions discussed in
Chapter 4 to achieve greater flexibility. For example, abs can now be mapped over a list of
numbers.
In[3]:= Map abs, 2, 1, 0, 1, 2
Out[3]= 2, 1, 0, 1, 2
By default, this function will not automatically map across lists.
In[4]:= abs 2, 1, 0, 1, 2
Out[4]= If 2, 1, 0, 1, 2 0, 2, 1, 0, 1, 2 , 2, 1, 0, 1, 2
If you want abs to behave like many of the built-in functions and automatically map
across lists when they are given as the argument to abs, you need to make the function
Listable as described in Sections 2.4 and 4.2.
In[5]:= SetAttributes abs, Listable
In[6]:= abs 2, 1, 0, 1, 2
Out[6]= 2, 1, 0, 1, 2
Here are some additional examples using If. Given a list, the following function
adds 1 to all the numeric quantities occurring in it.
In[7]:= incrementNumbers lis_ : Map If NumericQ #1 , # 1, # &, lis
In[8]:= incrementNumbers 4, f, 6.1 I,
Out[8]= 5, f, 7.1 , 1
Here is a function that divides 100 by every number in a numerical list, except 0s.
In[9]:= divide100By lis_ : Map If # 0, #,
100
#
&, lis
In[10]:= divide100By 5, , 0
Out[10]= 20,
100
, 0
Here is a function to remove consecutive occurrences of the same value.
In[11]:= removeRepetitions lis_ :
Fold If #2 Last #1 , #1, Append #1, #2 &,
First lis , Rest lis
In[12]:= removeRepetitions 0, 1, 1, 2, 2, 2, 1, 1
Out[12]= 0, 1, 2, 1
132 An Introduction to Programming with Mathematica
As a final example of If, the function applyChar takes a list as an argument. This
list must contain, first, a character, which must be one of "+", "-", "*", or "/"; that
character must be followed by all numbers. applyChar applies the function named by the
character to the elements of the rest of the list.
In[13]:= applyChar lis_ : Module op First lis , nums Rest lis ,
If op " ", Apply Plus, nums ,
If op " ", Apply Subtract, nums ,
If op " ", Apply Times, nums ,
If op " ", Apply Divide, nums ,
Print "Bad argument to applyChar"
In[14]:= applyChar " ", 1, 2, 3, 4
Out[14]= 10
(Recall the Module function, which permits us to introduce local variables. In this case, it
saves us from having to write First[lis] and Rest[lis] several times each.)
Even though the argument list in applyChar must contain one of the four operators as its
first element, it is still best to check for it explicitly; otherwise, if the condition is ever
violated, the results may be very mysterious. We have used the Print function, which
prints all of its arguments (of which it can have an arbitrary number) and then skips to a
new line.
In[15]:= applyChar "^", 2, 5, 10
Bad argument to applyChar
Notice that what we have in this code is several nested Ifs, each occurring in the
false part of the previous one. Thus, the structure of the computation is a sequence of tests
of predicates until one is found to be true, at which point a result can be computed. Such a
sequence of cascading If statements can get quite long, and the indentation can become
unmanageable, so it is conventional to violate the usual rule for indenting If expressions
and indent this type of structure as follows:
If cond
1
, result
1
,
If cond
2
, result
2
,
If cond
n
, result
n
,
result
n 1
] ]]
Conditional definitions can be written using another construct in Mathematica, the
Condition operator, /;. For example, the abs function can be entered (using several
definitions) as follows:
5 Procedural programming 133
In[16]:= Clear abs
In[17]:= abs x_ : x ; x 0
In[18]:= abs x_ : x ; x 0
The first definition should be interpreted as abs[x] is equal to x whenever (or under the
condition that) x is greater than or equal to 0 and the second definition as abs[x] is
equal to the opposite of x whenever x is less than 0.
The conditions on the right-hand side of the rules can, in fact, be entered on the
left-hand side of these definitions as follows:
In[19]:= abs x_ ; x 0 : x
In[20]:= abs x_ ; x 0 : x
This last notation has the advantage of preventing the right-hand side of our definitions
from being evaluated whenever the pattern on the left does not match.
In[21]:= abs 4
Out[21]= 4
In[22]:= abs z
Out[22]= abs z
This use of multiple rules associated with the symbol abs is a very useful and powerful
means of associating rules with symbols under user-defined conditions and we turn to it
next.
Multiclause definitions
The abs function defined above is fine for integers and real number arguments, but, since
the complex numbers cannot be ordered, the initial test comparing a complex number
argument with 0 will fail.
In[23]:= abs 3 4 I
GreaterEqual::nord :
Invalid comparison with 3 4 attempted. More
Less::nord : Invalid comparison with 3 4 attempted. More
Out[23]= abs 3 4
134 An Introduction to Programming with Mathematica
We can solve this problem by providing an additional definition for abs.
In[24]:= Clear abs ;
abs x_ : Sqrt Re x
2
Im x
2
; x Complexes;
abs x_ : x ; x 0
abs x_ : x ; x 0
The test as the first argument of If on the right-hand side checks to see if x is an
element of the domain of complex numbers and, if it is, then re x
2
im x
2
is com-
puted. If x is not complex, nothing is done, but then the other definition for abs will be
invoked.
In[28]:= abs 3 4 I
Out[28]= 5
In[29]:= abs 3
Out[29]= 3
The condition itself can appear on the left-hand side of the function definition, as
part of the pattern match. Here is a slight variation on the abs definition.
In[30]:= Clear abs
abs x_ : If x 0, x, x
abs x_ ; x Complexes : Sqrt Re x
2
Im x
2
In[33]:= abs 3 4 I
Out[33]= 5
In[34]:= abs 3
Out[34]= 3
We may want to add an additional rule for symbols.
In[35]:= abs x_ ; Head x Symbol : x
In[36]:= abs z
Out[36]= z
Such a definition is called a multiclause definition. In this case we have associated three
rules with abs; two are rather specific and will only be applied if the argument to abs
passes the conditions specified. If neither of those conditions are met, then the most
general rule (the one with no conditions on x) will be used.
5 Procedural programming 135
Which and Switch
Recall the earlier definition of applyChar defined using cascading Ifs.
In[37]:= applyChar lis_ : Module op First lis , nums Rest lis ,
If op " ", Apply Plus, nums ,
If op " ", Apply Subtract, nums ,
If op " ", Apply Times, nums ,
If op " ", Apply Divide, nums ,
Print "Bad argument to applyChar"
Needless to say, this is a little difficult to read and figure out which clause goes with
which If. Fortunately, cascaded Ifs are so common that Mathematica provides a more
direct way of writing them, using the function Which.
Which cond
1
, result
1
,
cond
2
, result
2
,
cond
n
, result
n
,
True, result
n 1
]
This has exactly the same effect as the cascaded If expression above: it tests each
condition in turn, and, when it finds an i such that cond
i
is true, it returns result
i
as the
result of the Which expression itself. If none of the conditions turns out to be true, then it
will test the final condition, namely the expression True, which always evaluates to true,
and it will then return result
n 1
.
applyChar can now be written more neatly.
In[38]:= applyChar lis_ : Module op First lis , nums Rest lis ,
Which op " ", Apply Plus, nums ,
op " ", Apply Subtract, nums ,
op " ", Apply Times, nums ,
op " ", Apply Divide, nums ,
True, Print "Bad argument to applyChar"
One last form deserves mention. Our use of the Which command is still quite
special, in that it consists of a simple sequence of comparisons between a variable and a
constant. Since this is also a common form, Mathematica again provides a special function
for it, called Switch.
136 An Introduction to Programming with Mathematica
Switch[expr,
pattern
1
, result
1
,
pattern
2
, result
2
,
pattern
n
, result
n
,
_, result
n 1
]
This evaluates expr and then checks each pattern, in order, to see whether expr
matches; as soon as expr matches one, say pattern
i
, it returns the value of result
i
. Of course,
if none of the patterns pattern
1
, , pattern
n
matches, the _ certainly will.
If all the patterns happen to be constants, the Switch expression is equivalent to the
following Which expression.
Which[expr == pattern
1
, result
1
,
expr == pattern
2
, result
2
,
expr == pattern
n
, result
n
,
True, result
n 1
]
Here, then, is our final version of applyChar.
In[39]:= applyChar lis_ : Module op First lis , nums Rest lis ,
Switch op,
" ", Apply Plus, nums ,
" ", Apply Subtract, nums ,
" ", Apply Times, nums ,
" ", Apply Divide, nums ,
_, Print "Bad argument to ApplyChar"
Notice that Switch uses the blank character, _, for the final, or default case, just as
Which uses the always-true expression True. We will have much more to say about
patterns and pattern matching in Chapter 6.
5 Procedural programming 137
Piecewise
Several of the functions we created in previous sections could be caste as piecewise-defined
functions. Although technically not a procedural construct, Piecewise (new in Version
5.1) is designed specifically for such problems. The syntax is
Piecewise e
1
, c
1
, , e
n
, c
n
which outputs e
1
if c
1
is true, e
2
if c
2
is true, , e
n
if c
n
is
true, and 0 otherwise (the default).
So, for example, here is the definition for the absolute value function given as a
piecewise object.
In[40]:= abspw x_ : Piecewise x, x 0 , x, x 0
Piecewise objects display as you would expect in traditional mathematical notation.
In[41]:= abspw x
Out[41]=
x x 0
x x 0
Furthermore, Piecewise is fully integrated with the algebraic, symbolic, and graphical
functions in Mathematica and so is preferable to other approaches.
In[42]:= Integrate abspw x , x, 1, 1
Out[42]= 1
In[43]:= D abspw x , x
Out[43]=
1 x 0
1 x 0
Indeterminate True
In[44]:= Plot abspw x , x, 2, 2 ;
2 1 1 2
0.5
1
1.5
2
Notice that the definition of the absolute value function given in terms of condition-
als is not fully supported by many of the built-in functions.
In[45]:= Clear abs
In[46]:= abs x_ : x ; x 0
138 An Introduction to Programming with Mathematica
In[47]:= abs x_ : x ; x 0
In[48]:= Integrate abs x , x, 1, 1
Out[48]=
1
1
abs x x
In[49]:= D abs x , x
Out[49]= abs x
Argument checking
Often, when we write functions, we know ahead of time that the definitions we give them
are valid only for certain kinds of inputs. For example, the following definition for the
factorial function only makes sense for positive integers.
In[50]:= fact 0 1;
fact n_ : n fact n 1
In[52]:= fact 5
Out[52]= 120
If we were to give fact an argument that was not a positive integer, the recursion could
run away from us.
In[53]:= fact 3.4
$RecursionLimit::reclim :
Recursion depth of 256 exceeded. More
Out[53]= 2.729671867921455 10
494
Hold fact 250.6 1
Conditionals are a convenient way of checking that the arguments to our functions
pass some criteria. For example, there are several ways that we could make the fact
function valid only under the condition that its argument is a positive integer. Here is how
we might approach it using the If construct to test that n passes the appropriate criteria.
In[54]:= Clear fact
In[55]:= fact 0 1;
In[56]:= fact n_ : If IntegerQ n && n 0, n fact n 1
In[57]:= fact 5 , fact 3 , fact 2.4
Out[57]= 120, Null, Null
5 Procedural programming 139
We see that the function works fine for positive integers, but since we did not give an
alternative condition to the If function, nothing is returned (technically Null is returned)
when the test condition fails.
Let us define a message that will be output in the case that the argument to fact
fails the positive integer test.
In[58]:= fact::noint "Argument `1` is not a positive integer.";
We then use Message as the third argument to our If, so that when the condition
fails the message will be triggered. Essentially Message messname, e
1
, e
2
, prints
using StringForm messg, e
1
, e
2
, , where messg is the value of the message name
and the e
i
are substituted in for any expressions of the form `i`. In the above example, the
message name is noint and its value is the string beginning with "Argument...". In
this example, the value of n will be substituted into the string where the `1` occurs.
In[59]:= fact n_ : If IntegerQ n && n 0,
n fact n 1 ,
Message fact::noint, n
In[60]:= fact 3
fact::noint : Argument 3 is not a positive integer.
Of course, there are a variety of ways of using conditionals to do argument checking.
Here are three more implementations, without the messaging.
In[61]:= fact1 0 1;
fact1 n_ : n fact1 n 1 ; IntegerQ n && n 0
In[63]:= fact1 5 , fact1 2.4
Out[63]= 120, fact1 2.4
In[64]:= fact2 0 1;
fact2 n_ ; IntegerQ n && n 0 : n fact2 n 1
In[66]:= fact2 5 , fact2 2.4
Out[66]= 120, fact2 2.4
In[67]:= fact3 0 1;
fact3 n_?IntegerQ ; n 0 : n fact3 n 1
In[69]:= fact3 5 , fact3 2.4
Out[69]= 120, fact3 2.4
140 An Introduction to Programming with Mathematica
Summary
When writing a function whose result must be computed differently, depending upon the
values of its arguments, you have a choice:
1. Use a multiclause definition, where the conditions are optional, and may appear
after the right-hand sides.
f pattern
1
_ /; cond
1
:= rhs
1
f pattern
n
_ /; cond
n
:= rhs
n
2. Use a single-clause definition with a conditional expression.
f[x_] := If cond
1
, rhs
1
,
If cond
n
, rhs
n
,
rhs
n 1
]
In the latter case, if n is greater than two, use the equivalent Which expression; and if all
conditions have the form x == const
i
, for a given variable x and some constants const
i
, use
the Switch function.
The next section contains several applications that use various combinations of the
procedural constructs we have learned in this chapter.
Exercises
1. Write the function signum[x] which, when applied to an integer x, returns 1, 0,
or 1, according as x is less than, equal to, or greater than, 0. Write it in three ways:
using three clauses, using a single clause with If, and using a single clause with
Which.
2. Extend signum from Exercise 1 to apply to both integers and reals; again, write it in
three ways (though you may use more than three clauses for the multiclause version).
3. Write applyChar in multiclause form, using pattern matching on the first element
of its argument.
5 Procedural programming 141
4. Use If in conjunction with Map or Fold to define the following functions:
a. In a list of numbers, double all the positive numbers, but leave the negative
numbers alone.
b. remove3Repetitions is like removeRepetitions except that it only alters
three or more consecutive occurrences, changing them to two occurrences; if
there are only two occurrences to begin with, they are left alone. For example,
remove3Repetitions[{0,1,1,2,2,2,1}] will return {0,1,1,2,2,1}.
c. Add the elements of a list in consecutive order, but never let the sum go below 0.
In[1]:= positiveSum 5, 3, 13, 7, 3, 2
Out[1]= 6
Since the 13 caused the sum to go below 0, it was instead put back to 0 and the
summation continued from there.
5. Using NestWhileList, write a function CollatzSequence[n] that produces
the Collatz sequence for any positive integer n. The Collatz sequence is generated as
follows: starting with a number n, if it is even, then output
n
2
; if n is odd, then output
3 n 1. Iterate this process while n 1.
5.4 Examples
Sieve of Eratosthenes
One of the oldest algorithms in the history of computing is the Sieve of Eratosthenes.
Named after the famous Greek astronomer Eratosthenes (ca. 276 ca. 194 BC), this
method is used to find all prime numbers below a given number n. The great feature of
this algorithm is that it finds prime numbers without doing any divisions an operation
that took considerable skill and concentration before the introduction of the Arabic
numeral system. In fact, its only operations are addition and component assignment.
The algorithm can be summarized as follows: to find all the prime numbers less than
an integer n:
create a list of the integers 1 through n
starting with p 2, cross out all multiples of p
142 An Introduction to Programming with Mathematica
increment p (that is, add 1 to p) and cross out all multiples of p
repeat the previous two steps until p n .
You should convince yourself that the numbers that are left after all the crossings out
are in fact the primes less than n. This algorithm lends itself very well to a procedural
approach, so let us walk through the steps.
We will use a For structure for this problem. The syntax is For[start, test, incr,
body], where start will first be evaluated (initializing values), and then incr and body will be
repeatedly evaluated until test fails.
1. Let lis be a list containing all the integers between 1 and n.
In[1]:= n 20;
lis Range n
Out[2]= 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20
2. Let p 2. Repeat the following two steps:
Starting at position 2p, cross out every pth value in lis. We will assign 1 to
lis at positions 2p, 3p, and the 1 will represent a crossed out value.
In[3]:= p 2;
Do lis i 1, i, 2 p, n, p
In[5]:= lis
Out[5]= 1, 2, 3, 1, 5, 1, 7, 1, 9, 1, 11, 1, 13, 1, 15, 1, 17, 1, 19, 1
While p n , increment p by 1, until lis[[p]] is not 1, or until p n 1.
In[6]:= n 20;
lis Range n ;
For p 2,
p 1 && p Floor Sqrt n ,
p ,
Do lis i 1, i, 2 p, n, p
3. The non-1s in lis are all the prime numbers less than or equal to n.
In[9]:= DeleteCases lis, 1
Out[9]= 2, 3, 5, 7, 11, 13, 17, 19
5 Procedural programming 143
Let us put these steps together in our function Sieve.
In[10]:= Clear n, p, lis
In[11]:= Sieve n_Integer : Module lis Range n , p ,
For p 2,
p 1 && p Floor Sqrt n ,
p ,
Do lis i 1, i, 2 p, n, p ;
DeleteCases lis, 1
Here are a few simple tests to check the correctness of our function. First we check that
Sieve produces the correct number of primes less than a large integer.
In[12]:= Length Sieve 10
5
Out[12]= 9592
The built-in PrimePi[x] gives the number of primes x less than or equal to x.
In[13]:= PrimePi 10
5
Out[13]= 9592
Next we do some simple timing tests to check the efficiency of this algorithm against the
built-in functions that are optimized for this task.
In[14]:= Sieve 10
6
; Timing
Out[14]= 13.62 Second, Null
In[15]:= Timing Table Prime i , i, 10
6
;
Out[15]= 5.648 Second, Null
In[16]:= Timing Map Prime, Range 10
6
;
Out[16]= 5.628 Second, Null
For numbers in this range (less than about 10
6
), sieving is fairly efficient its speed is
within an order of magnitude of the built-in algorithms. But, beyond this range, it does
tend to bog down and it would be best to consider specialized algorithms that are asymptoti
cally fast (for large integers, PrimePi uses an algorithm due to Lagarias, Miller, and
Odlyzko that is based on estimates of the density of primes).
144 An Introduction to Programming with Mathematica
Classifying points
Quadrants in the Euclidean plane are conventionally numbered counterclockwise from
quadrant 1 (x and y positive) to quadrant 4 (x positive, y negative). The function point
Loc[{x,y}] will compute the classification of point x, y , according to Table 5.1.
Point Classification
0, 0 0
y 0 on the x axis 1
x 0 on the y axis 2
Quadrant 1 1
Quadrant 2 2
Quadrant 3 3
Quadrant 4 4
Table 5.1: Quadrant classification
We will use this problem to illustrate the features covered in this chapter, by giving a
number of different solutions, using multiclause function definitions with predicates,
single-clause definitions with If and its relatives, and combinations of the two.
Perhaps the first solution that suggests itself is one that uses a clause for each of the
cases above.
In[17]:= pointLoc 0, 0 : 0
pointLoc x_, 0 : 1
pointLoc 0, y_ : 2
pointLoc x_, y_ : 1 ; x 0 && y 0
pointLoc x_, y_ : 2 ; x 0 && y 0
pointLoc x_, y_ : 3 ; x 0 && y 0
pointLoc x_, y_ : 4 ; x 0 && y 0
It is a good idea to include the last condition as a comment, rather than as a condi-
tion in the code, because Mathematica would not realize that the condition has to be true at
that point and would check it anyway.
We will use the following list of points as our test cases.
In[24]:= pts
0, 0 , 4, 0 , 0, 1.3 , 2, 4 , 2, 4 , 2, 4 , 2, 4 ;
In[25]:= Map pointLoc, pts
Out[25]= 0, 1, 2, 1, 2, 3, 4
5 Procedural programming 145
Translated directly to a one-clause definition using If, this becomes:
In[26]:= pointLoc x_, y_ :
If x 0 && y 0, 0,
If y 0, 1,
If x 0, 2,
If x 0 && y 0, 1,
If x 0 && y 0, 2,
If x 0 && y 0, 3, 4
In[27]:= Map pointLoc, pts
Out[27]= 0, 1, 2, 1, 2, 3, 4
Actually, a more likely solution here uses Which.
In[28]:= pointLoc x_, y_ : Which
x 0 && y 0, 0,
y 0, 1,
x 0, 2,
x 0 && y 0, 1,
x 0 && y 0, 2,
x 0 && y 0, 3,
True x 0&&y 0 , 4
In[29]:= Map pointLoc, pts
Out[29]= 0, 1, 2, 1, 2, 3, 4
In[30]:= pointLoc 5, 9
Out[30]= 3
All of our solutions so far suffer from a certain degree of inefficiency, because of
repeated comparisons of a single value with 0. Take the last solution as an example, and
suppose the argument is ( 5, 9). It will require five comparisons of 5 with 0 and three
comparisons of 9 with 0 to obtain this result. Specifically:
1. evaluate x==0; since it is false, the associated y== 0 will not be evaluated, and
we next
2. evaluate y==0 on the following line; since it is false,
3. evaluate x==0 on the third line; since it is false,
4. evaluate x>0 on next line; since it is false, the associated y> 0 will not be evalu-
ated, and we next,
5. evaluate x<0 on the next line; since it is true, we do,
146 An Introduction to Programming with Mathematica
6. the y>0 comparison, which is false, so we next,
7. evaluate x<0 on the next line; since it is true, we then evaluate y< 0, which is
also true, so we return the answer 3.
How can we improve this? By nesting conditional expressions inside other condi-
tional expressions. In particular, as soon as we discover that x is less than, greater than, or
equal to 0, we should make maximum use of that fact without rechecking it. That is what
the following pointLoc function does.
In[31]:= pointLoc x_, y_ :
Which x 0, If y 0, 0, 2 ,
x 0, Which y 0, 1,
y 0, 4,
True y 0 , 1 ,
True, x 0
Which y 0, 3,
y 0, 2,
True y 0 , 1
Let us count up the comparisons for 5, 9 this time: (i) evaluate x ==0; since it is
false, we next, (ii) evaluate x> 0; since it is false, we go to the third branch of the Which,
evaluate True, which is, of course, true; then, (iii) evaluate y<0, which is true, and we
return 3. Thus, we made only three comparisons a substantial improvement.
When pattern matching is used, as in our first, multiclause solution, efficiency
calculations are more difficult. It would be inaccurate to say that Mathematica has to
compare x and y to 0 to tell whether the first clause applies; what actually happens is more
complex. What is true, however, is that it will do the comparisons indicated in the last four
clauses. So, even if we discount the first three clauses with argument 5, 9 , some extra
comparisons are done. Specifically: (i) the comparison x> 0 is done; then, (ii) x <0 and (iii)
y >0; then, (iv) x <0 and (v) y< 0. This can be avoided by using conditional expressions
within clauses.
In[32]:= pointLoc 0, 0 : 0
pointLoc x_, 0 : 1
pointLoc 0, y_ : 2
pointLoc x_, y_ : If x 0, 2, 1 ; y 0
pointLoc x_, y_ : If x 0, 3, 4 ; y 0
Now, no redundant comparisons are done. For 5, 9 , since y >0 fails, the fourth
clause is not used, so the x> 0 comparison in it is not done. Only the single x <0 compari-
son in the final clause is done, for a total of two comparisons.
5 Procedural programming 147
Having done all these versions of pointLoc, we would be remiss if we did not
remind the reader of a basic fact of life in programming: your time is more valuable than
your computers time. You should not be worrying about how slow a function is until there
is a demonstrated need to worry. Far more important is the clarity and simplicity of the
code, since this will determine how much time you (or another programmer) will have to
spend when it comes time to modify it. In the case of pointLoc, we would argue that we
got lucky and found a version (the final one) that wins on both counts (if only program-
ming were always like that!).
Finally, a technical, but potentially important, point: Not all of the versions of
pointLoc work exactly the same. The integer 0, as a pattern, does not match the real
number 0.0, since they have different heads. Thus, using the last version as an example,
pointLoc[{0.0,0.0}] returns 4.
In[37]:= pointLoc 0.0, 0.0
Out[37]= 4
See Section 6.2 for a discussion of alternatives, which allows us to efficiently deal
with these various cases.
Exercises
1. Using an If function, write a function gcd[m,n] that implements the Euclidean
algorithm (see Exercise 10 of Section 5.2) for finding the greatest common divisor of
m and n.
2. Use Piecewise to define the pointLoc function given in this section.
3. Extend pointLoc to three dimensions, following this rule: for point (x, y, z), if
z 0, then give the same classification as (x, y), with the exception that zero is treated
as a positive number (so the only classifications are 1, 2, 3, and 4); if z 0, add 4 to
the classification of (x, y) (with the same exception). For example, (1, 0, 1) is in octant
1, and (0, 3, 3) is in octant 8. pointLoc should work for points in two or three
dimensions.
148 An Introduction to Programming with Mathematica
6 Rule-based programming
The use of rules to transform expressions from one form to another is one of the most
powerful and useful tools available in the Mathematica programming language. The
thousands of rules built in to Mathematica can be expanded limitlessly through the
creation of user-defined rules. Rules can be created to change the form of expressions,
to filter data based on some criteria, and can be set up to apply to broad classes of
expressions or limited to certain narrow domains through the use of appropriate
pattern matching techniques. These rules can perform many of the tasks normally
associated with more traditional programming constructs, such as we have discussed in
the chapters on procedural and functional programming. In this chapter we will
discuss the structure and application of rules to common programming tasks and look
at their application in some concrete examples.
6.1 Introduction
Users of Mathematica typically first encounter rules as the output to many built-in func-
tions. For example, the Solve function returns its solutions as a list of rules.
In[1]:= soln Solve a x
2
b x c 0, x
Out[1]= x
b b
2
4 a c
2 a
, x
b b
2
4 a c
2 a
They are also used to specify options for functions and replacement rules in many kinds of
computations.
In[2]:= FactorInteger 5, GaussianIntegers True
Out[2]= , 1 , 1 2 , 1 , 2 , 1
In[3]:= StringReplace "acgttttccctgagcataaaaacccagcaatacg",
"ca" "CA", "tt" "TT"
Out[3]= acgTTTTccctgagCAtaaaaaccCAgCAatacg
When you define a function via an assignment such as the function f below, you are
defining a rule that says whenever f is given an argument, it should be replaced with that
argument squared. This rule will be applied automatically whenever you evaluate
f[anything].
In[4]:= f x_ : x
2
In[5]:= f bob
Out[5]= bob
2
On the other hand, you can set up rules to be applied on demand by using the
replacement operator ReplaceAll, written in shorthand notation as /. . These rules can
then be used to transform one expression into another. For example, the following rule is
used to extract the real and imaginary parts of a complex number and convert it to an
ordered pair.
In[6]:= 3 4 . Complex a_, b_ a, b
Out[6]= 3, 4
This rule reverses the elements in each ordered pair.
In[7]:= , 1 , , 2 , , 3 . x_, y_ y, x
Out[7]= 1, , 2, , 3,
And here is a rule that turns each of the superscripts in the polynomial below into a
subscript.
In[8]:= poly Factor 1 x
11
Out[8]= 1 x 1 x x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
x
10
In[9]:= ToBoxes poly . SuperscriptBox SubscriptBox DisplayForm
Out[9]//DisplayForm=
1 x 1 x x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
x
10
Rule-based programming is such a useful construct for manipulating lists and arbi-
trary expressions that no user of Mathematica should be without a working knowledge of
this paradigm. This chapter gives a thorough introduction to pattern matching and then
proceeds to rule-based programs, many of which were introduced earlier as functional or
procedural programs.
150 An Introduction to Programming with Mathematica
6.2 Patterns
Blanks
When you make an assignment to a symbol, like x=4, you are making a rule that should be
applied to the literal expression x. Loosely speaking, the rule says, replace x with the value
4 whenever x is encountered. We have seen that you can also define functions of one or
more arguments that allow you to substitute arbitrary expressions for those arguments.
In[1]:= f x_ : x 1
The left-hand side of the above assignment is a pattern. It contains a blank (underscore)
which can stand for any expression, not just the literal expression x.
In[2]:= f
Out[2]= 1
In[3]:= f bob
Out[3]= 1 bob
While any specific expression can be pattern matched (because any object must
match itself), we usually want to be able to pattern match large classes of expressions (for
example, a sequence of expressions or expressions having Integer as the head). For this
purpose, patterns are defined as expressions that may contain blanks. That is to say, a
pattern may contain one of the following: a single (_) blank, a double (__) blank, or a triple
(___) blank.
We will find it useful to identify the pattern to which an expression is matched (for
example, on the left-hand side of a function definition) so that it can be referred to by
name elsewhere (for example, on the right-hand side of the function definition). A pattern
can be labeled by name_, or name__, or name___ (which can be read as a pattern called
name) and the labeled pattern will be matched by the same expression that matches its
unlabeled counterpart. The matching expression is given the name used in the labeled
pattern.
You can see what class of expressions match a given pattern by using MatchQ. For
example, this tests whether the symbol bob matches any expression because the single
underscore can stand for any Mathematica expression.
In[4]:= MatchQ bob, _
Out[4]= True
6 Rule-based programming 151
This tests whether the number 3.14 matches any expression with head Real.
In[5]:= MatchQ 3.14, _Real
Out[5]= True
Of course 3.14 does not match any expression with head Integer.
In[6]:= MatchQ 3.14, _Integer
Out[6]= False
If you want to look at a list of expressions and see which ones match a particular
pattern, you can use Cases. Cases[expr,patt] outputs those elements of expr that match
the pattern patt. For example, the only two elements of the list below that have head
Integer are 3 and 17. Notice the fourth element is a string.
In[7]:= Cases 3, 3.14, 17, "3", 4 5 I , _Integer
Out[7]= 3, 17
In[8]:= Cases 3, 3.14, 17, "3", 4 5 I , _String
Out[8]= 3
Remember that the OutputForm of strings is to display without the quote characters. If
you want to check the structure of this last output, use FullForm or check its Head.
In[9]:= FullForm %
Out[9]//FullForm=
List "3"
Here are some additional examples of pattern matching. This next example matches
all those expressions with head g.
In[10]:= Cases g x , f x , g h x , g a, 0 , _g
Out[10]= g x , g h x , g a, 0
In the following example, the pattern {p_,q_} matches any list with two elements.
In[11]:= Cases a, b , , 1, 0 , c, d, 3 , p_, q_
Out[11]= a, b , 1, 0
Let us clear symbols we no longer need.
In[12]:= Clear f
152 An Introduction to Programming with Mathematica
Sequence pattern matching
A sequence consists of a number of expressions separated by commas. For example, the
arguments of expressions are written as sequences.
A double blank (BlankSequence) represents a sequence of one or more expres-
sions and __h represents a sequence of one or more expressions, each of which has head h.
An expression that matches a blank will also match a double blank.
A triple blank (BlankNullSequence) represents a sequence of zero or more
expressions and ___h represents a sequence of zero or more expressions, each of which has
head h. An expression that matches a blank will also match a triple blank and a sequence
that matches a double blank pattern will also match a triple blank pattern.
The pattern {p__}, using two _ characters, matches any list containing one or more
elements.
In[13]:= Cases a, b , , 1, 0 , c, d, 3 , p__
Out[13]= a, b , 1, 0 , c, d, 3
The pattern {p___}, using three _ characters, matches any list containing zero or more
elements.
In[14]:= Cases a, b , , 1, 0 , c, d, 3 , p___
Out[14]= a, b , , 1, 0 , c, d, 3
A list {a,b,c} is matched by the pattern _ (using Blank), as well as by List[__]
(using BlankSequence) and List[___] (with BlankNullSequence). However, the
list {a,b,c} is not matched by the pattern List[_] (a list of one expression) because for
the purposes of pattern matching, a sequence is not an expression.
In[15]:= MatchQ a, b, c , _
Out[15]= True
In[16]:= MatchQ a, b, c , _
Out[16]= False
Here are some other examples of successful pattern matches.
In[17]:= MatchQ a, b, c , __
Out[17]= True
In[18]:= MatchQ a, b, c , ___
Out[18]= True
6 Rule-based programming 153
In[19]:= MatchQ a, b, c , x__
Out[19]= True
In[20]:= MatchQ a, b, c , x___
Out[20]= True
In the last two examples above, the labels on the blanks do not affect the success or
failure of the pattern match.
In[21]:= MatchQ a, b, c , __
Out[21]= True
The labels simply serve to identify different parts of the expression. For example, in
MatchQ[{a,b,c},x_], x names the list {a,b,c}, but in Match
Q[{a,b,c},{x___}], x names the sequence a,b,c which is quite different. This is
illustrated further in the section on conditional pattern matching.
Finally, note that the discussion about lists here applies equally to any function. For
example, the following returns True, with x naming the sequence a,b,c.
In[22]:= MatchQ Plus a, b, c , Plus x__
Out[22]= True
Example: Finding subsequences
As an example of sequence pattern matching, consider the problem of finding a particular
subsequence within a sequence of numbers. To simplify this problem, consider both the
sequence and the subsequence to be given as lists of numbers. As a concrete example, we
will find the positions at which the subsequence 3238 occurs in the digits of .
Here are the digits of . Initially, we will look at only 50 digits so we can easily
inspect the progress of our program.
In[23]:= pidigs First RealDigits N , 50 3
Out[23]= 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3,
2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0,
2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1
Here is our subsequence, given as a list of digits.
In[24]:= subseq 3, 2, 3, 8 ;
154 An Introduction to Programming with Mathematica
One approach to this problem is to partition the list of digits in pidigs into lists of
the same length as the list subseq, with overlapping sublists of offset 1. This means that
we will examine all length 4 sublists from pidigs.
In[25]:= p Partition pidigs, Length subseq , 1
Out[25]= 1, 4, 1, 5 , 4, 1, 5, 9 , 1, 5, 9, 2 ,
5, 9, 2, 6 , 9, 2, 6, 5 , 2, 6, 5, 3 ,
6, 5, 3, 5 , 5, 3, 5, 8 , 3, 5, 8, 9 , 5, 8, 9, 7 ,
8, 9, 7, 9 , 9, 7, 9, 3 , 7, 9, 3, 2 , 9, 3, 2, 3 ,
3, 2, 3, 8 , 2, 3, 8, 4 , 3, 8, 4, 6 , 8, 4, 6, 2 ,
4, 6, 2, 6 , 6, 2, 6, 4 , 2, 6, 4, 3 , 6, 4, 3, 3 ,
4, 3, 3, 8 , 3, 3, 8, 3 , 3, 8, 3, 2 , 8, 3, 2, 7 ,
3, 2, 7, 9 , 2, 7, 9, 5 , 7, 9, 5, 0 , 9, 5, 0, 2 ,
5, 0, 2, 8 , 0, 2, 8, 8 , 2, 8, 8, 4 , 8, 8, 4, 1 ,
8, 4, 1, 9 , 4, 1, 9, 7 , 1, 9, 7, 1 , 9, 7, 1, 6 ,
7, 1, 6, 9 , 1, 6, 9, 3 , 6, 9, 3, 9 , 9, 3, 9, 9 ,
3, 9, 9, 3 , 9, 9, 3, 7 , 9, 3, 7, 5 , 3, 7, 5, 1
Now we are ready for the pattern match. From the list p above, we are looking for
the positions of any sublist that matches {3,2,3,8}. The Position function takes as
its first argument, the expression from which we are trying to match. The second argu-
ment is the pattern to match. We will use BlankNullSequence (___) on either side of
our subsequence because zero or one or two expressions may occur before or after it in p.
In[26]:= Position p, Flatten ___, subseq, ___
Out[26]= 15
So the subsequence 3238 occurs starting at the 15th digit in the sequence given by
pidigs.
Finally, let us turn this into a function and test it on a much larger example. Note
that we use the pattern _List on both arguments so that FindSubsequence will only
match arguments that have head List. (In Exercise 5 at the end of this section, you are
asked to create a version of FindSubsequence that takes numbers instead of lists as its
arguments.)
In[27]:= FindSubsequence lis_List, subseq_List : Module p ,
p Partition lis, Length subseq , 1 ;
Position p, Flatten ___, subseq, ___
We store the first 100,000 digits of in the symbol pidigs.
In[28]:= pidigs First RealDigits N , 10
5
3 ;
6 Rule-based programming 155
We find that the subsequence {3,2,3,8} occurs at the following nine different positions
in the first 100,000 digits of .
In[29]:= FindSubsequence pidigs, 3, 2, 3, 8
Out[29]= 15 , 8990 , 20522 , 20756 ,
28130 , 41865 , 57208 , 86505 , 91936
The subsequence 31415 occurs once in the first 100,000 digits of starting at the
88,008th digit.
In[30]:= FindSubsequence pidigs, 3, 1, 4, 1, 5
Out[30]= 88008
Conditional pattern matching
Attaching a predicate
In addition to specifying the head of an expression, you can also match expressions against
predicate functions. If the blanks of a pattern are followed with ?test, where test is a predi-
cate, then a match is only possible if test returns True when applied to the entire
expression.
So, to match a class of expressions that have head h, you use _h. To match a class of
expressions that evaluate to True when the predicate pred is applied, use _?pred.
In[31]:= MatchQ 1, 2, 3 , _?ListQ
Out[31]= True
In[32]:= MatchQ 1, 2, 3 , _?NumberQ
Out[32]= False
Note that in the above example, even though the list {1,2,3} consists of numbers, it
does not match ?NumberQ because its head (List) does not pass the NumberQ test.
The pattern _?Negative matches any expression that passes the Negative test;
that is, it returns true when Negative is applied to it.
In[33]:= Cases 2, 7, 1.2, 0, 5 2 I , _?Negative
Out[33]= 2, 1.2
The following examples use a pure predicate function. In the first example, we are
asking if {a,b,c} has head List and if the length of {a,b,c} is greater than 2. Since it
passes both of these conditions, MatchQ returns True.
156 An Introduction to Programming with Mathematica
In[34]:= MatchQ a, b, c , _List? Length # 2 &
Out[34]= True
Even though the head of {a,b,c} is List, the condition below fails since the list has
length less than 4.
In[35]:= MatchQ a, b, c , _List? Length # 4 &
Out[35]= False
Note that when using a pure function in ?test, because of the precedence Mathemat-
ica gives to evaluating various quantities, it is necessary to enclose the entire function,
including the &, in parentheses. We have used test to place a constraint on the entire
expression.
Here is a simple application of attaching a predicate. This definition of the Fibonacci
function tests its argument to see that it is an integer (specifically, this tests that the head of
n is Integer).
In[36]:= f 1 f 2 1;
In[37]:= f n_?IntegerQ : f n 1 f n 2
Because of the predicate, f will not evaluate for noninteger arguments.
In[38]:= f 1.2
Out[38]= f 1.2
In[39]:= f 5 , f 10 , f 15
Out[39]= 5, 55, 610
We could also check that the arguments to f are both integral and positive.
In[40]:= Clear f
In[41]:= f 1 f 2 1;
In[42]:= f n_? IntegerQ && Positive : f n 1 f n 2
In[43]:= f 3
Out[43]= f 3
6 Rule-based programming 157
Attaching a condition
If part of a labeled pattern is followed with an expression such as /;condition, where
condition contains labels appearing in the pattern, then a match is possible only if condition
returns True. We use condition to place a constraint on the labeled parts of an expression.
The use of labels in condition is useful for narrowing the scope of a pattern match.
In[44]:= MatchQ[x^2, _^y_ /; EvenQ[y]]
Out[44]= True
In[45]:= MatchQ[x^2, _^y_ /; OddQ[y]]
Out[45]= False
We mentioned above that matching a list like {a,b,c} with the pattern x_ is
different from matching it with x___ because of the various expressions that are associated
with x.
In[46]:= MatchQ 4, 6, 8 , x_ ; Length x 4
Out[46]= False
In[47]:= MatchQ 4, 6, 8 , x___ ; Length x 4
Length::argx : Length called with
3 arguments; 1 argument is expected. More
Out[47]= False
In[48]:= MatchQ 4, 6, 8 , x___ ; Plus x 10
Out[48]= True
In the first example, x was associated with the entire list {4,6,8}; since
Length[{4,6,8}] is not greater than 4, the match failed. In the second example, x
became the sequence 4,6,8 so that the condition was Length[4,6,8]>4; but Length
can only have one argument, hence the error. In the last example, x was again associated
with 4,6,8, but now the condition was Plus[4,6,8]>10, which is perfectly legal, and
true.
In the following example, the pattern matches all those expressions that are between
2 and 5.
In[49]:= Cases 1, 2, 3, 4, 5, 6, 7, 8 , x_ ; 2 x 5
Out[49]= 3, 4
158 An Introduction to Programming with Mathematica
Let us try to recast the Fibonacci function example from the previous section in
terms of a conditional.
In[50]:= Clear f
In[51]:= f 1 f 2 1;
In[52]:= f n_ : f n 1 f n 2 ; IntegerQ n
Because of the predicate, f does not evaluate for noninteger arguments.
In[53]:= f 1.2
Out[53]= f 1.2
In[54]:= f 5 , f 10 , f 15
Out[54]= 5, 55, 610
Similarly, we can check that the arguments to f are both integral and positive.
In[55]:= Clear f
In[56]:= f 1 f 2 1;
In[57]:= f n_ : f n 1 f n 2 ; IntegerQ n && Positive n
In[58]:= f 3 , f 10
Out[58]= f 3 , 55
Note that you can alternatively put the condition inside the left-hand side of your
definition.
In[59]:= Clear f
In[60]:= f 1 f 2 1;
In[61]:= f n_ ; IntegerQ n && Positive n : f n 1 f n 2
In[62]:= f 15 , f 1.4 , f 4
Out[62]= 610, f 1.4 , f 4
Alternatives
A final type of pattern uses alternatives. Alternatives are denoted p
1
p
2
p
n
where the p
i
are independent patterns. This pattern will match an expression whenever any one of those
independent patterns match it.
6 Rule-based programming 159
In the following example, x^2 matches an expression which is either the symbol x
raised to a real number or the symbol x raised to an integer.
In[63]:= MatchQ[x^2, x^_Real | x^_Integer]
Out[63]= True
In this example, x^2 matches x raised to an expression which is either a real num-
ber or an integer.
In[64]:= MatchQ[x^2, x^(_Real | _Integer)]
Out[64]= True
Here the pattern matches any expression that has head Integer or Rational or
Real.
In[65]:= Cases 1, 3.1,
2
3
, x, 3 4 I, "Hello" ,
_Integer _Rational _Real
Out[65]= 1, 3.1,
2
3
As a final example, recall the function pointLoc from Section 5.4.
In[66]:= pointLoc 0, 0 : 0
pointLoc x_, 0 : 1
pointLoc 0, y_ : 2
pointLoc x_, y_ : If x 0, 2, 1 ; y 0
pointLoc x_, y_ : If x 0, 3, 4
The integer 0, as a pattern, does not match the real number 0.0, since they have different
heads.
In[71]:= Head 0 , Head 0.0
Out[71]= Integer, Real
Thus, using the above version of pointLoc, {0.0,0.0} returns 4, which is, of course,
wrong.
In[72]:= pointLoc 0.0, 0.0
Out[72]= 4
On the other hand, the single-clause versions using If and Which returned 0, because
0.0== 0 is true. How can we fix this? There are a number of possibilities. Perhaps the
simplest way is to change the rules involving zeroes by means of alternatives.
In[73]:= Clear pointLoc
160 An Introduction to Programming with Mathematica
In[74]:= pointLoc 0 0.0, 0 0.0 : 0
pointLoc x_, 0 0.0 : 1
pointLoc 0 0.0, y_ : 2
pointLoc x_, y_ : If x 0, 2, 1 ; y 0
pointLoc x_, y_ : If x 0, 3, 4
Now the several cases that led to inconsistencies in the previous versions are dealt with
properly.
In[79]:= pointLoc 0, 0.0
Out[79]= 0
In[80]:= pointLoc 1, 0
Out[80]= 1
String patterns
All of the pattern matching discussed in the previous sections extends to strings in a
very powerful manner. You might find it helpful to think of strings as a sequence of
characters and use the same general principles on these expressions as you do with lists.
Let us look at a few examples to try and make this concrete.
The expression {a,b,c,c,d,e} matches the pattern {__,s_,s_,__} because it
is a list that starts with a sequence of one or more elements, it contains an element
repeated once, and then ends with a sequence of one or more elements.
In[81]:= MatchQ a, b, b, c, d, e , __, s_, s_, __
Out[81]= True
If we now use a string instead of a list and StringMatchQ instead of MatchQ, we
get a similar result using the shorthand notation ~~ for StringExpression, which
essentially concatenates strings.
In[82]:= StringMatchQ "abbcde", __ s_ s_ __
Out[82]= True
In[83]:= "a" "b"
Out[83]= ab
In[84]:= FullForm HoldForm "a" "b"
Out[84]//FullForm=
HoldForm StringExpression "a", "b"
6 Rule-based programming 161
StringExpression is quite similar to StringJoin (both can be used to concatenate
strings) except that with StringExpression, you can concatenate nonstrings.
The next example also shows the similarity between the pattern matching that we
explored earlier and string patterns. Using Cases, we return all those expressions that
match the pattern _Symbol; that is, we pick out all those symbols from the list.
In[85]:= Cases 1, f, g, 6, x, t, 2, 5 , _Symbol
Out[85]= f, g, x, t
With the string "1fg6xt25" we can use StringCases whose second argument is a
pattern that represents a class of characters to match. For example, LetterCharacter
matches a single letter.
In[86]:= StringCases "1fg6xt25", LetterCharacter
Out[86]= f, g, x, t
You can match single digits with DigitCharacter.
In[87]:= StringCases "1fg6xt25", DigitCharacter
Out[87]= 1, 6, 2, 5
Starting in Version 5.1, you can use regular expressions to match string patterns.
Regular expressions in Mathematica follow a syntax very close to that of the Perl program-
ming language. This syntax is quite compact and powerful but it comes at the cost of
readability regular expressions tend to be quite cryptic to humans. As a result, we will
only cover a few examples of their use here and refer the interested reader to the Mathemat-
ica documentation on string patterns.
The regular expression 1.* will be matched by any string starting with 1, followed
by any character repeated zero or more times.
In[88]:= StringMatchQ "1a2b3c4d", RegularExpression "1. "
Out[88]= True
The regular expression \\d represents any digit 0 through 9.
In[89]:= StringCases "1a2b3c4d", RegularExpression "\\d"
Out[89]= 1, 2, 3, 4
In the following example, we use a regular expression to look for the pattern consist-
ing of the character "a" repeated one or more times, followed by the character "c",
followed by any character. The StringReplace function then replaces any expression
matching this pattern with a large, bold formatted expression. The "$0" is used to refer to
the matched pattern.
162 An Introduction to Programming with Mathematica
In[90]:= StringReplace "acgttttccctgagcataaaaacccagcaatacg",
RegularExpression "a..c." "\ \ \ StyleBox
\"$0\",FontSize 14,FontWeight \"Bold\" \ "
Out[90]= acgttttccctgagcataaaaacccagcaatacg
Exercises
1. Find as many patterns as possible that match the expression x^3 + y z.
2. Find as many pattern matches as possible for the following expression.
{5, erina, {}, "give me a break"}
3. Using both forms (predicate and condition), write down five conditional patterns
that match the expression {4,{a,b},"g"}.
4. In Exercise 10 of Section 5.2, we developed a procedural implementation of the
Euclidean algorithm for finding the greatest common divisor of two numbers. The
function given in the solutions does no argument checking and hence can give
erroneous output for arguments that are not integers. Rewrite the gcd function
given there so that it uses pattern matching to check that each of its two arguments
are integers.
5. The function FindSubsequence defined in this section suffers from the limitation
that the arguments lis and subseq must both be lists of numbers. Write another
definition of FindSubsequence that takes integers as its two arguments. So for
example, the following should work:
In[1]:= pi FromDigits RealDigits N Pi, 10
5
3 1 ;
In[2]:= FindSubsequence pi, 1415
Out[2]= 1 , 6955 , 29136 , 45234 , 79687 , 85880 , 88009
6. Write a function Collatz that takes an integer n as an argument and returns 3 n 1
if n is an odd integer and returns
n
2
if n is even. Your function Collatz should
attach a predicate to its argument to check whether it is even or odd.
7. Write the Collatz function from the above exercise, but this time attach a condi-
tion instead of a predicate. In addition, your condition should also check that the
argument to Collatz is positive.
6 Rule-based programming 163
8. Use alternatives to write a function abs[x] that, whenever x is an integer or a
rational, returns x if x 0, and x if x 0. Whenever x is complex, abs[x] should
return re x
2
im x
2
.
9. Create a function swapTwo[lis_List] that returns lis with its first two ele-
ments interchanged; for example, swapTwo[{a,b,c,d,e}] is {b,a,c,d,e}. If
lis has fewer than two elements, swapTwo just returns it. Write swapTwo using
three clauses: one for the empty list, one for one-element lists, and one for all other
lists. Then write it using two clauses: one for lists of length zero or one and another
for all longer lists.
10. Convert this definition to one that has no conditional parts (/;), but instead uses
pattern matching in the argument list:
f x_, y_ : x y ; IntegerQ x
f x_, y_ :
x 1 y ; Head x List && IntegerQ First x && y 1
11. Write a version of the HammingDistance function (described in Section 4.7) that
uses Cases instead of Select.
6.3 Transformation rules
Transformation rules are ubiquitous in Mathematica. They are used to represent solutions
to equations, as a means to specify options for functions, and they form the basis of most of
the algebraic manipulation in Mathematica. In this section we will look at how to use
pattern matching together with replacement rules to transform expressions based on these
rules.
A replacement rule is of the form pattern replacement or pattern replacement. Just
like traditional function definitions, the left-hand side of each of these rules matches an
expression and the right-hand side describes the transformation of that expression.
One of the most common uses for rules is in making substitutions of the form expr /.
rule. Any part of expr that matches the pattern in rule will be rewritten according to that
rule.
In[1]:= x y . y
Out[1]= x
164 An Introduction to Programming with Mathematica
A similar rule but using assignments would look like this:
In[2]:= f x_, y_ x y;
In[3]:= f x,
Out[3]= x
The main difference between the replacement rule and the assignment is that the
assignment will automatically be used whenever there is an appropriate pattern match
during evaluation. The expression f[x, ] matched the rule for f and the substitution
was performed automatically.
If you wish to restrict the use of a rule to a specific expression, you can use the
ReplaceAll function (shorthand notation /.) with the expression as the first argument
and a user-defined Rule or RuleDelayed function as the second argument. In standard
input form, the transformation rule (or local rewrite rule) appears immediately after the
expression, as the second argument to ReplaceAll.
In[4]:= x y . y
Out[4]= x
Here is the standard input form of the above.
In[5]:= ReplaceAll x y, Rule y,
Out[5]= x
When the Rule function is used with an expression, the expression itself is first
evaluated. Then both the left-hand side and right-hand side of the rule are evaluated, except
for those parts of the right-hand side that are held unevaluated by the Hold attribute.
Finally, everywhere that the evaluated left-hand side of the rule appears in the evaluated
expression, it is replaced by the evaluated right-hand side of the rule.
In[6]:= a, a . a Random
Out[6]= 0.474439, 0.474439
Using Trace, we can see the way the transformation rule works. Note in particular, that
the right-hand side of the rule is evaluated first.
In[7]:= Trace a, a . a Random
Out[7]= Random , 0.0883691 , a 0.0883691, a 0.0883691 ,
a, a . a 0.0883691, 0.0883691, 0.0883691
Just as in the case of assignments, there are immediate rules and delayed rules. In an
immediate rule (pattern replacement, with standard input form Rule[pattern, replace-
ment]), the replacement will be evaluated immediately. For delayed rules (pattern replace-
6 Rule-based programming 165
ment, with standard input form RuleDelayed[pattern, replacement]), the replacement is
only evaluated after the substitution is made.
In[8]:= a, a . a Random
Out[8]= 0.672823, 0.703154
Using Trace, we can see the way this transformation rule works.
In[9]:= Trace a, a . a Random
Out[9]= a Random , a Random , a, a . a Random ,
Random , Random , Random , 0.174287 ,
Random , 0.722288 , 0.174287, 0.722288
Transformation rules can be written using symbols.
In[10]:= a, b, c . List Plus
Out[10]= a b c
Transformation rules can also be written using labeled patterns.
In[11]:= 3, 4 , 7, 2 , 1, 5 . x_, y_ y, x
Out[11]= 4, 3 , 2, 7 , 5, 1
We can use multiple rules with an expression by enclosing them in a list.
In[12]:= a, b, c . c b, b a
Out[12]= a, a, b
A transformation rule is applied only once to each part of an expression (in contrast
to a rewrite rule) and multiple transformation rules are used in parallel. Hence, in the
above example, the symbol c is transformed into b but it is not further changed into a. In
order to apply one or more transformation rules repeatedly to an expression until the
expression no longer changes, the ReplaceRepeated function is used.
For example, the product of x and y is replaced by the sum of x and y, but this is
only done for the first such occurrence that matches.
In[13]:= a b c d . x_ y_ x y
Out[13]= a b c d
Using ReplaceRepeated, the rule is applied repeatedly until the expression no longer
changes.
In[14]:= a b c d . x_ y_ x y
Out[14]= a b c d
166 An Introduction to Programming with Mathematica
Let us now look at a few examples of problems that we solved earlier using a func-
tional style of programming but now solve them using a rule-based approach.
Example: Counting coins
Recall the pocket change example from Chapter 4 where a list of coins was given and a
function was constructed to count the value of the set of coins. Let us try to do the same
thing, but with a rule that gives the values of the coins.
In[15]:= coins p, p, q, n, d, d, p, q, q, p
Out[15]= p, p, q, n, d, d, p, q, q, p
Here are the values, given by a list of rules.
In[16]:= values p 1, n 5, d 10, q 25 ;
This replaces each coin by its value.
In[17]:= coins . values
Out[17]= 1, 1, 25, 5, 10, 10, 1, 25, 25, 1
And here is the value of the set of coins.
In[18]:= Apply Plus, coins . values
Out[18]= 104
Finally, here is a function that wraps up all these steps.
In[19]:= CountChange coins_List : Module values ,
values p 1, n 5, d 10, q 25 ;
Apply Plus, coins . values
In[20]:= CountChange p, q, q, n, d, d, p, q, q, d, d
Out[20]= 147
Example: Finding maxima
Our last example employs a sophisticated rewrite rule which demonstrates most of the
things discussed in this section: the repeated use of a transformation rule with delayed
evaluation, sequence patterns, and conditional pattern matching.
Recall the maxima function that we defined in Chapter 4, which returns the ele-
ments in a list of positive numbers that are bigger than all of the preceding numbers in the
list.
6 Rule-based programming 167
In[21]:= maxima x_List : Union Rest FoldList Max, 0, x
In[22]:= maxima 3, 5, 2, 6, 1, 8, 4, 9, 7
Out[22]= 3, 5, 6, 8, 9
We can also write this function using a pattern matching transformation rule.
In[23]:= maximaR x_List :
x . a___, b_, c___, d_, e___ ; d b a, b, c, e
Basically, the transformation rule repeatedly looks through the list for two elements
(b and d here), separated by a sequence of zero or more elements, such that the second
selected element is no greater than the first selected element. It then eliminates the second
element. The process stops when there are no two elements such that the second is less
than or equal to the first.
In[24]:= maximaR 3, 5, 2, 6, 1, 8, 4, 9, 7
Out[24]= 3, 5, 6, 8, 9
Exercises
1. Using Trace on maxima and maximaR, explain why the functional version is much
faster than the pattern matching version of the maxima function.
2. The following compound expression returns a value of 14.
In[1]:= z 11;
a 9;
z 3 . z a
Out[3]= 14
Describe the evaluation sequence that was followed. Use the Trace function to
check your answer.
3. Use the Hold function in the compound expression in the previous exercise to
obtain a value of 12.
4. The function definition f[x_Plus]:= Apply[Times,x] works as follows:
In[4]:= Clear f, a, b, c
In[5]:= f x_Plus : Apply Times, x
168 An Introduction to Programming with Mathematica
In[6]:= f a b c
Out[6]= a b c
The rewrite rule g[x_]:= x /.Plus[z___] Times[z] does not work. Use
Trace to see why and then modify this rule so that it performs the same operation as
the function f above.
5. Create a rewrite rule that uses a repeated replacement to unnest the nested lists
within a list.
In[7]:= unNest a, a, a , a , b, b, b , b, b , a, a
Out[7]= a, a, a , a , b, b, b , b, b , a, a
6. Define a function using pattern matching and repeated replacement to sum the
elements of a list.
7. Using the built-in function ReplaceList, write a function cartesianProduct
that takes two lists as input and returns the Cartesian product of these lists.
In[8]:= cartesianProduct x
1
, x
2
, x
3
, y
1
, y
2
Out[8]= x
1
, y
1
, x
1
, y
2
, x
2
, y
1
, x
2
, y
2
, x
3
, y
1
, x
3
, y
2
8. The function CellularAutomaton[rule, init, t] creates a list of the evolution
of a cellular automaton. For example, this generates five iterations of the cellular
automaton rule number 30 starting with the initial condition of a single 1 surrounded
by 0s.
In[9]:= CellularAutomaton 30, 1 , 0 , 5
Out[9]= 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0 ,
0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 , 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0 ,
0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0 , 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1
Write a function CAGraphics[lis] that takes as argument, a list generated by
CellularAutomaton and produces a Graphics object that can then be displayed
directly with Show. Your function should use RasterArray and also a set of rules
to transform each 0 and 1 into different color directives such as Hue[.2].
In[10]:= ca30 CellularAutomaton 30, 1 , 0 , 500 ;
6 Rule-based programming 169
In[11]:= Show CAGraphics ca30
Out[11]= Graphics
6.4 Examples
This section focuses on two classical problems in computer science: encryption and
sorting. Even though we will only scratch the surface of these two very deep problems,
they are so important and ubiquitous in modern computing that it is well worth while
learning about them. As it turns out, these problems are well suited to a rule-based
approach, at least at an introductory level. We encourage you to investigate further the
theory and implementation of modern cipher and sorting algorithms. See, for example,
(Sedgewick, 1988) and (Wagon, 1999) for details.
Encoding text
In this example, we will develop functions for coding and decoding strings of text. The
particular coding that we will do is quite simplistic compared with contemporary commer-
cial-grade ciphers, but it will give us a chance to see how to combine string manipulation,
the use of functional programming constructs, and rule-based programming all in a very
practical example that should be accessible to anyone.
The problem in encryption is to develop an algorithm that can be used to encode a
string of text and then a dual algorithm that can be used to decode the encrypted message.
At first we will just limit ourselves to the 26 lowercase letters of the alphabet.
In[1]:= alphabet Map FromCharacterCode , Range 97, 122
Out[1]= a, b, c, d, e, f, g, h, i, j, k,
l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
One of the simplest encryption schemes is attributed to Julius Caesar who is said to
have used this cipher to encode communications with his generals. The scheme is simply
170 An Introduction to Programming with Mathematica
to shift each letter of the alphabet some fixed number of places to the left. Using Thread,
we can set up rules that implement this shift.
In[2]:= CaesarCodeRules Thread alphabet RotateLeft alphabet
Out[2]= a b, b c, c d, d e, e f, f g, g h, h i,
i j, j k, k l, l m, m n, n o, o p, p q, q r,
r s, s t, t u, u v, v w, w x, x y, y z, z a
The decoding rules are simply to shift back in the other direction.
In[3]:= CaesarDecodeRules Thread alphabet RotateRight alphabet
Out[3]= a z, b a, c b, d c, e d, f e, g f, h g,
i h, j i, k j, l k, m l, n m, o n, p o, q p,
r q, s r, t s, u t, v u, w v, x w, y x, z y
To code a string, we will decompose the string into individual characters, apply the
code rules, and then join up the resulting characters in a word.
In[4]:= Characters "hello"
Out[4]= h, e, l, l, o
In[5]:= % . CaesarCodeRules
Out[5]= i, f, m, m, p
In[6]:= Apply StringJoin, %
Out[6]= ifmmp
Here is the function to accomplish this.
In[7]:= encode str_String, coderules_ :
Apply StringJoin, Characters str . coderules
Similarly, here is the decoding function.
In[8]:= decode str_String, decoderules_ :
Apply StringJoin, Characters str . decoderules
Let us try it out on a phrase.
In[9]:= encode "squeamish ossifrage", CaesarCodeRules
Out[9]= trvfbnjti pttjgsbhf
In[10]:= decode %, CaesarDecodeRules
Out[10]= squeamish ossifrage
6 Rule-based programming 171
Other ciphers can be created using permutations on the letters of the alphabet. We
will need the randomPermutation function we created in Section 5.2 of the chapter on
procedural programming.
In[11]:= randomPermutation lis_ : Module x, res , l2 lis ,
Do
x Part l2, Random Integer, 1, Length l2 ;
res Append res, x ;
l2 Complement l2, x ,
i, 1, Length lis ;
res
First we create a random permutation of the letters of the alphabet.
In[12]:= p randomPermutation alphabet
Out[12]= y, g, r, e, j, h, f, b, t, p, a,
k, i, m, o, c, u, z, w, d, v, q, s, n, x, l
Then, using Thread, we create a rule for each letter paired up with the corresponding
letter from the permutation p.
In[13]:= PermutationCodeRules Thread alphabet p
Out[13]= a y, b g, c r, d e, e j, f h, g f, h b,
i t, j p, k a, l k, m i, n m, o o, p c, q u,
r z, s w, t d, u v, v q, w s, x n, y x, z l
The decoding rules are obtained by simply reversing the above rules.
In[14]:= PermutationDecodeRules Thread p alphabet
Out[14]= y a, g b, r c, e d, j e, h f, f g, b h,
t i, p j, a k, k l, i m, m n, o o, c p, u q,
z r, w s, d t, v u, q v, s w, n x, x y, l z
In[15]:= encode "squeamish ossifrage", PermutationCodeRules
Out[15]= wuvjyitwb owwthzyfj
In[16]:= decode %, PermutationDecodeRules
Out[16]= squeamish ossifrage
Sorting a list
This next example also incorporates several of the concepts discussed in this chapter. It
uses a delayed rule, contains a conditional, and has several types of pattern matching.
172 An Introduction to Programming with Mathematica
We will create a rule named listsort that, upon repeated application, will put a
list of numbers into numerical order. To account for the first and last elements in the list,
we use BlankNullSequence (___).
In[17]:= listsort
x___, a_?NumericQ, b_?NumericQ, y___ x, b, a, y ; b a
Out[17]= x___, a_?NumericQ, b_?NumericQ, y___ x, b, a, y ; b a
The pattern that has to match {x___,a_,b_,y___} is a list of at least two ele-
ments since x___ and y___ will match zero or more elements. The condition on the
right-hand side of the rule says that whenever b is less than a, switch the order of a and b
in the original list to output {x,b,a,y}.
Here is a list of ten real numbers between 0 and 1.
In[18]:= nums Table Random , 10
Out[18]= 0.237736, 0.182151, 0.822792, 0.264693, 0.968603,
0.599673, 0.602053, 0.101958, 0.219543, 0.539043
In[19]:= nums //. listsort
Out[19]= 0.101958, 0.182151, 0.219543, 0.237736, 0.264693,
0.539043, 0.599673, 0.602053, 0.822792, 0.968603
Notice that because we used ?NumericQ as part of the pattern match, listsort
will work on expressions that may not be explicit numbers, but are numerical in nature;
that is, expressions that return explicit numbers when N is applied to them.
In[20]:= , , EulerGamma, GoldenRatio . listsort
Out[20]= EulerGamma, GoldenRatio, ,
This algorithm is far less efficient than many classical sorting algorithms, especially
those that employ a divide-and-conquer strategy.
In[21]:= nums Table Random , 100 ;
In[22]:= Timing nums . listsort ;
Out[22]= 0.942 Second, Null
The built-in Sort function uses a classical algorithm called merge sort (discussed
in Section 7.5), which starts by dividing the list into two parts of approximately equal size.
It then sorts each part recursively and finally merges the two sorted sublists.
In[23]:= Timing Sort nums ;
Out[23]= 0. Second, Null
6 Rule-based programming 173
The above implementation of listsort only works for numerical arguments. We
can overload listsort to work on characters of strings by making only two small
changes. First, we pattern match a and b with head String instead of ?NumericQ.
Second, instead of comparing a<b, we need to compare their character codes.
In[24]:= ToCharacterCode "z" 1
Out[24]= 122
Here then is the definition of listsort that operates on lists of string characters.
In[25]:= listsort x___, a_String, b_String, y___ x, b, a, y ;
Part ToCharacterCode b , 1 Part ToCharacterCode a , 1 ;
Out[25]= x___, a_String, b_String, y___
x, b, a, y ; ToCharacterCode b 1 ToCharacterCode a 1
Here are ten random characters.
In[26]:= chars
Table FromCharacterCode Random Integer, 97, 122 , 10
Out[26]= c, x, z, e, c, i, d, c, a, l
Here they are sorted.
In[27]:= chars . listsort
Out[27]= a, c, c, c, d, e, i, l, x, z
Exercises
1. Modify the Caesar cipher so that it encodes by shifting five places to the right.
2. Modify the alphabet permutation cipher so that instead of being based on single
letters, it is instead based on adjacent pairs of letters. Whereas the single letter cipher
will have 26 403291461126605635584000000 permutations, the adjacent pairs
cipher will have 26
2
permutations a very large number.
In[1]:= N 26
2
Out[1]= 1.883707684133810 10
1621
3. You can quickly create a graphics function to plot binary data (0s and 1s) using
Raster. For example:
174 An Introduction to Programming with Mathematica
In[2]:= data Table Random Integer , 5 , 5
Out[2]= 1, 1, 0, 0, 1 , 1, 0, 1, 1, 0 ,
0, 1, 1, 1, 1 , 0, 1, 0, 1, 1 , 0, 1, 1, 1, 0
In[3]:= Show Graphics Raster Reverse data , AspectRatio Automatic
Out[3]= Graphics
If you wanted to color the squares with color directives such as RGBColor or Hue or
GrayLevel, then you need to use RasterArray instead. Create a function
matrixPlot[mat,rules] that takes a matrix mat as its first argument and a list of
rules as the second argument. The list of rules should specify what color directive
each of the values in mat should be mapped to. Finally, compare your function with
ArrayPlot (new in Version 5.1).
In[4]:= ArrayPlot data, ColorRules 0 Black, 1 White ;
4. Plot the function sin x over the interval [ 2 , 2 ] and then reverse the x- and
y-coordinates of each point by means of a transformation rule.
5. Plot the function sin x y with x and y taking on values from 0 to 3 2. Then use a
transformation rule to perform a shear by shifting the graphic in the x-direction by a
factor of four.
6 Rule-based programming 175
6. Create a function rotatePlot[gr, ] that takes a plot gr and rotates it about the
origin by an angle . For example, to rotate a plot of the sine function, first create the
plot:
In[5]:= plot1 Plot Sin x , x, 0, 2 ;
1 2 3 4 5 6
1
0.5
0.5
1
Then perform the rotation of radians.
In[6]:= rotatePlot plot1, ;
6 5 4 3 2 1
1
0.5
0.5
1
7. Create a function rotatePlot3D[gr, , , ] that will rotate a Graphics3D
object gr about the origin by the angles , , and in the x, y, and z directions,
respectively.
176 An Introduction to Programming with Mathematica
7 Recursion
Some very important and classical problems in mathematics and computer science are
defined, or have solutions in terms of recursive definitions. A function is defined using
recursion if in its definition, it makes calls to itself. This programming paradigm is
easily implemented in Mathematica in a manner that is both natural and quite efficient.
In fact, many of the built-in operations of Mathematica could be written in Mathemat-
ica itself using recursion. In this chapter, we will present many examples of recursion
and explain how recursive functions are written.
7.1 Fibonacci numbers
Recursive definitions of mathematical quantities were used by mathematicians for centu-
ries before computers even existed. One famous example is the definition of a special
sequence of numbers first studied by the thirteenth-century Italian mathematician
Leonardo Fibonacci. The Fibonacci numbers have since been studied extensively, finding
application in many areas; see (Knuth 1997) for a detailed discussion.
The Fibonacci numbers are obtained as follows: write down two 1s, then continue
writing numbers computed by adding the last two numbers you have written down.
1 1 2 3 5 8 13 21
F
1
F
2
F
3
F
4
F
5
F
6
F
7
F
8
b.
0, 1, 1, 2, 3, 5, 8, 13, 21,
B
1
B
2
B
3
B
4
B
5
B
6
B
7
B
8
B
9
c.
0, 1, 2, 3, 6, 11, 20, 37, 68,
C
1
C
2
C
3
C
4
C
5
C
6
C
7
C
8
C
9
2. The numbers FA
n
represent the number of additions that are done in the course of
evaluating F[n].
0 0 1 2 4 7 12 20 33
FA
1
FA
2
FA
3
FA
4
FA
5
FA
6
FA
7
FA
8
FA
9