Mathematica Tutorial

Author: Michel Schellekens
Date: Aug 28, 2022
University College Cork
School of Computer Science and Information Technology
Email: m.schellekens@cs.ucc.ie

These notes clarify aspects of Mathematica that can be confusing for a beginner or can benefit from a more detailed introduction:

a Mathematica sequence and its different behaviour compared with standard Mathematica expressions.

a  detailed definition of a position in an expression based on its tree.

"heads" of atoms and their inaccessibility as parts.

Additional sections are included on functions, Apply and Map.

These notes are kept on www.notebookarchive.org and may be occasionally updated. Comments are welcome of course.

It is assumed that a running version of Mathematica has been set up to execute examples following a Shift-Return. 

Part of the notes benefitted from other resources including R. J. Gaylord’s notes on Wolfram Language Fundamentals (WLF). 

The WLF-reference is a useful resource to study further aspects of RuleDelayed applications and the order of execution in the rule base as well as functional programming. These are not addressed here.   https://library.wolfram.com/infocenter/MathSource/5216

Nuts and bolts

Rewrite rules

Mathematica is a functional language and uses rewrite rules. The main constructs of the language are expressions. Expressions represent functions. 

The rewrite rules are carried out automatically on expressions unless the user specifies otherwise. For instance: the expression 1 + 3 + 2 is automatically evaluated to 6.

1 + 3 + 2

To prevent Mathematica from automatically printing an evaluation of an expression one can use a call to HoldForm (or related commands with varying effects such as Hold or Unevaluated.).

HoldForm[1 + 3 + 2]

To learn about the effects of HoldForm, use the question mark format as in ?HoldForm, followed by Shift-Return to evaluate.

?HoldForm

Expressions

Functions are represented by expressions in Mathematica.

f[x,y] represents a function with two arguments. 
Plus[x,y] represents the sum-function with arguments x and y. 

Mathematica internally represents x + y as the expression Plus[x,y]. 

We can check this via the FullForm command:

FullForm[x+y]

FullForm[f[x,y]]

The expressions f[x,y] and Plus[x,y] share a similar structure. The first-occurring symbol in each expression, f and Plus respectively, form the head of the expression.  The subexpressions x and y form the arguments of the expression. 

It is easy to change the head of an expression using the Apply operator. Recall that operators (in mathematics) transform functions into new functions.  ?Apply displays the documentation for Apply.

?Apply

Apply[Plus,f[x,y]]

FullForm[x+y]

The head f of the expression f[x,y] has been replaced with the new head Plus. The Apply operator can also represented in infix-notation by @@

Plus @@ f[x,y]

Lists

Mathematica lists  are represented using curly {   } brackets.  Lists correspond to the standard list data structure (and the standard mathematical notion of a sequence). 

{1, 3, 2, 3, 0} and {0} are Mathematica lists.  

The Apply operation enables quick summing of list elements by replacing the head List of a list with the new head Plus:

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

FullForm[{1,2,3}]

Apply[Plus, {1,2,3}] replaces the head of {1,2,3} with Plus. 
{1, 2, 3} is internally represented by the expression List[1,2,3]. Thus {1,2,3} has List as head. This head is replaced with Plus by the Apply operator.

Plus @@ {1, 2, 3}

Apply[Plus, {1, 2, 3]] is internally represented as Apply[Plus, Head[1, 2, 3]]. After replacing the head this results in Plus[1, 2, 3] and Mathematica evaluates this expression to 6. 

This series of steps involved in this evaluation can be displayed using Trace

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

Subtleties of sequences

Mathematica Sequences consists of a number of expressions separated by commas. 

Sequences are indicated by square brackets. This is merely an informal shorthand notation, not to be used in Mathematica. 

Sequences are used to indicate arguments of functions. This is consistent with the fact that the function expression f[1, 2, 4] indicates that the function f is applied to the arguments 1, 2 and 4. 

The symbolic part [1, 2, 4] of the function expression is referred to in Mathematica as a sequence. Internally, Mathematica can treat [1, 2, 4] for instance as Sequence[1, Sequence[2, 4]], however this is not reflected by FullForm (for readability and reflecting the fact that “Sequence” essentially is a head indicating the expression has “no head”).

FullForm[f[1, 2, 4]]

As observed, Sequences play a special role in Mathematica (distinct from general expressions). 

Consider the sequence [1, 2, 3]. This is merely a short-hand notation for Sequence[1, 2, 3]. This is the only format in which sequences can be used directly in Mathematica code (aside from direct use in function expressions, such as f[1, 2, 4]). 

When discussing sequences informally we use the notation [1, 2, 3]. Using this informal notation in Mathematica will lead to errors. For instance, retrieving the Head of the sequence [b, c] results in an error.

Head[[b,c]]
(MessageTemplate[Part, pkspec1, "The expression \!\(\*RowBox[{\"b\"}]\) cannot be used as a part specification.", , , , , Local])
Head[[b,c]]

The error derives from the fact that Mathematica does not recognize [b, c] as a sequence (the proper format is Sequence[b,c]). Instead, the double brackets [[ and ]] in Head[[b, c]] are interpreted as a call to the Part function [[  ]] used to obtain parts of an expression (see more on this later). The error "The expression b cannot be used as a part specification" indicates that b is merely a formal symbol that does not indicate a part.

The informal sequence notation [1, 2, 3] intuitively represents the fact that 1, 2 and 3 are used as function arguments (in a term), hence the outermost brackets will be dropped. This is ensured by a Mathematica "splice" rule.

The square brackets used in a sequence [1, 2, 3] essentially are function-notation brackets, unrelated to the arguments 1, 2 and 4. They merely serve to contain the function arguments ("inputs"). 

Mathematica merely uses sequences such as [1, 2, 3] to represent such function arguments. The outermost square brackets are ignored in the sequence  [1, 2, 3] (as they can be interpreted as part of the function notation f[  ], where f operates on three arguments 1, 2, 3 (forming the sequence). 

Sequence[expr1, expr2,…]

represents a sequence of arguments to be spliced automatically into any function.

A sequence is automatically "spliced in" (via an (implicit) "rule" reducing an expression like f[a, Sequence[b,c], d] to f[a, b, c, d].

f[a, Sequence[b, c], d]

We discuss rules later. Rules form a powerful part of Mathematica.
The rule behavior of sequences leads to some (at first) surprising outcomes. Let's try to repair the error we obtained when using Head[[b, c]] by replacing the sequence [b, c] with its proper Mathematica representation Sequence[b, c]

Head[Sequence[b,c]]

Head[Sequence[b, c]] returns an error in some systems, but instead, here, it gives an unexpected outcome c[Symbol]. The problem is that Head has no "Hold" attributes, i.e. Sequence is evaluated within Head. 

By the above rule this means that Head[Sequence[b, c]] reduces to Head[b, c], i.e. Head is called with two arguments. The Trace output (which displays steps in a Mathematica computation) sheds more light on this :

Trace@Head[Sequence[b, c]]

A useful application of a sequence is to serve as a (complete) "head-remover" as in the following example: 

The Mathematica notation 

/. List -> Sequence 

specifies a rule that replaces every List head with the Sequence head. 

This results in a further (automated) reduction by the splice rule. 

*All* list brackets (i.e. all List heads) are removed by splice (contrary to list flattening (illustrated below) which removes all brackets bar the outermost one, returning a list as opposed to a sequence of arguments).

f[{{a, b}, {c, d}, {a}}] /. List -> Sequence

Flatten[{{a, b}, {c, d}, {a}}]

Due to the splicing process a sequence may not behave as one might expect  a Mathematica expression to behave.  For instance, consider the following FullForm evaluation.

If Sequence[y, z] would simply behave as any other Mathematica expression one would expect FullForm[Sequence[y, z]] to return Sequence[y, z]. Instead, an error is produced.

FullForm[Sequence[y,z]]

The reason is that FullForm[Sequence[y,z]], due to splicing, results in FullForm[y,z]

FullForm[y,z]

Clearly [y, z] does not have the internal representation Sequence[y, z] in Mathematica as one might expect (on the basis of, say,  x + y having internal representation Plus[x, y]). 
Hence [y, z] cannot be used as Mathematica input (unlike x + y).

To avoid errors it is useful to restrict the use of sequences in Mathematica to splicing applications (adding arguments to functions) or to removing heads of expressions as outlined in the example given above and re-illustrated next, using the Apply operator.

Replace the Flatten command by an Apply operator application 

As an exercise, produce the list {1, 2, 3, 4, 5} by removing the nested brackets (not the outer brackets) from the list {1, {2, {3, {4,5 }}}} using the Apply operator rather than the Flatten command. 

This can be done by replacing the List heads in the nested lists only with Sequence heads which are then spliced away by the splicing rule.  

Apply Sequence at levels 1 to 3 (where 0 is the level of the outermost List head) as follows:

Apply[Sequence, List[1,List[2,List[3,List[4,5]]]],{1,3}]

The final parameter {1, 3} in the above expression indicates that levels 1 to 3 (i.e. 1, 2 and 3) need to have the List head replaced by a Sequence head. We introduce levels formally later on in these notes. 

At this stage, the level can simply be regarded as the nesting depth of List heads with the first List occurrence (in left-to-right order in the expression) having level 0. The splicing rule for sequences (which drops the square sequence brackets) results in the flat (i.e. non-nested) list {1, 2, 3, 4, 5}.

Deleting the head of a sequence
https : // mathematica.stackexchange.com/questions/50381/how - to - completely - delete - the - head - of - a - function - expression (see this link for further info). 

You can Delete the head of the expression as follows (where 0 refers to the location of the head. See more on this in the section on Part and Level) :

Delete[#, 0] & /@ {Cos[a], Sin[b], Tan[c]}
{a, b, c}

With version 10 operator forms :

Delete[0] /@ {Cos[a], Sin[b], Tan[c]}
{a, b, c}

The documentation for Delete specifies that deleting the head of a whole expression makes the head Sequence.

Delete[Cos[a], 0] produces Sequence[a]

This evaluates to a.

In summary: difference between lists and sequences

The sequence and list terminology may be confusing at first since lists and sequences are the same in the context of mathematics. For Mathematica we illustrated important differences for the case of sequences. 

The use of a sequence typically takes place in the context of a "rule", namely a splice operation, splicing additional arguments into a given expression. Sequences hence serve to expand other expressions (by adding extra arguments using splicing) or to shrink other expressions (by replacing heads with Sequence to remove the original heads through further splicing rule applications).

A Mathematica list intuitively corresponds to the list data structure, denoted in Mathematica by {a1, ..., an}. Lists in Mathematica are displayed using curly brackets.                               {3, {3, 5, {6}}} is a list.

A Mathematica sequence consists of a number of “expressions” separated by commas. These expressions serve as arguments for functions.

Arguments for expressions are written in the form of sequences [a1,...,an]  (with outer square brackets that are implicitly intended to be omitted). A sequence intuitively correspond to the arguments a1, ..., an of a multi-argument function f[a1,...,an]. For instance arguments can correspond to the elements of a list {a1, ... , an}, without the list’s encompassing brackets (where a list, in Mathematica, is internally interpreted as a (function-)expression List[a1,...,an].).

A final illustration of the effect of the splicing rule in a combined use of lists and sequences:

{Sequence[a,b]}

Expressions in Mathematica: formal intro

Overview

This section provides a high level overview of expressions. The nature of expressions will become clearer through examples using the TreeForm function which displays the trees associated with expressions.

Expression trees (terms)

Mathematica uses n-ary expressions t of the form f(t1, ..., tn) where n >= 0, each ti is a sub-expression (aka an argument of f) and f is referred to as the head of the expression t. 

In case n = 0, t = f is a constant value, referred to as an atom. 
Atoms can be: numbers, strings or symbols. 

Expressions (aka “terms”) are naturally represented as (expression) trees. 

The left-most part of an expression is referred to as the “head” of the expression. Intuitively the head indicates the type of the data structure under consideration, for instance List. The head forms the root of the expression tree. 

Lists in Mathematica are displayed using curly brackets, so {3, {3, 5, {6}}} is a list. The outermost curly brackets intuitively form the head of the expression. 

A list in Mathematica is an n-ary expression as discussed above.  Mathematica interprets a list {3, {3, 5, {6}}} (internally to the system) as an expression: List[3,List[3,5,List[6]]]. The head of this expression is List (coloured in Magenta for highlighting only). 

This fact can be verified by calling the FullForm function in Mathematica, which displays an expression in “full form” including explicit names for the heads of each subterm.

FullForm

FullForm[{3,{3,5,{6}}}]

TreeForm

Each expression corresponds to a tree, with the head at the root and a subexpression (subtree) at each internal node. Leaves are atoms. TreeForm[t] displays the tree structure of the expression t

Display the tree of the expression {3, {3, 5, {6}}} via TreeForm:

TreeForm[{3,{3,5,{6}}}]

Mathematica programs operate over expressions, or rather over their tree-representation. Hence Mathematica includes operations to access subtrees of expressions etc. to which we return in the section on Level, Position and Parts.

Atoms

All expressions in the Wolfram Language are ultimately made up from a small number of basic or atomic types of objects. 

Atoms are the leaves of the expression tree, i.e. constant expressions f(t1 ... tn) (for which n = 0, i.e. expressions with a head f and zero arguments). 

Atoms have heads that are symbols that can be thought of as “tagging” their types. For instance the list element 3 in the above TreeForm is an atom. The Mathematica operation Head gives its type (integer).

Head[3]

The Head of an atom is an implicit property of the atom, i.e. is not displayed in the FullForm nor in the TreeForm of the atom. Atoms however have no head that can be altered (say, with the Apply function).

Head[3]

Apply[List,3]

FullForm[3]

TreeForm[3]

One can determine the head (i.e. the type) of an atom using the Head function, but one cannot alter the head.

How far does the atomic approach go? An integer is viewed in Mathematica as an “atomic” entity. It is not represented via an expression tree reflecting its construction from other parts, say in decimal, binary or unary.

The use of the terminology “atom” may seem strange when one learns about the “head” of an atom, which seems to indicate that an atom has a part (the head). The head is as a “type” of the atom, e.g. integer. The head is not part of the actual construction of, say, an integer, i.e the integer is not further divisible (into smaller parts that would be combined via an expression tree into the integer). This is reflected in its TreeForm which does not display the Head. In other words: the integer 3 in Mathematica is displayed as 3 (never as, say, Integer[3]).

The decision of what parts constitute atoms regards a choice of a base-case at which point expression trees are no longer used to represent the construction of an item from basic parts. In this sense an atom is a “smallest” element, i.e., can no longer be further divided into parts by an expression tree (but does have a “type-head”, which remains implicit, i.e. not displayed in FullForm).

Kinds of atoms

There are three kinds of atoms:

Symbols (a letter followed without interruption by letters or numbers)

Head[a]

Head[a34bfQ]

Head[Head]

Head[Pi]

Numbers

There are four types of Numbers: Integers, Rational Numbers, Real Numbers, Complex Numbers

Head[9]

Head[9.1]

Head[2]

Head[3/4]

Rational

To enter an imaginary number, type: Esc-i i-Esc

Head[I]

Strings

A string comprises of characters enclosed in quotes

Head["hello"]

Recall that the head of an atom indicates what type the atom is. The head of an expression that is not an atom can be returned as the head of the expression by the Head command: Head[f[t1,...,tn]] returns f (for a non-atom). In case of an atom, e.g. 5, Head[5] returns Integer (i.e. it does not return 5). 

The FullForm of an atom is the atom, except for rational and imaginary numbers.

FullForm[6.45]

FullForm[6/8]

Head[6/8]

To enter an imaginary number, type: Esc-i i-Esc

I

1 + 3I

FullForm[1 + 3I]

FullForm[1 + 3]

Note: FullForm behaves differently on certain number atoms. for integers and real numbers, FullForm simply reproduces the corresponding atom. For rationals and imaginary numbers, FullForm displays a Head (Complex or Rational) and arguments. Again, this head cannot be replaced.

FullForm[3]

Head[3]

FullForm[3/5]

Head[3/5]

Even though some atoms do have heads, as does 3/5 (the head Rational) it still is not the case that the head can be replaced via Apply.

Apply[Complex,Rational[3,5]]

Obtaining parts of expressions

Part obtains the part of an expression at an indicated location in the expression tree. 

0 is the location of the root
1 is the location of the left branch from the root
2 is the location of the right branch from the root

FullForm[{3,4}]

TreeForm[{3,4}]

0 is the location of the root in the tree

Part[{3,4},0]

1 is the location of the leftmost branch (from the root)

Part[{3, 4}, 1]

1 is the location of the rightmost branch (from the root)

Part[{3, 4}, 2]

Part can be abbreviated using the notation [[  ]]

{3,4}[[1]]

{3,4}[[2]]

“Part 0” returns the head of the expression.

{3, 4}[[0]]

(3 + I)[[0]]

(3 + I)[[1]]

The error arises since imaginary numbers are atoms, hence only have a head (that cannot be altered), and no other parts. This error arises even though FullForm will display the two parts.

FullForm[(3 + I)]

In the next section we discuss Lists as an example of expression trees and introduce the notions of level and position in trees. We also continue the discussion of the Part function of an expression acting on positions occurring deeper in the tree (i.e. deeper than levels 0 or 1).These notions generalize in a straightforward way to any expression tree (other than trees arising from Mathematica Lists).

Lists, Level, Position and Part functions

Lists

Lists are constructed using curly brackets

{1, 2, 4, 4, 5} is a list
{ } is the empty list

In Mathematics a set is denoted by curly brackets.

In Mathematica a set is represented by a sorted list without repeated elements (a representative of the equivalence class of sets in their usual unordered presentation). 

Note that lists are enumerated starting from position 1 as is the case in mathematics 
(not from index 0, as in computer science).

Generating lists

A list of integers 1 up to k can be created by Range[k] which returns the list {1, ... , k}

Range[4]

Another way to generate a list is through ConstantArray[k, l], creating a list containing l copies of k, where k could be an integer or a list etc.

ConstantArray[{2},3]

{{2},{2},{2}}

Accessing subexpressions using “level” and “position” (encoding a path)

Path to a subexpression

Each occurrence of a subexpression u in an expression t is determined by a unique path in the expression-tree of t leading from the root (the head of t) to the occurrence of u. 

Such a path from the root to a subexpression u is encoded by the Mathematica notion of position. 

A position is a sequence of integers indicating which branch of the tree is to be followed per level to reach u from the root.

Level in an expression tree

Subexpressions  in the expression tree residing at the same distance from the root are said to reside at the same level. Each level has a fixed number of subexpressions residing at that level. 

The level of a subexpression  u  =   (the number of nodes on path from root to u) -1 
                                                                 =   the number of edges on path from root to u

The list-expression {{1, 2}, {3, {4, 5}}} contains:

the outermost brackets { and } at level 0

the sub-lists {1, 2} and {3, {4, 5}} at level 1

the elements 1, 2 and 3 and the sublist {4, 5} at level 2

the elements 4 and 5 at level 3

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

A position encodes a path to an occurrence of a subexpression

A position of an occurrence of a subexpression u in an expression t is a list of integers {n1, ... , nk} such that:

If the subexpression u is an atom (occurring at a leaf of the expression tree) or a composite expression u = f[u1,...,ul] (where l is at least 1) then all integers in the list {n1, ..., nk} are strictly positive.

The only value in the position(-list) {n1, ..., nk} allowed to be 0 is the final value nk (where k can be 1). The integer-value 0 is reserved to indicate an occurrence of a subexpression u constituting a (non-atomic) head in the expression t.

A position(-list) {n1, ..., nk} (for an occurrence of subexpression u in t) determines the path to be followed in the expression tree of  t, starting from the root (the head of t) and ending at the occurrence of the subexpression u under consideration.

Convention: the position of the subexpression t in the expression t is the empty sequence { }.

A position(-list) of a strict subexpression u that is an atom or a composite expression u = f[u1,...,ul] (l at least 1) records the path in the expression tree leading to u (from the root) as a list of strictly positive integers {n1, ..., nk}:

Starting from the root, take the n1-th branch in left-to-right order (among branches emanating from the root to a node at level 1), then,

at the resulting node at level 1, take the n2-th branch in left-to-right order (among the branches emanating from the node under consideration to a node at level 2), then,

take the n3-th branch in left-to-right order (among the branches emanating from the node under consideration to a node at level 3), ...

until  one reaches the occurrence of the subexpression u at level k taking the nk-th branch at the node on the prior level.

A position list {n1, ..., nk} with nk = 0 indicates that u resides at level k - 1 as a (non-atomic) head occurring in t.

Example 1: the list {1, 2, 4} has integer elements 1, 2, 4. The FullForm of this list is : List[1, 2, 4]. 
Each element, 1, 2, and 4, in this list occurs at level 1. 

Recall that the position of a (non-strict) subexpression t in the expression t is set to the empty list { }. Hence the position of the subexpression {1, 2, 4} in the expression {1, 2, 4} is { }. 

The head of the list {1, 2, 4}, i.e. List in List[1, 2, 4] occurring at level 0, has position {0}.

The positions of the elements 1, 2 and 4 (at level 1) are the lists {1}, {2} and {3} respectively.

Example 2: The position of any sub-list occurrence in the expression { {1, 2}, { 3, {4, 5} } } is determined by considering the TreeForm of the list-expression { {1, 2}, { 3, {4, 5} } }:

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

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

Level 0 : the root position is {0}. This is the position of the head-occurrence List in List[List[1,2],List[3,List[4,5]]]

Level 1 : 
          Position of sublist {1, 2} : {1} (left path from root)    
          Position of sublist {3, {4, 5}} : {2} (right path from root)
          Position of the (non-atomic) head-occurrence List in List[List[1, 2], List[3, List[4, 5]]] is: {1, 0}
          Position of the (non-atomic) head-occurrence List in List[List[1, 2], List[3, List[4, 5]]] is: {2, 0}
          
Level 2 : 
	Position of 1: {1, 1} 
		(left branch from element (i.e. List) in position {1})
	Position of 2 : {1, 2} 
		(right branch from element in position {1} (i.e. List))
	Position of 3 : {2, 1} 
		(left branch from element in position {2}  (i.e. List))
	Position of {4, 5}: {2, 2}
		(right branch from element in position {2} (i.e. List))
	Position of the (non-atomic) head-occurrence List in List[List[1, 2], List[3, List[4, 5]]] is: {2, 2, 0}

Level 3 : 
	Position of 4 : {2, 2, 1} 
		(left branch from element in position {2, 2}  (i.e. List))
	Position of 5 : {2, 2, 2} 
		(right branch from element in position {2, 2}  (i.e. List))

If the subexpression u is an atom (occurring at a leaf of the expression tree) or a composite expression u = f[u1,...,ul] (with l at least 1) then the length k of the position {n1, ..., nk} (with strictly positive integer elements) is the number of levels one must go down in the expression tree (of t) to reach u.

If the subexpression u is a (non-atomic) head occurring in t then the length k of a position {n1, ..., nk} with nk = 0 is 1 more than the number of levels one must go down in the expression tree to reach u.

The Mathematica function Position[t,u]

The Mathematica function Position[t, u] returns a list of all positions at which the subexpression u occurs in t. 
The following examples all regard a subexpression u that occurs only once in t (i.e. has only one position in t).

Position[{1,2},{1,2}]

The Mathematica function Position[t, Head[t]] always includes {0} (the position of Head[t] at the root of the expression tree for t)

Position[{1,2},List]

Position[{1,2},1]

Position[{1,2},2]

Position[{1,{2,3}},2]

An occurrence of a subexpression that consists solely of a head always has a position that has 0 as final list element . 0 indicates that the subexpression u occurs at the level under consideration and not further down a branch to the a subsequent level.

TreeForm[f[a,g[b]]]

Position[f[a,g[b]],g]

Zero is only used to indicate a subexpression that is a non-atomic head, i.e. a head not residing at a leaf. 
Atoms are indicated by the (non-zero) branch-number n to be followed to reach the leaf at which the atom resides.

Position[f[a,g[b]],b]

Replacing the function Position[x, y] by the user-defined function positionsList[x, y]

The function Position[x,y] in Mathematica returns a list with all positions (i.e. a list of lists) of each occurrence of a subexpression y in the expression x. This terminology is confusing since Position[x, y] refers to a list of positions rather than a single position. We define a new function to eliminate confusion: 

positionsList[x, y] returns the list of all positions at which the subexpression y occurs in the expression x.

positionsList[x_, y_] := Position[x,y]

positionsList[{1,2,2},2]

From here on we use the function "positionsList" instead of "Position".

The following command determines the position of the head of the list {1, 2, 2}. The head always occurs in position {0}. The function positionsList in this case returns the list of all positions of the head: {{0}}.

positionsList[{1,2,2},Head[{1,2,2}]]

TreeForm[{1,{2}}]

positionsList[{1,{2}},Head[{1}]]

positionsList[{1,{2}},1]

Note that  positionsList[x, y] returns a list of flat lists, i.e. a list of non-nested lists. 

Try out the Flatten function which “flattens” a list to a non-nested version.

Flatten[{{1, 2}, 3, {4, {5, 6}}}]

For the particular case of flat Lists, positionsList[L, a] obtains all the positions of an element a in a list L.

positionsList[{3,3,6,3,4,4},3]

positionsList[{3,{4,5}},4]

positionsList[{3,{4,5}},Head[{3,{4,5}}]]

The result is correct as indicated by TreeForm of {3, {4, 5}}, which has two occurrences of the head List.

TreeForm[{3, {4, 5}}]

Extracting subexpressions via a position

Extract[t, position] obtains a subexpression at the given position. 
Consider the example:

positionsList[{3, {3, 5, {6}}},6]

The function positionsList[expr, subexpr] returns the list of positions {{2, 3, 1}}. 
Retrieve the subexpression at position {2, 3 , 1} using the Extract function:

Extract[{3, {3, 5, {6}}},Flatten[{{2,3,1}}]]

Or, using the position directly as {2, 3, 1}:

Extract[{3, {3, 5, {6}}},{2,3,1}]

Extracting a list of subexpressions via a list of positions

Extract[t, positionsList] obtains a list of all subexpressions at the positions in the positionsList.

Using the list of positions means a list of the subexpressions at those positions is returned:

Extract[{3, {3, 5, {6}}},{{2,3,1}}]

Extract[{3,{3,5,{6}}},positionsList[{3,{3,5,{6}}},3]]

Accessing subexpressions via the Part function

Part (denoted by [[  ]]) obtains a subexpression at a position

We can obtain elements at a given position from a list (or any expression) via the Part function [[  ]]. 
More generally, the Part function retrieves a subexpression (of an expression) at a given position. 
However, the Part function does not operate over actual positions (i.e. flat lists of integers {n1, ..., nk}) but over the elements of that list (i.e. the arguments of the list-expression): n1, ..., nk

Accessing nested subexpressions, e.g the element 4 in {3, {4, 5}}, can be achieved as follows using the Part function:

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

Or equivalently, in one go, as

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

Using Part with the actual position (i.e. a flat list {n1, ..., nk}) does not work, as illustrated by the following example. 

The following command accesses first and second element of the given list and interchanges these based on the position 1, 2 which is indicated as required to become 2, 1. The command does not return the expression in position {2, 1} as one might expect.

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

The expression 4 in position {2, 1} is returned via the following Part application:

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

You can use the Part function as usual to access subexpressions

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

in general [[i]] ... [[j]] can be replaced by [[i, ..., j]]

positionsList[{3, {3, 5, {6}}}, 6]

To use part one has to drop the double list brackets in the positions-list {{2, 3, 1}}

{3, {3, 5, {6}}}[[2]][[3]][[1]]

The Flatten function removes all brackets of a list bar the outer ones.

Flatten[{{2,3,1}}]

To remove all brackets one can use the /. List -> Sequence replace rule.

{3, {3, 5, {6}}}[[positionsList[{3, {3, 5, {6}}}, 6] /. List -> Sequence]]

The Part function cannot be used to simultaneously obtain two elements in different positions. This is because Part is executed sequentially:

testlist={3,{4,5}}

testlist[[1,2]]

We can select the head of each sub list simultaneously as follows:

(#[[0]])& /@ testlist

Note that this application necessarily works at the same level in the expression tree.

If we want to get the first and second element of the first and second list (residing at different levels) we can use Extract which allows one to specify multiple positions (which are accessed in parallel rather than sequentially):

Extract[testlist,{{1},{2,1}}]

Functions

Pre-defined functions on lists

Mathematica's predefined functions are capitalized as are their sub parts (!).

User-defined functions (to be introduced later on) start with lowercase letters, but may contain capitalized sub parts.

Length[L] obtains the length of a list L

Length[{1,2,4}]

More generally:

The length of an expression f(a1, ... , an) is n 

Length calculates the number of arguments in an expression

Length[a + b + c]

Mathematica includes symbolic manipulation, so 2 + 3 is evaluated to 5 (unless otherwise indicated with HoldForm). 

5 is an atom, which has no arguments (only a “head”, i.e. 5) and Length[5] = 0

Length[2 + 3]

In this case the length of the expression is not 2 as one might expect, because 2 +3 immediatly evaluates to the atom 5.

Verify this by following the Trace of the computation:

Trace[Length[2 + 3]]

FullForm[t] displays the structure of the expression t

FullForm[{3,{3,5,{6}}}]

TreeForm[t] displays the tree structure of the expression t

TreeForm[{3,{3,5,{6}}}]

Position[t, u] obtains all the positions of a subexpression u in expression t (note “Position” is singular though position may return several values, depending on how many times a occurs in the list L). Position returns a list of the positions of an element as a list of (flat) lists.

Position[{3,{3,5,{6}}},3]

The subexpression 3 occurs in positions {1} and in {2, 1} in the list {3, {3, 5, {6}}}

TreeForm[{3,{3,5,{6}}}]

Interpreting the position {2, 1}: starting from the root of the tree, proceed along the second branch (in left-to-right order), leading to level 1. 2 is followed by 1 in {2, 1}, i.e. we proceed to level 2 via the first branch (in left-to-right order), arriving at the subexpression 3. 

The position of a deeper nested element, such as 6, occurring at level 3, is still indicated by a list of (flat) lists of integers. (A flat list is a list that does not contain sublists. For instance a list returned by Flatten)

Position[{3,{4,5,{6},6}},6]

2 indicates the second branch (at level 1), 3 the third branch (at level 2) and 1 the first branch (at level 3) in the tree.

Position[t, x, 1] give the position of the subexpression x in expression t from level 1 to level 1.

Position[t, x, {1}] gives the position of x in t at level 1.

Position[{1,1,{1,{1}}}, 1, {1}]

Position[t, x, 3] gives the position of x in t from level 1 up to level 3.

Position[{1,{1,{1}}}, 1, 3]

Position[t, x, {1, 3}] does the same (boundary values given as a list {1,3})

Position[{1,{1,{1}}}, 1, {1,3}]

FullForm[{3, {3, 5, {6}}}]

Position[{3, {3, 5, {6}}}, 3]

{{1},{2,1}}

Position of subexpression 3 from level 1 to level 2:

Position[{3, {3, 5, {6}}}, 3,2]

Position of subexpression 3 at level 1:

Position[{3, {3, 5, {6}}}, 3,{1}]

Position of subexpression 3 at level 2:

Position[{3, {3, 5, {6}}}, 3,{2}]

Recall that the level associated with a position in an expression is the length of the path (from the root) to the subexpression at that position. 

The Mathematica function Level retrieves all the subexpressions at a given level. In other words, it retrieves subexpressions of a tree per level.

Level[t, k] returns the list of subexpressions occurring from level 1 to level k
This function returns subexpressions at given levels (not actual levels, i.e. integers).

Level[t, {2}] returns the subexpressions occurring at level 2 only

TreeForm[{3, {3, 5, {6}}}]

Level[{3, {3, 5, {6}}},2]

Level[{3, {3, 5, {6}}},{2}]

Level can also be determined from the leaves upward using negative integers.

Leaf extraction

Level[t, {-1}] gives the subexpressions occurring at level -1 only

(= the leaves).

Level[{3, {3, 5, {6}}},{-1}]

Level[{3, {3, 5, {6}}},{-2}]

Level[{3, {3, 5, {6}}},{-3}]

{{3,5,{6}}}
Level[{3, {3, 5, {6}}},{-4}]

Depth[t] The depth of an expression t is the length of its longest branch (number of nodes). In other words: the depth is the maximum level of any subexpression + 1.

TreeForm[{3, {3, 5, {6}}}]

Depth[{3, {3, 5, {6}}}]

Depth[{3, 5, {6}}]

TreeForm[{3, 5, {6}}]

To create all subexpressions that have depth 3, find the subexpressions at level -3  (where count starts from - 1 at leaves)

Level[{3,{3, 5, {6}}},{-3}]

TreeForm[{3,{3, 5, {6}}}]

Append[f(t1,...,tn), u] results in f(t1, ..., tn, u)

Append inserts an extra argument in a function-expression

Append[3w * y,z]

Append[f[3a,3b,z],u]

User-defined functions

We can define functions via their arguments and values (inputs and outputs)

Function[{x, y}, x + y] displays the arguments of the function as a list {x,y}. The function sums x and y, returning the value x + y

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

Function[{x,y,z}, x + y -z -2][1,2,3]

Pure function

Pure functions are defined via “lambda-notations” or “functional programming style notations”.

Arguments of a function are indicated by #, or by #1, #2, .. , #k for k-variate functions.

(#1 + #2)&

This function has two arguments #1 and #2. The end of this function specification is indicated by an ampersand &

Note that the brackets around the arguments are standard arithmetical-operation-brackets reserved for arithmetical operations.

Arguments for a newly defined function are enclosed via square function-brackets as usual.

The use of the percentage symbol % allows one to refer to the previously displayed expression in a Mathematica execution.

(#1 + #2)&

% at this stage represents the function (#1 + #2) &.

To call the function on the arguments 3 and 4, type

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

or alternatively

(#1 + #2)&

%[3,4]

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

Apply affects the head
Map affects  the arguments

Map and Apply

Mapping (\@) and applying (@@) a function : 
A function is mapped to arguments of another function
A function is applied to the head of another function.

Mapping a function g to an expression f(t1, ..., tn) applies g to the arguments: f[g[t1], ... , g[tn]]

Map[g, f[t1, ... tn]] or g  /@  f(t1, ... , tn) returns: f[g[t1], ... , g[tn]]

Case of lists: Map[g, {a, b, c}] returns {g[a], g[b], g[c)]}

Map[#^2&,{1,2,3}]

#^2& /@ {1,2,3}

Applying a function g to an expression f(t1, ... tn) replaces the head by g: g(t1, ... , tn)

Apply[g, f[t1, ... ,tn]] or g @@ f[t1,...,tn]

Apply[#^2&,{2}]

#^2& @@{2}

Map and Apply act on a list or, more generally, on an expression.

Map is an operation familiar from functional programming.

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

Abbreviation for Map

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

Map is abbreviated by /@

f  /@  {a, b, c, d}

A function f is mapped over a list {a, b, c, d} by “distributing” f over the list elements.

Map[f, {a, b, c, d}] returns the list {f[a], f[b], f[c], f[d]}.

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

f /@ {a,b,c,d}

Mapping a function over (general) expressions

Map[f, g[h[i, j], l]]

The expression g[h[i, j], l] is a typical Mathematica expression. 

Map skips the head g (as for the case of lists, where the list brackets (the head) are ignored). 

Map distributes f over the two (level one) parts of the expression (the sub-terms h[i, j] and l). 

Map composes g with a single variable function f that is applied to the pair ( h[i, j], l ) coordinate wise (projections on each coordinate prior to application). 

Map[f, g[h[i, j], l]]  results in g[f[h[i, j], f[l]] where f is assumed to be a single-variable function.

Map[f,g[h[i,j],l]]

f /@ g[h[i,j],l]

Deeper level mapping

Map[f, expr, {k}] indicates that f is mapped over the sub-terms of level k (of the given expression).

Map[f,g[h[i,j],l],{2}]

Map[f, expr, k] indicates that f is mapped over the sub-terms from level 1 up to k (of the given expression).

Map[f,g[h[i,j],l],2]

Mapping f over the leaves via level -1 
This is a useful operation, affecting leaves directly.

Map[f,g[h[i,j],l],{-1}]

Note: we don’t count the leaves of the tree in negative notation as starting from 0 as this would lead to ambiguity with 0 being the level of the head of the expression (“g” in our example).

Applying a function

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

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

FullForm[{a, b, c, d}]

FullForm[f[a, b, c, d]]

The head (List) of {a, b, c, d} has been replaced by the new head f after executing Apply[f, {a, b, c, d}]

Apply can operate on an arbitrary expression, replacing the head.  

Apply[g, f[h[i, k], l]] results in g[h[i,k],l]

Apply[g,f[h[i,k],l]]

The result of 
Apply[f, {a, b, c, d}] 

is the same as: 

 Apply[f, {a, b, c, d}, {0}] 
 (the 0-level indicates the outermost curly brackets replaced by f)

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

Apply[g,f[h[i,k],l], {0}]

Apply[g,f[h[i,k],l], {1}]

The expression l is atomic and hence has no head to replace.

Apply[g,f[h[i,k],l], {2}]

Nothing happened since level 2 regards the atoms i and k (which have no heads).

Apply is abbreviated via @@

Apply can be applied at various levels.

TreeForm[{1,{2,{3}}}]

TreeForm[Apply[f,{1,{2,{3}}},{1}]]

When Apply is carried out at level 1, the abbreviation @@ is used.

When Apply is carried out at level 2, the abbreviation @@@ is used.

Higher level applications require the use of Apply[f, expr, {k}] 
(attempts to use @@@ followed by @@ or @@@ followed by @@@ leads to errors)

f @@ {a,b,c,d}

For levels greater than level 1, a list of expressions is returned, 
where f replaces the heads at that given level

f@@@{{a,b},{c,d}}

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

TreeForm[{{{a,b},{c,d}}}]

Apply[f, {{{a, b}, {c}}}, {3}] results in {{{a, b}, {c, d}}} since level 3 is the location of the atoms, which have no head, so no replacement with f occurs.

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

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

The notation {1} in Apply[f, {{a, b}, {c, d}}, {1}]  means that f is to be applied to the first level elements of the given list

As before Apply[f, {{{a}, {b}}, {c, d}}, 2] means that f is to be applied to level 1 and level 2 elements of the given list

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

f @@ {a, b, c, d} is the same as Apply[f, {a, b, c, d}, {0}]

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

f @@@ {{a, b},  {c, d}} is the same as Apply[f, {{a, b}, {c, d}}, {1}]

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

As observed, higher level applications cannot rely on combinations of @@ and @@@ and must use Apply[f, expr, {k}].