Mathematica Programming Fundamentals: Lecture Notes

Richard Gaylord

These notes form the basis of a series of lectures given by the author, in which the fundamental principles underlying Mathematica’s programming language are discussed and illustrated with carefully chosen examples. This is not a transcription of those lectures, but the note set was used to create a set of transparencies which Professor Gaylord showed and spoke about during his lectures. These notes formed the basis for both a single 6 hour one-day lecture and a series of four 90 minute lectures, delivered to professionals and to students.

Introduction

In order to use Mathematica efficiently, you need to understand how the Mathematica programming language works. This tutorial (which has been extensively field-tested in university courses) is intended to provide you with the background that’s needed to write your own code.

Note: While this material can’t replace the use of The Mathematica Book by Wolfram or Mathematica’s online help (which give definitions of built-in functions and simple illustrations of their use), it can help to make you more comfortable with the Mathematica programming language which often seems obscure, even enigmatic, when first encountered by someone whose programming experience is with one of the traditional procedural languages.

In this note set, the following aspects of the Mathematica programming language are emphasized: the nature of expressions, how expressions are evaluated, how pattern-matching works, creating rewrite rules, and using higher-order functions.

Consider the data structure {1,2,3}. How can we add up the elements in the list?

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

What’s going on here?

Every quantity entered into Mathematica is represented internally as an expression. An expression has the form

where the head and argi can be other expressions.

For example, if we look at two common quantities, a list data structure, {a,b,c}, and an arithmetic operation, a+b+c, they appear to be quite different, but if we use the FullForm function to look at their internal representation

FullForm[{a,b,c}]

FullForm[a+b+c]

we see that they differ only in their heads.

The use of a common expression structure to represent everything is not merely cosmetic; it allows us to perform some computations quite simply. For example, to add the elements in a list, it is only necessary to change the head of the expression, List, to Plus. This can be done using the built-in Apply function.

?Apply

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

The obvious approach to this task is to do the same sort of thing that we did to add the elements in a list.

Apply[List,a+b+c]

This works when the list elements are symbols, but it doesn’t work for a list of numbers.

Apply[List,1+2+3]

In order to understand the reason for the different results obtained above, it is necessary to understand how Mathematica evaluates expressions. To do that, we first want to distinguish between the various kinds of expressions.

Expressions

Non-atomic expressions have parts which can be extracted from the expression with the Part function, and can be replaced with the ReplacePart function. For example:

Part[{a,7,c},1]

{a,7,c}[[0]]

Part[a+b+c,0]

ReplacePart[{a,7,c},e,2]

Atomic expressions constitute the basic “building blocks” of the Mathematica language. There are three kinds of atomic expressions:

1. A symbol, consisting of a letter followed by letters and numbers (eg., darwin)

2. Four kinds of numbers:
	integer numbers (eg., 4)
	real numbers (eg., 5.201)
	complex numbers (eg., 3+4I)
	rational numbers (eg., 5/7)

3. A string, comprised of letters, numbers and spaces (ie., ASCII characters) between quotes (eg., "Computer Simulations with Mathematica")

Atomic expressions differ from non-atomic expressions in several ways:

The FullForm of an atomic expression is the atom itself.

{FullForm[darwin],FullForm[4],FullForm["Computer Simulations with Mathematica"]}

The head (or 0th part) of an atom is the type of atom that it is.

{Head[List],Head["Computer Simulations with Mathematica"], 5[[0]]}

An atomic expression has no parts which can be extracted or replaced.

Part["Computer Simulations with Mathematica",1]

A CompoundExpression is an expression consisting of a sequence of expressions separated by semi-colons (;).

a=5+3;4a

When an expression is entered in Mathematica, it is evaluated and the result is returned, unless it is followed by a semi-colon.

4^3

When an expression is followed by a semi-colon, the expression is also evaluated, even though nothing is returned.

2-6;

%+3

%%

When the entered expression is a compound expression, its contents are evaluated sequentially and the result of the last evaluation is returned.

Trace[a=3+5;4a]

Mathematica is a term rewriting system (TRS). Whenever an expression is entered, it is evaluated by term rewriting using rewrite rules. These rules consist of two parts: a pattern on the left-hand side and a replacement text on the right-hand side. When the lhs of a rewrite rule is found to pattern-match part of the expression, that part is replaced by the rhs of the rule, after substituting values in the expression which match labelled blanks in the pattern into the rhs of the rule. Evaluation then proceeds by searching for further matching rules until no more are found.

The evaluation process in Mathematica can be easily understood with the following analogy:

Think of your experiences with using a handbook of mathematical formulas, such as the integral tables of Gradshteyn and Ryzhik. In order to solve an integral, you consult the handbook which contains formulas consisting of a left-hand side (lhs) and a right-hand side (rhs), separated by an ‘equals’ sign. You look for an integration formula in the handbook whose left-hand side has the same form as your integral.

Note: While no two formulas in the handbook have the identical lhs, there may be several whose lhs have the same form as your integral (eg., one lhs might have specific values in the integration limits of in the integrand, while another lhs has unspecified (dummy) variables for these quantities). When this happens, you use the formula whose lhs gives the closest fit to your integral.

You then replace your integral with the right-hand side of the matching lhs and you substitute the specific values in your integral for the corresponding variable symbols in the rhs.

Finally, you look through the handbook for formulas (eg., trigonometric identities or algebraic manipulation) that can be used to change the answer further.

This depiction provides an excellent description of the Mathematica evaluation process.

However, the application of the term rewriting process to a Mathematica expression requires a bit more discussion because a Mathematica expression consists of parts, a head and zero or more arguments which are themselves expressions.

It is therefore necessary to understand the order in which the various parts of an expression are evaluated by term rewriting.

The implementation of the evaluation procedure is (with a few exceptions) straightforward:

1. If the expression is a number or a string, it isn’t changed.

2. If the expression is a symbol, it is rewritten if there is an applicable rewrite rule in the global rule base; otherwise, it is unchanged.

3. If the expression is not a number, string or symbol, its parts are evaluated in a specific order:

a. The head of the expression is evaluated.

b. The arguments of the expression are evaluated from left to right in order. An exception to this occurs when the head is a symbol with a hold attribute (eg., HoldFirst, HoldRest, or HoldAll), so that some of its arguments are left in their unevaluated forms (unless they, in turn, have the head Evaluate). For example, the Set or SetDelayed function which we will discuss in a moment.

4. After the head and arguments of an expression are each completely evaluated, the expression consisting of the evaluated head and arguments is rewritten, after making any necessary changes to the arguments based on the Attributes (such as Flat, Listable, Orderless) of the head, if there is an applicable rewrite rule in the global rule base.

5. After carrying out the previous steps, the resulting expression is evaluated in the same way and then the result of that evaluation is evaluated, and so on until there are no more applicable rewrite rules.

The details of the term-rewriting process in steps 2 and 4 are as follows:

a. part of an expression is pattern-matched by the lhs of a rewrite rule

b. the values which match labelled blanks in the pattern are substituted into the rhs of the rewrite rule and evaluated.

c. the pattern-matched part of the expression is replaced with the evaluated result.

With this understanding of the evaluation procedure, we can now understand what happened when we entered

Apply[List,1+2+3]

In evaluating this expression, the argument 1+2+3 was evaluated before the Apply function was employed.

We should point out that the user can (to some extent) wrest control of the evaluation process from Mathematica and either force or prevent evaluation. We won’t go into the details of doing this but we can indicate functions that can be used for this purpose: Hold, HoldAll, HoldFirst, HoldRest, HoldForm, HeldPart, ReleaseHold, Evaluate, Unevaluated, and Literal.

In order to turn the sum into a list, it is necessary to prevent the argument Plus[1,2,3] from being prematurely evaluated before the symbol Plus is replaced with the symbol List.

Apply[List,Unevaluated[1+2+3]]

Since term rewriting is based on pattern-matching, we need to look at the various sorts of patterns that Mathematica recognizes.

Patterns

Patterns are defined syntactically, ie., by the internal representation of an expression as given using FullForm.

In general, an expression will be matched by several patterns, of differing specificity. For example, constructing as many patterns to match x^2, in order of increasing generality.

1. x raised to the power of two.

2. x raised to the power of a number.

3. x raised to the power of something.

4. a symbol raised to the power of two.

5. a symbol raised to the power of a number.

6. a symbol raised to the power of something.

7. something raised to the power of two.

8. something raised to the power of a number.

9. something raised to the power of something.

10. something.

The term ‘something’ used above can be replaced by the term ‘an expression’, so that for example, the last case says that x^2 pattern-matches an expression (which is true since x^2 is an expression). To be precise, we need a notation to designate a pattern that has the form of an expression. We also need to designate a pattern that has the form of a sequence of expressions, consecutive expressions separated by commas.

Patterns are defined in Mathematica as expressions that may contain blanks. A pattern may contain a single (_) blank, a double (__) blank, or a triple (___) blank (the differences will be discussed shortly).

Note: A pattern can be labelled (given a name) by preceding the blank(s) by a symbol, eg., name_ or name__ or name___. The labelled pattern is matched by exactly the same expression that matches its unlabeled counterpart (pattern labeling, as we will see, is used to create dummy variables).

Note: A blank can be followed by a symbol, eg., _h or __h or ___h, in which case, an expression must have the head h to match the pattern (this is used to perform type checking).

We can use the MatchQ function to determine if a particular pattern matches an expression or a sequence of expressions. The most specific pattern-match is between an expression and itself.

MatchQ[x^2,x^2]

To make more general (less specific) pattern-matches, a single blank is used to represent an individual expression, which can be any data object.

We’ll work with x^2 to demonstrate the use of the Blank function in pattern-matching. In the following examples (which are arbitrarily chosen from the many possible pattern matches), we’ll first state the pattern-match and then check it using MatchQ.

x^2 pattern matches ‘an expression’.

MatchQ[x^2,_]

x^2 pattern-matches ‘x raised to the power of an expression’.

MatchQ[x^2,x^_]

x^2 pattern-matches ‘x raised to the power of an integer’ (to put it more formally, ‘x raised to the power of an expression whose head is Integer’).

MatchQ[x^2,x^_Integer]

x^2 pattern-matches ‘an expression whose head is Power’.

MatchQ[x^2,_Power]

x^2 pattern-matches ‘an expression whose head is a symbol and which is raised to the power 2’.

MatchQ[x^2,_Symbol^2]

x^2 pattern-matches ‘an expression raised to the power 2’.

MatchQ[x^2,_^2]

x^2 pattern-matches ‘an expression whose head is a symbol and which is raised to the power of an expression whose head is an integer’ (or stated less formally, ‘a symbol raised to the power of an integer’).

MatchQ[x^2,_Symbol^_Integer]

x^2 pattern-matches ‘an expression raised to the power of an expression’.

MatchQ[x^2,_^_]

x^2 pattern-matches ‘x raised to the power of an expression’ (the label y does not affect the pattern-match).

MatchQ[x^2,x^y_]

As a final example, we look at

MatchQ[5^2,_^_]

A sequence consists of a number of expression separated by commas. A double blank represents a sequence of one or more expressions and __h represents a sequence of one or more expressions, each of which has head h.

For example a sequence in a list pattern-matches a double blank (note: we are pattern-matching the sequence in the list, not the list itself)

MatchQ[{a,b,c},{__}]

but the arguments of an empty list (which has no arguments) do not pattern-match a double blank.

MatchQ[{},{__}]

An expression that pattern-matches a blank will also pattern match a double blank. For example,

MatchQ[x^2,__]

A triple blank represents a sequence of zero or more expressions and ___h represents a sequence of zero or more expressions, each of which has the head h. For example, the triple blank pattern-matches the empty list.

MatchQ[{},{___}]

An expression that pattern-matches a blank and a sequence that pattern-matches a double blank pattern both pattern-match a triple blank pattern.

MatchQ[x^2,___]

It is important to be aware that for the purposes of pattern-matching, a sequence is not an expression. For example,

MatchQ[{a,b,c},{_}]

We can make a pattern-match less restrictive by specifying alternative patterns that can be matched.

MatchQ[x^2,{_}|_^2]

We can make a pattern-match more restrictive by making it contingent upon meeting certain conditions. (Note: satisfying these conditions will be a necessary, but not sufficient, requirement for a successful pattern-match.)

If the blanks of a pattern are followed by ?test, where test is a predicate (ie., a function that returns a True or False), then a pattern-match is only possible if test returns True when applied to the entire expression. ?test is used with built-in predicate functions and with anonymous predicate functions.

MatchQ[x^2,_^_?OddQ]

MatchQ[2,_?(#>3&)]

MatchQ[2,_?(#>1.5&)]

MatchQ[2,_Integer?(#>3&)]

If part of a labeled pattern is followed by /; condition, where condition contains labels appearing in the pattern, then a pattern-match is possible only if condition returns True when applied to the labelled parts of an expression. For example,

MatchQ[x^2,_^y_]

MatchQ[a^b,_^y_/;Head[y]==Symbol]

With this understanding of how pattern-matching works in Mathematica, we can discuss how to create our own rewrite rules which can be used in term rewriting.

Rewrite rules

Mathematica provides over one thousand built-in functions that can be used for term rewriting. These rules are located in the global rule base whenever Mathematica is running. Functions defined in a Mathematica package are also placed in the global rule base during the session in which the package is loaded. Functions in the global rule base are always available for term rewriting and they are always used whenever applicable.

In addition to the built-in rewrite rules, user-defined rewrite rules can be created and placed in the global rule base where they are always available, and always used, when applicable for the duration of the ongoing session. However, they are not automatically preserved beyond the session in which they are created.

There are basically two ways to create a user-defined rewrite rule: with the Set function and with the SetDelayed function.

A value declaration is essentially a nickname for a value (eg., for a list or number) which can be used in place of the value. It is written using Set[lhs, rhs] or, more commonly, as

The lhs starts with a name, starting with a letter followed by letters and/or numbers (with no spaces). The rhs is either an expression or a compound expression enclosed in parentheses.

Note: the name on the lhs may be followed by a set of square brackets containing a sequence of patterns or labelled patterns, and the rhs may contain the labels, without the blanks.

For example, consider the following two simple Set functions

a={-1,1}

rand1=Random[Integer,{1,2}]

Notice that when a Set function is entered, a value is returned (unless it is followed by a semi-colon). If we look into the global rule base to see what rewrite rules have been created when a and rand1 were entered

?a

?rand1

we fine that the rewrite rule associated with a is the same as the Set function we entered, but the rewrite rule associated with rand1 differs from the corresponding Set function. The reason for this is that when a Set function is entered into the global rule base, its lhs is left unevaluated while its rhs is evaluated and when the rewrite rule that has been created is used, the unevaluated lhs and the evaluated rhs of the function are used. This property is known as the HoldFirst attribute.

Attributes[Set]

?HoldFirst

The reason for the Set function having the HoldFirst attribute is easily demonstrated by seeing what happens when Set does not have this attribute.

Set[a,6]

a

ClearAttributes[Set,HoldFirst]

Attributes[Set]

Set[a,7]

SetAttributes[Set,HoldFirst]

Attributes[Set]

When the rhs is a compound expression enclosed in parentheses, the expressions of the rhs are evaluated in sequence and the rhs of the resulting rewrite rule is the result of the final evaluation. For example,

rand2=(b={-1,1};Random[Real,b])

?rand2

What happened here is that the b was first evaluated to give {-1,1} and this value was then used to evaluate the random number function.

The order of expressions on the rhs is important. An expression on the rhs must appear before it is used in another expression on the rhs. For example,

rand3=(Random[Real,c];c={-1,1})

Note that even though an error message was generated when the first expression in the compound expression was evaluated, the overall evaluation of the compound expression continued by evaluated the second expression and its value was then entered into the global rule base.

?rand3

?c

When a Set function is entered, both it and any Set or SetDelayed functions on the rhs create rewrite rules in the global rule base.

?b

?c

After a value has been declared by entering a Set function, the appearance of the value’s name during an evaluation causes the value itself to be substituted in (which is why we say that it acts like a nickname). For example,

Abs[rand2]

What happened here was that the rewrite rule associated with rand2 in the global rule base was used as an argument to the Abs function.

The lhs of a rewrite rule can only be associated with one value at a time. When a Set function is entered, the resulting rewrite rule ‘overwrites’ any previous rewrite rule with the identical lhs. For example,

rand4=Random[Integer,{1,2}];

?rand4

rand4=Random[Integer,{1,2}];

?rand4

What we see is that the value of rand4 was 2 after rand4 was first entered and this value was then changed to 1 after rand4 was re-entered.

While the lhs of a rewrite rule can only be associated with one value at a time, a value can be associated with several names, simultaneously. We made use of this earlier when we defined both b and c as {-1,1}.

Finally, user-defined rewrite rules can be removed from the global rule base using either the Clear or Remove function.

Clear[b]

?b

Remove[c]

?c

Function definitions (ie., programs) are written as

The lhs starts with a name. The name is followed by a set of square brackets containing a sequence of labelled patterns, which are symbols ending with one or more underscores (ie., blanks). The rhs is either an expression or a compound expression enclosed in parentheses, containing the labels on the lhs (without the blanks).

For example, consider the function definition

We’ll enter this program

f[x_]:=Random[Real,{0,x}]

The first thing we notice is that, in contrast to a Set function, nothing is returned when a SetDelayed function is entered. If we query the rule base,

?f

we see that a rewrite rule associated with f has been placed in the global rule base that is identical to the SetDelayed function. The reason is that when a SetDelayed function is entered both its lhs and the rhs are left unevaluated. This property is known as the HoldAll attribute.

Attributes[SetDelayed]

?HoldAll

A user-defined function is called in the same way as a built-in function is called, by entering its name with specific argument value(s).

f[8]

Each time the lhs of a SetDelayed rewrite rule is entered with specific argument values, the rhs of the rule is evaluated using these values, and the result is returned.

f[8]

Note: In contrast to the := function, the = function only evaluates the rhs when it is first entered and thereafter, that same evaluated rhs is returned each time the lhs is entered with specific argument values. For example, consider

f[x_]=x;

?f

f[9]

f[7]

?f

The definition of f above seems to work fine. However, the problem arises when the rhs of the Set function has already had a value assigned to it prior to the entry of the Set function.

y=7;

g[y_]=y;

?g

g[3]

This problem does not arise when the SetDelayed function is used.

z=8;

g[z_]:=z

?g

g[2]

This property of fresh evaluation of both the lhs and rhs of the := function with each use, is why the := function is used to write programs rather than the = function.

When the rhs of the SetDelayed function is a compound expression enclosed in parentheses, no rewrite rules are created from the auxiliary functions on the rhs when the function is entered (this is because the rhs is not evaluated). When the program is run (or equivalently, a user-defined function is called) for the first time, all of the auxiliary functions are then placed in the global rule base.

g[x_]:=(d=2;x+d)

?g

?d

g[3]

?d

The use of a rewrite rule can be restricted by attaching constraints on either the lhs or the rhs of a SetDelayed rule. Conditional pattern-matching with _h or with _? and _/; can be attached to the dummy variable arguments on the lhs. Also, /; can be placed on the rhs, immediately after the (compound) expression.

s[x_?EvenQ]:=N[Sqrt[x]]

s[6]

s[5]

As we have pointed out, when the lhs of a Set or SetDelayed function is evaluated (which occurs when a Set function is first entered and when a SetDelayed rewrite rule is first called), rewrite rules for all of its auxiliary functions are placed in the global rule base. This can cause a problem if a name being used in a program conflicts with the use of the name elsewhere.

We can prevent a name clash by ‘insulating’ the auxiliary functions within the rewrite rule so that they are not placed in the global rule base as separate rewrite rules; they will only ‘exist’ while being used in the evaluation of the rule.

This is usually done using the Module function.

For example,

t[y_]:=Module[{m},m=2;y+m]

?m

t[3]

?m

When the lhs of more than one built-in and/or user-defined rewrite rule is found to pattern-match an expression (which occurs when the lhs’s only differ in their specificity), the choice of which rule to use is determined by the order of precedence:

A user-defined rule is used before a built-in rule.

A more specific rule is used before a more general rule (a rule is more specific, the fewer expression it pattern-matches).

So, for example, if we have two rewrite rules whose lhs’s have the same name but whose labelled patterns have different specificity, both rules will appear in the global rule base (since their lhs’s are not identical) and the more specific rule will be used in preference to the more general rule. For example, if we enter both of the following function definitions

f[x_]:=x^2
f[x_Integer]:=x^3

and then query the rule base,

?f

Now, entering f with a real-valued argument

f[6.]

returns a different result from entering f with an integer-valued argument.

f[6]

This occurs because while an integer-valued argument pattern-matches both x_ and x_Integer (and hence pattern-matches both of the f rewrite rules), the second rule is a more specific pattern-match for the integer value 6.

Note: If Mathematica cannot deduce which rule is more general, it uses the rules in the order in which they appear in the global rule base.

The ordering of rewrite rules makes it possible for us to create sets of rewrite rules with the same name that give different results, depending on the arguments used. This is key to writing rule-based programs.

Note: It is necessary to be careful about the labelling of patterns in rewrite rules because if two or more rules are identical except for the labelling, these rules will all be placed in the global rule base and it may not be obvious which rule will be used. For example,

w[x_]:=x^4

w[_]:=Random[]

w[2]

?w

Transformation rules

There are times when we want a rewrite rule to only be applied to (ie., used inside) a specific expression, rather than being placed in the global rule base where it will be used whenever it pattern-matches an expression. For example, the ‘temporary’ substitution of a value for a name in an expression may be preferable to the permanent assignment of the name to the value via a Set function. When this is the case, the ReplaceAll function can be used together with a Rule or RuleDelayed function to create a transformation (or local rewrite) rule which is placed directly after the expression to which it is to be applied.

A Rule function is attached to an expression. It is written

The lhs can be written using symbols, numbers or labelled patterns.

When an expression with an attached Rule transformation rule is entered, the expression itself is evaluated first. Then, both the lhs and the rhs of the Rule transformation rule are evaluated. Finally, the fully evaluated transformation rule is used in the evaluated expression. For example

Clear[a]

Table[1,{4}]/.Random[Integer,{0,1}]->a

Table[1,{4}]/.Random[Integer,{0,1}]->a

Table[1,{4}]/.Random[Integer,{0,1}]->{a,b}[[Random[Integer,{1,2}]]]

Table[1,{4}]/.Random[Integer,{0,1}]->{a,b}[[Random[Integer,{1,2}]]]

Table[1,{4}]/.Random[Integer,{0,1}]->{a,b}[[Random[Integer,{1,2}]]]

We can attach a list of rules to an expression using

For example,

{a,b,c}/.{c->b,b->a}

Multiple transformation rules are used in parallel. The rules are applied in order so that a later rule in the list is used only if all the earlier rules do not match, and only one transformation rule at most, is applied to a given part of an expression, and no matching rules are used thereafter, as the above example illustrates.

A RuleDelayed function is attached to an expression. It is written

or, for a list of rules

The lhs can be written using symbols, numbers or labelled patterns.

When an expression with an attached rule is entered, the expression itself is evaluated first. Then, the lhs of the RuleDelayed transformation rule is evaluated but the rhs is not evaluated. Finally, the partially evaluated transformation rule is used in the evaluated expression (the unevaluated rhs will be evaluated subsequently).

For example,

Table[1,{4}]/.Random[Integer,{0,1}]:>{a,b}[[Random[Integer,{1,2}]]]

Table[1,{4}]/.Random[Integer,{0,1}]:>{a,b}[[Random[Integer,{1,2}]]]

Table[1,{4}]/.Random[Integer,{0,1}]:>{a,b}[[Random[Integer,{1,2}]]]

By placing /; condition immediately after a RuleDelayed :> transformation rule, its use can be restricted in the same way that using /; condition can be used to restrict the use of a SetDelayed rewrite rule.

Note: Placing a /; condition after a Rule -> transformation rule serves no purpose since the rhs of the rule has already been evaluated before it is used and hence the conditional restriction is ignored.

To apply one or more transformation rules repeatedly to an expression until the expression no longer changes, the ReplaceRepeated function is used. For example,

{a,b,c}//.{c->b,b->a}

Note: In using //. with a list of transformation rules, it is important to keep in mind the order of application of the rules. The transformation rules are not repeatedly applied in order. Rather, each rule, in turn, is applied repeatedly.

Functional programming style

Mathematica works with built-in and user-defined functions in ways which are characteristic of the ‘functional’ style of programming.

Consider the following consecutive computations:

Tan[4.0]

Sin[%]

Cos[%]

We can combine these function calls into a nested function call.

Cos[Sin[Tan[4.0]]]

Notice that the result of one function call is immediately fed into another function without having to first name (or declare) the result.

A nested function call is the application of a function to the result of applying another function to some argument value. In applying functions successively, it is not necessary to declare the value of the result of one function call prior to using it as an argument in another function call.

We can illustrate the use of nested function calls using a deck of playing cards:

Range[2,10]

Join[%,{𝒥,𝒬,𝒦,𝒜}]

Outer[List,{♣,♢,♡,♠},%]

Flatten[%,1]

Combining these operations, we can define cardDeck by combining the operations above.

cardDeck=Flatten[Outer[List,{♣,♢,♡,♠},Join[Range[2,10],{𝒥,𝒬,𝒦,𝒜}]],1]

Shuffling a deck of cards

Transpose[Sort[Transpose[{Table[Random[],{52}],cardDeck}]]][[2]]

Note: We can also shuffle a deck of cards using a transformation rule.

Sort[Transpose[{Table[Random[],{52}],cardDeck}]]/.{_,y_}:>y

User-defined anonymous functions can be created and used ‘on the spot’ without being named or entered prior to being used.

An anonymous function is written using the same form as the rhs of a rewrite rule, replacing variable symbols with #1, #2, … and enclosing the expression in parentheses followed by an ampersand (&).

This notation can be demonstrated by converting some simple user-defined functions into anonymous functions. For example, a rewrite rule that squared a value

square[x_]:=x^2

can be written as an anonymous function and applied to an argument, eg., 5, instantly.

(#^2)&[5]

An example of an anonymous function with two arguments, raises the first argument to the power of the second argument.

(#1^#2)&[5,3]

It is important to distinguish between an anonymous function which takes multiple arguments and an anonymous function which takes a list with multiple elements as its argument.

For example, the anonymous function just given doesn’t work with an ordered pair argument.

(#1^#2)&[{2,3}]

If we want to perform the operation on the components of an ordered pair, the appropriate anonymous function is

(#[[1]]^#[[2]])&[{2,3}]

Anonymous functions can be nested, in which case it is sometimes necessary to use the form

rather than the #·& form, in order to distinguish between the arguments of the different anonymous functions.

(#^3)&[(#+2)&[3]]

Function[y,y^3][Function[x,x+2][3]]

The two forms can also be used together.

Function[y,y^3][(#+2)&[3]]

(#^3)&[Function[x,x+2][3]]

Anonymous functions are useful for making predicates and arguments for higher-order functions.

Note: An anonymous predicate function must be written using the #·& form.

A higher-order function takes a function as an argument and/or returns a function as a result. This is known as ‘treating functions as first-class objects’. We’ll illustrate the use of some of the most important built-in higher order functions.

?Apply

We have already seen Apply used to add the elements of a linear list. Given a nested list argument, Apply can be used on the outer list or the interior lists. For example, for a general function, f, and a nested list.

Apply[f,{{a,b},{c,d}}]

Apply[f,{{a,b},{c,d}},2]

?Map

For a general function, f, and a linear list.

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

For a nested list structure, Map can be applied to either the outer list or to the interior lists, or to both. For example, for a general function g:

Map[g,{{a,b},{c,d}}]

Map[g,{{a,b},{c,d}},{2}]

?MapThread

For a general function, g, and a nested list.

MapThread[g,{{a,b,c},{x,y,z}}]

MapThread[List,{{a,b,c},{x,y,z}}]

MapThread[Plus,{{a,b,c},{x,y,z}}]

Nest performs a nested function call, applying the same function repeatedly.

The Nest operation applies a function to a value, then applies the function to the result, and then applies the function to that result and then applies… and so on a specified number of times.

?NestList

{0.7,Sin[0.7],Sin[Sin[0.7]],Sin[Sin[Sin[0.7]]]}

NestList[Sin,0.7,3]

If we are only interested in the final result of the NestList operation, we can use the Nest function which does not return the intermediate results.

?Nest

Nest[Sin,0.7,3]

The Nest operation does not stop until it has completed a specified number of function applications. There is another function which performs the Nest operation, stopping after whichever of the following occurs first: (a) there have been a specified number of function applications, (b) the result stops changing, or (c) some predicate condition is met.

?FixedPointList

?FixedPoint

As an example,

FixedPointList[Sin,0.7,5,SameTest->(#2<0.65&)]

FixedPointList[Sin,0.7,5,SameTest->((#1-#2)<0.045&)]

Note: In these examples, #1 refers to the next-to-last element in the list being generated and #2 refers to the last element in the list.

?FoldList

?Fold

The Fold operation takes a function, a value and a list, applies the function to the value, and then applies the function to the result and the first element of the list, and then applies the function to the result and the second element of the list and so on. For example,

Fold[Plus,0,{a,b,c,d}]

FoldList[Plus,0,{a,b,c,d}]

FoldList[Plus,0,Table[Random[Integer],{5}]]

Exercises

List manipulation

Use Table to create the list {1,2,3,4,5,6,7,8,9,10}. Use Range to create this same list.

Use Table to create the list {2,4,6,8,10}. Use Range to create this same list.

Use Table and Random to create a 5 by 5 matrix of random real numbers between 0 and 1.

Use Table and Random to create a 5 by 5 matrix of integers between 0 and 9 inclusive.

Use Table to create the list {{0},{0,2},{0,2,4},{0,2,4,6},{0,2,4,6,8}} in two ways.

Create a 10-element list of random 1’s and -1’s in two ways.

Use Table to create a list equivalent to Array[f,5] and to create a list equivalent to Array[f,{3,4}]

What is the length of the output of Range[7]? What are its dimensions?

What is the length of the list {{1,2},{2,3},{3,4},{4,5},{5,6},{6,7}}? What are its dimensions?

Where does 2 appear in the list {1,1,2,3,5}? Confirm your guess with Position, and extract the 2 using Part.

Use ReplacePart to replace the 2 in {1,1,2,3,5} with the string "two".

Use ReplacePart to replace the 2 in the list {{1},{2,3},{4,5,6,7}} with the string "two".

Extract the last 4 elements of {5,3,2,1,1} using four different functions: Part, Take, Drop, Rest.

Extract the 2nd through 4th elements of {5,3,2,1,1} using two different functions: Part, Take.

Remove the first and last elements from {5,3,2,1,1} by using Drop twice.

Create {{1},{2,3,4,5,6}} out of Range[6].

Use the lists {a,b,c,d} and {3,2,4,1} to create the list {c,b,d,a} (one step).

Generate a 5 by 5 matrix whose entries are randomly either random integers or random reals. (Hint: how do you generate a number that’s randomly either a random integer or a random real?)

In this section, lis represents the matrix {{1,2,3},{4,5,6},{7,8,9}}.

What will Reverse do to lis?

Use RotateRight to change lis into {{7,8,9},{1,2,3},{4,5,6}}. Use RotateLeft to do the same thing.

Use RotateLeft to change lis into {{2,3,1},{5,6,4},{8,9,7}}. Use RotateRight to do the same thing.

Create lis using Range and Partition.

Create {{1,2},{2,3},{3,4},{4,5},{5,6},{6,7}} using Range and Partition. Do the same thing using Table.

Use Range, Partition and Flatten to create the list {1,2,2,3,3,4,4,5,5,6,6,7}.

What will Transpose do to lis?

Use Transpose to collect the x’s together and the y’s together in the list {{x1,y1},{x2,y2},{x3,y3},{x4,y4}}.

Combine the lists {1,2,3,4} and {0.1,0.2,0.3,0.4} into one list.

Remove the first and last elements from {5,3,2,1,1} by using only Rest and Reverse.

Remove the first and last elements from {5,3,2,1,1} using RotateRight and Rest. Do the same thing using RotateLeft and Rest.

Add a random real number to the beginning of the list {5,3,2,1,1} using Prepend. Do the same thing using Join.

Add a random real number to the end of the list {5,3,2,1,1} using Append. Do the same thing using Join.

Given an n by n matrix, add a border or zeros around it. (Eg., given {{1,2,3},{4,5,6},{7,8,9}}, return {{0,0,0,0,0}, {0,1,2,3,0}, {0,4,5,6,0}, {0,7,8,9,0}, {0,0,0,0,0}}.)

For each of the following, use the lists {1,2,3,4,5}, {1,3,5} and {2,4,5,6}

Combine the lists into a single, sorted list.

What effect will Union have on the three lists?

Find the elements common to these lists.

Find the elements unique to the second list.

Construct a list of ten random integers between 0 and 9 inclusive. Extract a list of which integers appear in this list, and which do not.

Use the lists {a,b,c,d} and {3,2,4,1} to create the list {d,b,a,c} (three steps).

Find all elements of the lists {a,b,c,d} and {a,b,e,f} that are not common to the two lists.

Extract the 5 largest elements from a list of random numbers.

Pattern matching

Find 10 patterns that match {{-1,0},{3,5.6,c}}. Verify the match with MatchQ.

Use Alternatives to create a pattern that will match 1, -1, a, or 3.14, but nothing else.

Determine how many 10’s there are in a random list of one thousand integers between 1 and 10.

Determine how many sublists of Table[i+j,{i,1,10},{j,1,3}] contain the number 5.

Extract the sublists of Table[i+j,{i,1,10},{j,1,3}] that contain the number 5.

Remove the sublists of Table[i+j,{i,1,10},{j,1,3}] that contain the number 5.

Extract every subexpression of {1,{-1,0},{3,5.6,{c}}} that matches _List.

Remove every positive number from {1,{-1,0},{3,5.6,{c}}}.

Function definitions

Without using Condition, define a function, g, of three arguments that returns 0 if any two of the arguments are 0, and 1 otherwise.

Using Condition, define a function, f, of three arguments that returns 0 if any of its arguments is 0, and 1 otherwise.

Define a function, pointLoc, that takes an ordered pair {x,y} as its argument. It returns zero if one or both of x and y is zero, and otherwise returns the number of the quadrant in which the point {x,y} appears. (The first quadrant is where x and y are positive, and the numbering continues counter-clockwise. The fourth quadrant is where x is positive and y is negative.)

Rewrite the following function definition without using Condition:

Define a function, g, that takes five arguments, and satisfies the following conditions:
1. If exactly three of the last four arguments are 1’s, return a 1.
2. If the first argument and exactly two other arguments are 1’s, return a 1.
3. In all other cases, return a 0.
Check your function by making sure step[1,1,0,0,1], step[1,1,0,1,1] and step[1,1,0,0,0] evaluate the way you expect.

Define a function, findTriplet, that returns a list of three of its arguments that add up to 100, if three such arguments exist, and returns the string "Impossible" otherwise. (Hint: it can be done in two lines using BlankNullSequence and Condition.) Test your function on the first 20 prime numbers (findTriplet[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71]).

Write a function that randomly selects a specified number of elements from a linear list with duplicates allowed. (This is called sampling with replacement.)

Write a function that randomly selects a specified number of elements from a list with no duplicates allowed. (This is called sampling without replacement.)

Write a function that interleaves the elements of two lists of unequal length. (Let the first list be longer than the second list.)

Transformation rules

Given a list of random triplets, count the number of zeros in each using transformation rules.

Given a random 5 by 5 table of zeros and ones, use Rule and ReplaceAll change all zeros to ones and all ones to zeros.

Use rules to replace every sublist of Table[i+j,{i,1,10},{j,1,3}] that contains the number 5 with the list {5,5,5}.

Use rules and ReplaceAll to convert all sublists of Table[Random[Integer],{5},{5}] that contain at least three zeros to {0,0,0,0,0}.

Use a transformation rule to take a list of elements and return a list of those elements that are greater than all of the preceding elements in the list.

Sum the elements in a list using the repeated application of a transformation rule.

Anonymous and higher-order functions

Create a pure function equivalent to the right-hand side of f[z_]:=3.75*z*(1-z). Calculate the value of the function at 0.5.

Create a pure predicate function that returns True if its (integer) argument is a perfect square (whose square root is an integer). Use the function as an argument to Select to return all the perfect squares in the list {1,2,3,…,1000}.

Create a pure function that takes as an argument a list of two numbers, and returns the first number raised to the power of the second. Test it on the list {2,10}. Do this two ways: one using the Part function and one without using the Part function.

Use a nested function call to take the lists {3,2,4,1} and {a,b,c,d} and return {d,b,a,c}.

Use a nested function call to create a deck of cards and perform a perfect shuffle on it. (A perfect shuffle consists of cutting the deck in half and interleaving the two halves.)

Rotate each sublist of {{1,2},{3,4,5},{6,7,8,9}} one element to the right. Find the length of each sublist.

Given the following “matrix of lists”, extract the second element from each of the innermost lists: {{0,1,2},{3,4,5},{6,7,8,9}}

Use MapThread to compute the maximum element in each column of a random 5 by 5 array of integers.

Without using Count, find out how many 1s are in a random list of 0s and 1s.

Rotate the matrix {{1,2,3},{4,5,6}} 90 degrees clockwise to form {{4,1},{5,2},{6,3}}. Use TableForm to show both the original and the final matches.

Using Map and a pure function, add a zero to the beginning and end of each sublist of {{1,2,3},{4,5,6},{7,8,9}}.

Un-nest the nested lists within the list {{a,a,a},{a},{{b,b,b},{b,b}},{a,a}} to {{a,a,a},{a},{b,b,b},{b,b},{a,a}}.

Create a random 5 by 5 matrix of 0s and 1s, and compute the mean of each row of the matrix.

Use Nest to compute Sin[Sin[Sin[Sin[Sin[2.0]]]]]

Iterate a pure function equivalent to f[z_]:=3.75*z*(1-z) thirty times, starting with z=0.5 and saving all the intermediate results.

Create a one-dimensional random walk by iterating (50 times) a function that randomly adds either -1 or 1 to its argument.

Use a nested function call including FoldList, to take a list and return a list of those elements that are greater than all of the preceding elements in the list.

Define a function that takes a list of two random even integers (such as {20,2}) and adds one to the larger of the two numbers and subtracts one from the smaller. Iterate this function until the two numbers are equal.

Write a function that removes every other element in a list until only one element remains. (This is known as the Josephus problem.)

Write a function that calculates the frequencies of elements in a list. Do this in two ways: without using an anonymous function and using a anonymous function.