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
Either of these operations are suitable for the job (to coin a phrase, “there’s not a penny, nickel, quarter, or dime’s 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,
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.
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.
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 Mathematica’s 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 isoutside 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.