This notebook is part of a draft of a set of elementary Mathematica tutorials, as presented at the 1999 Mathematica Developer Conference, October 20-23, 1999. This draft is provided here to encourage suggestions about these tutorials. You can send suggestions about these tutorials to Wolfram Research Technical Support at the address support@wolfram.com.

Only the first nine parts of the Basic Tutorial are included in this draft.

Basic Tutorial (part 7 of 10)

7 Working with Expressions

In this seventh part of the Basic Tutorial you will learn about:

• The structure of expressions
• Constructing expressions
• Getting parts of expressions
• Combining expressions
• Patterns
• Pure functions

The structure of expressions

Expressions are fundamental to Mathematica, and are discussed throughout the documentation. For more information see especially Section 2.1.1 and Appendix A.1 in The Mathematica Book.

A normal expression is an expression that has a head and zero or more elements. The head of the normal expression f[a, b], for example, is f, and the elements are a and b.

The head and the elements in a normal expression are also expressions, and can, for example, be other normal expressions. The second element in f[1, g[2]], for example, is the normal expression g[2].

Expressions that are not normal expressions are called atoms. Atoms are either symbols, numbers, or strings. All expressions are constructed from atoms. The atoms in the normal expression f[1, g[2]], for example, are the symbols f and g and the numbers 1 and 2.

If expr is an expression, Head[expr] gives the head of expr, Length[expr] gives the number of elements in expr, and Part[expr,i] (or expr[[i]]) gives the element at position i in expr. For example:

Head[f[a, b]]

Length[f[a, b]]

Part[f[a, b], 2]

Constructing expressions

One way to construct an expression is to type it in to Mathematica. An expression is constructed every time you enter an input. For example, when you enter 2 + 2, Mathematica constructs the expression Plus[2, 2] to represent that input. Expressions are also constructed automatically to represent results. When you evaluate Plus[2, 2], for example, the expression 4 is generated to represent the result.

The Table function can be used for constructing lists and arrays. Here is an example in which Table returns a list of integers. The first argument i^2 in this example is the expression to evaluate to compute elements in the list, and the second argument {i, 1, 10} specifies that the first argument should be evaluated for values of i ranging from 1 to 10. The second argument is referred to as an iteration specification, since it describes the iteration to use in constructing the result.

Table[i^2, {i, 1, 10}]

There are several variant ways to use the Table function. Here is an example in which Table is used to generate an array of random numbers. Since the table index is not used in the first argument, the table index can be omitted from the iteration specifications.

Table[Random[], {3}, {2}]

Another function that can be used for constructing general lists and arrays is Array. The arguments in Array are used in a slightly different way than the arguments of Table. The difference is perhaps best illustrated by looking at a few examples, or by reviewing the documentation for these functions. Here is an example showing the use of Array to construct an array of expressions where each element of the array has a head of f.

Array[f, {2, 2}]

There are many other functions that are available for constructing special types of expressions. For example, the Range function can be used to generate a list of integers.

Range[10]

Notice that this same list can also be constructed using Table or Array.

Table[k, {k, 1, 10}]

Array[Identity, {10}]

There is always more than one way to construct an expression. Sometimes the best approach involves Table or Array, and sometimes it is better to use a more specialized function, such as Range. Other functions that are frequently used to construct expressions include DiagonalMatrix, IdentityMatrix, Sum, Product, NestList, and NestWhileList.

Combining expressions

Basic functions for combining expressions include Join, Append, Prepend, and Insert, and set operations such as Union and Intersection.

The Join function can be used to combine the elements from two or more expressions into a single expression. For example:

Join[{1, 2, 3}, {10, 20, 30}, {99}]

Append is used to insert an element to the end of an expression, and Prepend is used to insert an element at the beginning of an expression. For example:

Append[Prepend[{10, 20, 30}, 0], 99]

The Insert function can be used to insert an element anywhere in an expression. The first argument in Insert gives the original expression, the second argument gives the element to insert, and the third argument position at which to insert that element. For example:

Insert[{10, 20, 30}, 99, 2]

The Union and Intersection functions do more than just combine expressions. These functions also compare elements in expressions and return a result with the elements sorted in a built-in canonical order.

Union[{23, 45, 17, 14, 17}, {14, 9, 45}]

Intersection[{23, 45, 17, 14, 17}, {14, 9, 45}]

You can also use the Sort function if you simply want the elements to be returned in sorted order.

Sort[{23, 45, 17, 14, 17}]

Getting parts of expressions

The Part function can be used to pick an element out of an expression. Here is an example in which Part is used to pick out the element at position 3 in a list with five elements. The notation data[[3]] is used for Part[data, 3].

data = {10, 20, 30, 40, 50};

data[[3]]

There are several other ways to use Part to get parts of an expression, some of which have already been mentioned in this tutorial. For a review, see the documentation for Part.

You can use the Take function to pick out a range of elements from an expression. For example, this input picks out the first three elements from the value of data.

Take[data, 3]

You can use Drop to discard a range of elements from an expression. For example, another way to pick out the first three elements in the value of data is to discard the last two elements. A negative number in the second argument of Drop indicates that the position should be counted from the end of the expression.

Drop[data, -2]

A qualitatively different way to select parts of expressions is to select parts that match some pattern or criterion. Two functions in Mathematica for this purpose are Select and Cases.

Here is an example showing the use of Select to pick out all of the even numbers in a list. The result includes all of the elements for which EvenQ gives True.

Select[{6, 5, 3, 8, 7, 7, 4, 4}, EvenQ]

Cases is similar to Select except that the second argument in Cases specifies a pattern. For example, the pattern _?EvenQ matches any expression for which EvenQ applied to that expression gives True. Patterns are discussed in the next section.

Cases[{6, 5, 3, 8, 7, 7, 4, 4}, _?EvenQ]

Patterns

A pattern is an expression that is used to specify a set of expressions. For example, the pattern _?EvenQ specifies the set of expressions for which EvenQ applied to that expression gives True.

Any expression in the set of expressions specified by a pattern is said to match the pattern. For example, the number 2 would be said to match the pattern _?EvenQ.

Mathematica includes a rich language for constructing patterns. Only a few of the more common types of patterns will be mentioned here. Patterns are covered in detail in Section 2.3 of The Mathematica Book.

The simplest non-trivial pattern is Blank[], which matches any expression. Blank[] is usually entered as a single underscore character _. The pattern {_, _}, for example, matches any list with two elements.

The pattern _h, or Blank[h], matches any expression with a head of h. For example, _List matches any list (or more precisely, any expression with a head of List), _Integer matches any integer, _Real matches any real number, and so forth.

The pattern ___ (three underscore characters), or BlankNullSequence[], matches any sequence of expressions. For example, {___} matches any list with any number of elements.

The pattern _?EvenQ mentioned at the beginning of this section is an example of a pattern based on PatternTest. In FullForm notation, _?EvenQ is PatternTest[Blank[], EvenQ]. In general, PatternTest[p,test], or p?test, matches any expression that matches p and for which test applied to that expression gives True. You can use PatternTest to define arbitrarily complicated tests for whether or not an expression matches a pattern.

Another way to set up your own test for whether or not an expression matches a pattern is to use Condition. Condition[p,test], or p/;test, matches any expression that matches p and for which test evaluates to True. Unlike the test in PatternTest, which is applied to the expression that matches the pattern, the test in Condition must itself evaluate to True for the pattern to match.

Patterns based on Condition are almost always used in conjunction with named patterns. Named patterns are discussed in Section 2.3.3 of The Mathematica Book. A named pattern is a pattern that is accompanied by a symbolic name. The name of a named pattern provides a convenient way to refer elsewhere in a pattern or rule to the expression that matches the pattern. The ability to give names to patterns is an enormously useful feature of Mathematica pattern matching.

For example, the Condition pattern p_ /; EvenQ[p] matches any expression for which EvenQ gives True. In this pattern, the symbol p is used as the name of a pattern. That name is used in the test expression, where it refers to the expression that matches the pattern.

Named patterns are also useful in rules. For example, in the rule p_Integer -> 2  p, the symbol p in the pattern p_Integer on the left side of the rule is the name of that pattern. The symbol p on the right side of this rule refers to the expression that matches the pattern on the left side of the rule. This particular rule replaces any expression that matches the pattern p_Integer with twice that expression.

{2, 3, Pi, 4}/.p_Integer -> 2  p

Names for patterns are entered using Pattern. In FullForm notation, p_Integer is Pattern[p, Blank[Integer]]. Special notations for Pattern, such as p_Integer, are almost always more convenient than the corresponding FullForm notations.

Using patterns

Patterns and rules can be constructed for performing almost any operation. Here are a few examples to illustrate some of the operations that can be done using patterns.

The Condition pattern p_ /; p > 5 is used in this example to select from a list the elements that are less than 5.

Cases[{1, 3, 7, 4, 1, 7, 2, 11, 1}, p_ /; p < 5]

This example shows a more complicated Condition pattern that selects from a list of pairs the pairs for which the first element is smaller than the second element.

Cases[{{2, 5}, {2, 1}, {2, 2}, {2, 3}}, {p_, q_} /; p < q]

Here is a rule that replaces all of the inexact real numbers (expressions with a head of Real) in an expression with exact numbers computed using Rationalize. The rule in this example is an example of a delayed rule. Delayed rules are discussed in Section 2.4.8 of The Mathematica Book. A delayed rule is needed here so that the right side of this rule is not evaluated until the expression associated with the named pattern is inserted into the right side of the rule. A delayed rule is entered using the notation lhs:>rhs, or in FullForm notation, RuleDelayed[lhs,rhs] using RuleDelayed rather than Rule.

((2.7 + x^(0.5))/(1.0 + 2.5  x)) /. 
      p_Real :> Rationalize[p]

Here is a rule that sorts the elements in a list. The pattern on the left side of this rule uses both Condition and BlankNullSequence. The pattern {r___, p_, q_, s___} /; p > q matches any list with two or more elements in which the adjacent elements that match the named patterns p_ and q_ satisfy p > q.  This rule is applied using ReplaceRepeated (expr//.rules) because it is necessary to apply this rule repeatedly for it to have the desired effect.

{26, 12, 15, 57, 35, 
        22} //. {r___, p_, q_, s___} /; (p > q) -> {r, q, p, 
          s}

Functional operations on expressions

Two of the most frequently used functions in Mathematica are Map and Apply. These functions are used to apply other functions to expressions or to the elements of expressions.

Here is an example showing a typical use of the Map function. In this example, the function f is applied to the elements of a list.

Map[f, {1, 2, 3, 4, 5}]

Here is an example showing a typical use of Apply. The head of the list in this example is replaced by f.

Apply[f, {1, 2, 3, 4, 5}]

These operations are useful in a wide variety of common situations. Here is an example in which Apply is used with Plus to compute the sum of a list of numbers. The Apply function replaces the head of the list {1,2,3,4,5} with Plus to give Plus[1,2,3,4,5] as an intermediate result, after which the Plus function computes the sum.

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

Here is an example in which Map handles the key step of converting a list of coordinate pairs into a list of Point graphics primitives. The Table function constructs a list of coordinate pairs, and Map applies Point to each element in that list.

data = Table[{k, Sin[k/5.0]}, {k, 0, 30}];

pts = Map[Point, data]

Show[Graphics[pts], Frame -> True]

There are many other functional operations that are available in Mathematica. Functional operations are discussed in Section 2.2 of The Mathematica Book.

Mapping over lists

It is worth mentioning that, for many functions, mapping over lists is effectively invoked automatically. For example, Factor automatically maps over the elements in a list.

Factor[{3 + 4  x + x^2, 1 + 2  x + x^2, 2 + 2  x}]

The same result can be achieved using Map, but Map is obviously not necessary here.

Map[Factor, {3 + 4  x + x^2, 1 + 2  x + x^2, 2 + 2  x}]

Mapping over lists is also done automatically when the Listable attribute is used. An attribute is a property of a symbol. Attributes are discussed in Section 2.5.3 of The Mathematica Book.

For example, although Map can be used for an operation such as computing the cosine of each element in a list, this same operation can be handled simply by applying Cos to the list.

Map[Cos, {1.7, 2.3, 0.5, 0.9, 1.3}]

Cos[{1.7, 2.3, 0.5, 0.9, 1.3}]

You can check that Cos has the Listable attribute using the Attributes function.

Attributes[Cos]

Level specifications

Many function such as Map and Apply accept optional arguments that specify the level within an expression at which they should operate. Most uses of level specifications can be understood by considering a few examples. Level specifications are discussed in greater detail in Section 2.1.7 and in Appendix A.3.6 in The Mathematica Book.

Level 1 in an expression corresponds to the elements of an expression. By default, the Map function maps at level 1.

Map[f, {{1, 2}, {3, 4}}]

Level 2 in an expression corresponds to the elements of the elements of the expression. The optional third argument in Map allows you to specify the level or levels at which to map a function. For example, the level specification {2} in Map specifies that a function should be mapped only to the elements of the elements of an expression.

Map[f, {{1, 2}, {3, 4}}, {2}]

Level 0 in an expression corresponds to the expression itself.

Map[f, {{1, 2}, {3, 4}}, {0}]

The default behavior of the Apply function is to operate at level 0 of an expression, which means that it replaces the head of an expression.

Apply[f, b[c[1], c[2], c[3]]]

You can use the optional third argument in Apply to specify expressions at some other level. Here is an example in which the level specification {1} is used to specify that the heads of the elements of an expression should be replaced.

Apply[f, b[c[1], c[2], c[3]], {1}]

In addition to Map and Apply, other functions that accept level specifications include Cases, Count, FreeQ, Level, MapIndexed, Position, Replace, and Scan.

Pure functions

A pure function is a function that is introduced without defining a value for a symbol. Pure functions are constructed in Mathematica using Function expressions.

To illustrate the use of pure functions, here is an example in which a function f is defined and then mapped on to the elements of a list of pairs. This function adds the elements in each pair.

f[p_] := p[[1]] + p[[2]]

Map[f, {{1, 2}, {3, 4}, {5, 6}, {7, 8}}]

This same operation can be performed using a Function expression. This avoids the need for the assignment to define the function.

Map[
      Function[{p}, 
        p[[1]] + p[[2]]], {{1, 2}, {3, 4}, {5, 6}, {7, 
          8}}]

Function expressions can also be entered without introducing symbols to represent the variable or variables of the function. The pure function Function[{p}, p[[1]] + p[[2]]] is equivalent to Function[Slot[1][[1]] + Slot[[2]]], which can be entered using the notation #[[1]] + #[[2]] &.

Map[#[[1]] + #[[2]] &, {{1, 2}, {3, 4}, {5, 6}, {7, 
          8}}]

Function expression can be set up for use with any number of arguments. For example, the function Function[{x, y}, x + y] takes two arguments and adds those arguments together. Here is an example showing the use of that function applied directly to two arguments.

Function[{x, y}, x + y][2, 3]

The equivalent function using Slot variables is (#1 + #2)&. Parentheses are included here to avoid ambiguity, even though they are not required.

((#1 + #2) &)[2, 3]

This function can be used in conjunction with Apply and a non-default level specification to add up the elements in each pair in a list of pairs.

Apply[(#1 + #2) &, {{1, 2}, {3, 4}, {5, 6}, {7, 
          8}}, {1}]

All of these operations can also be done using assignments to define functions. For example:

f[p_, q_] := p + q

Apply[f, {{1, 2}, {3, 4}, {5, 6}, {7, 8}}, {1}]

The choice of whether to use a Function expression or an assignment to define a separate function is normally a matter of personal preference. Function expression simply provide a convenient way of introducing functions without the need to assign new values for a symbol.

Exercises

Exercise 7.1 Constructing an expression

An occasionally useful technique for testing and debugging Mathematica programs is to observe the results after inserting random numerical values for any symbolic variables in the program.

Construct from the following list of variables a list of rules to for replacing each variable in the list with a value generated using Random.

vars = {xxy, xxz, xyy, yxx, yzz, yzy}

Such a list can be constructed in a variety of ways. The result should be similar to:

Use your list of rules to find the one symbolic variable in the following expression that is not among the six variables included in the value of vars. After inserting numerical values for the listed variables, this expression will collapse to a simple expression in which the extra variable should be obvious.

expr = -((11 + 11 xxz + 7 xyy + 21 yzy)/
     (1 + 5 xxy + 11 xxz + 11 yxx)^2) + 
   (2 xxy + 10 xxz + 6 xyy + 9 yzy + 2 yzz)/
    (3 + 11 xxy + 5 xxz + 14 xyy + 5 yzy)^2 + 
   (5 + 10 xxy + 7 xxz + 11 xyy + 8 yxx + 4 yzz)^2/
    (6 xxy + 9 xxz + 8 xyy + 2 yxx + 9 yzy) - 
   (4 + 2 xxy + 4 xxz + 10 xyy + 8 yxx + 5 yzy + 4 yzz)/
    (3 xxy + 6 xxz + 4 yxx + 6 yzz)^2 + 
   (11 xxy + 8*xxz + 5 xyy + 6 yxy + yzz)/
    (1 + 8 xxy + 7 xxz + 11 yxx + 8 yzz)^2 + 
   (4 xyy + 9 yxx + 3 yzy + 6 yzz)^2/(3 xxz + 19 xyy + 15 yzz);

Exercise 7.2 Selecting elements in a list

Here is a polynomial with three negative roots, two positive roots, and two complex roots.

sol = 
      NSolve[205 + 1850  x + 2064  x^2 + 328  x^3 - 167  x^4 - 84  x^5 + 
            7.5  x^6 + x^7 == 0, x]

This picks out all of the negative roots.

Select[sol, Negative[x /. #] &]

The same result can be obtained using Cases.

Cases[sol, {_ -> _?Negative}]

Use Select or Cases to construct a list that includes all five real roots.

Exercise 7.3 Discarding outliers from data

Here is a list of 31 numbers, four of which are significantly larger or significantly smaller than the rest.

data = {50.30, 49.85, 46.11, 48.04, 52.34, 56.06, 48.22, 48.85, 
          48.62, 48.46, 51.00, 50.38, 97.10, 56.05, 48.50, 51.43, 49.86, 
          53.92, 2.35, 48.25, 52.15, 54.18, 52.79, 54.62, 88.6, 47.54, 51.34, 
          47.31, 45.10, 5.55, 52.49};

The four outliers can be seen in a plot of data.

ListPlot[data, PlotStyle -> PointSize[0.02], 
      PlotRange -> All]

Use Select to discard all elements in data that are not between 40 and 60. The result should be a list with 27 elements. You can also check your result by making another plot.

Exercise 7.4 Operating on the elements in a list

Show a way to construct a list that gives the square of the difference between the elements in each pair in the following list.

data = {{1.04, 0.42}, {2.41, 2.66}, {6.52, 7.15}, {9.20, 
            6.01}, {5.79, 1.40}, {1.33, 4.95}, {8.63, 5.30}, {8.66, 
            4.99}, {4.65, 6.58}, {3.45, 2.76}, {6.03, 4.61}, {5.48, 
            0.49}};

The result will be:

Exercise 7.5 Selecting parts of a matrix

Construct a vector consisting of the sum of the first two columns in this matrix. You can use the Part function to pick out elements, rows, and columns from a matrix.

m=(({{12, 0, 2, 3}, {11, 5, 17, 2}, {2, 5, 4, 13}, {3, 1, 8, 6}, {17, 2, 11, 9}, {3, 11, 10, 5}}))

Exercise 7.6 Using listability

Show how to construct the result shown here starting with the vector v and without using Map.

v = {x1, x2, x3, x4, x5};

Map[(1 + #^2) &, Map[Cos, v]]

Exercise 7.7 Patterns and rules

Here is a simple Graphics expression containing a Line primitive. The argument of the Line primitive is a list of pairs of real numbers. Each pair of numbers specifies a coordinate in two dimensions.

g = 
      Graphics[Line[{{0.0, 0.0}, {1.0, 1.0}, {2.0, 0.0}, {3.0, 1.0}, {4.0, 
              0.0}, {5.0, 1.0}, {6.0, 0.0}, {7.0, 1.0}, {8.0, 0.0}, {9.0, 
              1.0}, {10.0, 0.0}}], Axes -> True]

Show[g]

Apply a rule to this Graphics expression to replace all pairs of real numbers {p_Real, q_Real} with {q, p}. so that the coordinates in each pair are reversed, and display the result. This replacement corresponds to exchanging the vertical and horizontal dimensions, or equivalently, to reflecting the image across a diagonal.

Now apply the same rule to the  Graphics expression generated by the following Plot input, and display the result.

Plot[Sin[x], {x, 0, 2  Pi}]

Exercise 7.8 Levels in expressions

Here is 3×3 array of pairs of symbols.

m = {{{x1, y1}, {x2, y2}, {x3, y3}}, {{x4, y4}, {x5, y5}, {x6, 
              y6}}, {{x7, y7}, {x8, y8}, {x9, y9}}};

MatrixForm[m, TableDepth -> 2]

Show a way to use Apply to construct from this array the following array, the elements of which are the sum of the elements in the corresponding pair in the original array:

Exercise 7.9 Finding elements in a list

This assignment introduces a list of integers.

v = {3, 1, 5, 7, 7, 
          4, -3, -1, -3, -4, -5, -2, -1, -1, -2, 0, 2, 3};

This input picks out the last negative number in the list.

Extract[v, Last[Position[v, _?Negative]]]

Find an input that picks out the first even element in the list.

Exercise 7.10 Iteration to a fixed point

The following input illustrates a method of solving the equation x == Exp[Exp[-x]]. This method works by applying the pure function Exp[Exp[-#]] & to a number over and over until the result is the same as the original number. This method is called iteration to self-consistency, or iteration to a fixed point.

FixedPointList[Exp[Exp[-#]] &,  10.0]

The result can be checked using FindRoot. The algorithm used by FindRoot is almost always faster and more reliable than iteration to a fixed point, but the essential idea of iteration to a fixed point is nevertheless useful in a surprising variety of calculations.

FindRoot[Exp[Exp[-x]] == x, {x, 1}]

Use iteration to a fixed point with FixedPoint or FixedPointList to find a solution to the equation Cos[Sin[x]] == x.