Functional Operations
In an expression like f[x], the "function name" f is itself an expression, and you can treat it as you would any other expression.
This gives Log as the function name to use:
The ability to treat the names of functions just like other kinds of expressions is an important consequence of the symbolic nature of the Wolfram Language. It makes possible the whole range of functional operations.
Ordinary Wolfram Language functions such as Log or Integrate typically operate on data such as numbers and algebraic expressions. Wolfram Language functions that represent functional operations, however, can operate not only on ordinary data, but also on functions themselves. Thus, for example, the functional operation InverseFunction takes a Wolfram Language function name as an argument, and represents the inverse of that function.
InverseFunction is a functional operation: it takes a Wolfram Language function as an argument, and returns another function which represents its inverse:
The result obtained from InverseFunction is a function which you can apply to data:
You can also use InverseFunction in a purely symbolic way:
There are many kinds of functional operations in the Wolfram Language. Some represent mathematical operations; others represent various kinds of procedures and algorithms.
Unless you are familiar with advanced symbolic languages, you will probably not recognize most of the functional operations discussed. At first, the operations may seem difficult to understand. But it is worth persisting. Functional operations provide one of the most conceptually and practically efficient ways to use the Wolfram Language.
Many programs you write will involve operations that need to be iterated several times. Nest and NestList are powerful constructs for doing this.
Nest[f,x,n] | apply the function f nested n times to x |
NestList[f,x,n] | generate the list {x,f[x],f[f[x]],…}, where f is nested up to n deep |
You can iterate the function using Nest:
Nest and NestList allow you to apply functions a fixed number of times. Often you may want to apply functions until the result no longer changes. You can do this using FixedPoint and FixedPointList.
FixedPoint[f,x] | apply the function f repeatedly until the result no longer changes |
FixedPointList[f,x] | generate the list {x,f[x],f[f[x]],…}, stopping when the elements no longer change |
Using the function FixedPoint, you can automatically continue applying newton3 until the result no longer changes:
NestWhile[f,x,test] | |
NestWhileList[f,x,test] | generate the list {x,f[x],f[f[x]],…}, stopping when applying test to the result no longer yields True |
NestWhile[f,x,test,m]
,
NestWhileList[f,x,test,m] | |
supply the m most recent results as arguments for test at each step | |
NestWhile[f,x,test,All]
,
NestWhileList[f,x,test,All] | |
supply all results so far as arguments for test |
This repeatedly applies newton3, stopping when two successive results are no longer considered unequal, just as in FixedPointList:
Operations such as Nest take a function f of one argument, and apply it repeatedly. At each step, they use the result of the previous step as the new argument of f.
It is important to generalize this notion to functions of two arguments. You can again apply the function repeatedly, but now each result you get supplies only one of the new arguments you need. A convenient approach is to get the other argument at each step from the successive elements of a list.
FoldList[f,x,{a,b,…}] | create the list {x,f[x,a],f[f[x,a],b],…} |
Fold[f,x,{a,b,…}] | give the last element of the list produced by FoldList[f,x,{a,b,…}] |
Here is an example of what FoldList does:
Using Fold and FoldList you can write many elegant and efficient programs in the Wolfram Language. In some cases, you may find it helpful to think of Fold and FoldList as producing a simple nesting of a family of functions indexed by their second argument.
In an expression like f[{a,b,c}] you are giving a list as the argument to a function. Often you need instead to apply a function directly to the elements of a list, rather than to the list as a whole. You can do this in the Wolfram Language using Apply.
This gives Times[a,b,c] which yields the product of the elements in the list:
Here is a definition of a function that works like the built-in function GeometricMean, written using Apply:
Apply[f,{a,b,…}] | apply f to a list, giving f[a,b,…] |
Apply[f,expr] or f@@expr | apply f to the top level of an expression |
MapApply[f,expr] or f@@@expr | apply f at the first level in an expression |
Apply[f,expr,{1}] | equivalent to f@@@expr |
Apply[f,expr,lev] | apply f at the specified levels in an expression |
What Apply does in general is to replace the head of an expression with the function you specify. Here it replaces Plus by List:
If you have a list of elements, it is often important to be able to apply a function separately to each of the elements. You can do this in the Wolfram Language using Map.
Map[f,{a,b,…}] | apply f to each element in a list, giving {f[a],f[b],…} |
What Map[f,expr] effectively does is to "wrap" the function f around each element of the expression expr. You can use Map on any expression, not just a list.
Map[f,expr] applies f to the first level of parts in expr. You can use MapAll[f,expr] to apply f to all the parts of expr.
MapAll applies f at all levels in m. If you look carefully at this expression, you will see an f wrapped around every part:
In general, you can use level specifications as described in "Levels in Expressions" to tell Map to which parts of an expression to apply your function.
Map[f,expr] or f/@expr | apply f to the first‐level parts of expr |
MapAll[f,expr] or f//@expr | apply f to all parts of expr |
Map[f,expr,lev] | apply f to each part of expr at levels specified by lev |
Level specifications allow you to tell Map to which levels of parts in an expression you want a function applied. With MapAt, however, you can instead give an explicit list of parts where you want a function applied. You specify each part by giving its indices, as discussed in "Parts of Expressions".
To avoid ambiguity, you must put each part specification in a list, even when it involves only one index:
MapAt[f,expr,{part1,part2,…}] | apply f to specified parts of expr |
You can use MapAt on any expression. Remember that parts are numbered on the basis of the full forms of expressions:
MapIndexed[f,expr] | apply f to the elements of an expression, giving the part specification of each element as a second argument to f |
MapIndexed[f,expr,lev] | apply f to parts at specified levels, giving the list of indices for each part as a second argument to f |
This applies f to each element in a list, giving the index of the element as a second argument to f:
Map allows you to apply a function of one argument to parts of an expression. Sometimes, however, you may instead want to apply a function of several arguments to corresponding parts of several different expressions. You can do this using MapThread.
MapThread[f,{expr1,expr2,…}] | apply f to corresponding elements in each of the expri |
MapThread[f,{expr1,expr2,…},lev] | apply f to parts of the expri at the specified level |
MapThread works with any number of expressions, so long as they have the same structure:
Functions like Map allow you to create expressions with parts modified. Sometimes you simply want to go through an expression, and apply a particular function to some parts of it, without building a new expression. A typical case is when the function you apply has certain “side effects”, such as making assignments, or generating output.
Scan[f,expr] | evaluate f applied to each element of expr in turn |
Scan[f,expr,lev] | evaluate f applied to parts of expr on levels specified by lev |
Scan evaluates the result of applying a function to each element, but does not construct a new expression:
Function[x,body] | a pure function in which x is replaced by any argument you provide |
Function[{x1,x2,…},body] | a pure function that takes several arguments |
body& | a pure function in which arguments are specified as # or #1, #2, #3, etc. |
When you use functional operations such as Nest and Map, you always have to specify a function to apply. In all the examples above, we have used the "name" of a function to specify the function. Pure functions allow you to give functions which can be applied to arguments, without having to define explicit names for the functions.
There are several equivalent ways to write pure functions in the Wolfram Language. The idea in all cases is to construct an object which, when supplied with appropriate arguments, computes a particular function. Thus, for example, if fun is a pure function, then fun[a] evaluates the function with argument a.
You can use a pure function in Map:
Or in Nest:
This sets up a pure function with two arguments and then applies the function to the arguments a and b:
If you are going to use a particular function repeatedly, then you can define the function using f[x_]:=body, and refer to the function by its name f. On the other hand, if you only intend to use a function once, you will probably find it better to give the function in pure function form, without ever naming it.
If you are familiar with formal logic or the LISP programming language, you will recognize Wolfram Language pure functions as being like expressions or anonymous functions. Pure functions are also close to the pure mathematical notion of operators.
# | the first variable in a pure function |
#n | the n th variable in a pure function |
## | the sequence of all variables in a pure function |
##n | the sequence of variables starting with the n th one |
Just as the name of a function is irrelevant if you do not intend to refer to the function again, so also the names of arguments in a pure function are irrelevant. The Wolfram Language allows you to avoid using explicit names for the arguments of pure functions, and instead to specify the arguments by giving "slot numbers" #n. In a Wolfram Language pure function, #n stands for the n th argument you supply. # stands for the first argument.
This applies a function that takes the first two elements from each list. By using a pure function, you avoid having to define the function separately:
Using short forms for pure functions, you can simplify the definition of fromdigits given in "Applying Functions Repeatedly":
When you use short forms for pure functions, it is very important that you do not forget the ampersand. If you leave the ampersand out, the Wolfram Language will not know that the expression you give is to be used as a pure function.
When you use the ampersand notation for pure functions, you must be careful about the grouping of pieces in your input. As shown in "Operator Input Forms" the ampersand notation has fairly low precedence, which means that you can type expressions like #1+#2& without parentheses. On the other hand, if you want, for example, to set an option to be a pure function, you need to use parentheses, as in option->(fun&).
Pure functions in the Wolfram Language can take any number of arguments. You can use ## to stand for all the arguments that are given, and ##n to stand for the n th and subsequent arguments.
Array[f,n] | generate a length n list of the form {f[1],f[2],…} |
Array[f,{n1,n2,…}] | generate an n1×n2×… nested list, each of whose entries consists of f applied to its indices |
NestList[f,x,n] | generate a list of the form {x,f[x],f[f[x]],…}, where f is nested up to n deep |
FoldList[f,x,{a,b,…}] | generate a list of the form {x,f[x,a],f[f[x,a],b],…} |
ComposeList[{f1,f2,…},x] | generate a list of the form {x,f1[x],f2[f1[x]],…} |
NestList and FoldList were discussed in "Applying Functions Repeatedly". Particularly by using them with pure functions, you can construct some very elegant and efficient Wolfram Language programs.
"Manipulating Elements of Lists" shows how you can pick out elements of lists based on their positions. Often, however, you will need to select elements based not on where they are, but rather on what they are.
Select[list,f] selects elements of list using the function f as a criterion. Select applies f to each element of list in turn, and keeps only those for which the result is True.
This selects the elements of the list for which the pure function yields True, i.e., those numerically greater than 4:
You can use Select to pick out pieces of any expression, not just elements of a list.
"Putting Constraints on Patterns" discusses some "predicates" that are often used as criteria in Select.
In most cases, you want the head f of a Wolfram Language expression like f[x] to be a single symbol. There are, however, some important applications of heads that are not symbols.
One case where we have already encountered the use of complicated expressions as heads is in working with pure functions in "Pure Functions". By giving Function[vars,body] as the head of an expression, you specify a function of the arguments to be evaluated.
With the head Function[x,x^2], the value of the expression is the square of the argument:
There are several constructs in the Wolfram Language which work much like pure functions, but which represent specific kinds of functions, typically numerical ones. In all cases, the basic mechanism involves giving a head which contains complete information about the function you want to use.
Function[vars,body][args] | pure function |
InterpolatingFunction[data][args] | |
CompiledFunction[data][args] | |
LinearSolveFunction[data][vec] |
Here is the InterpolatingFunction object:
You can use the InterpolatingFunction object as a head to get numerical approximations to values of the function y:
Another important use of more complicated expressions as heads is in implementing functionals and functional operators in mathematics.
As one example, consider the operation of differentiation. As discussed in "The Representation of Derivatives", an expression like f' represents a derivative function, obtained from f by applying a functional operator to it. In the Wolfram Language, f' is represented as Derivative[1][f]: the "functional operator" Derivative[1] is applied to f to give another function, represented as f'.
This expression has a head which represents the application of the “functional operator” Derivative[1] to the “function” f:
You can replace the head f' with another head, such as fp. This effectively takes fp to be a “derivative function” obtained from f:
You can think of an expression like f[x] as being formed by applying an operator f to the expression x. You can think of an expression like f[g[x]] as the result of composing the operators f and g, and applying the result to x.
Composition[f,g,…] | the composition of functions f, g, … |
RightComposition[f,g,…] | the composition on the right of f, g, … |
InverseFunction[f] | the inverse of a function f |
Identity | the identity function |
RightComposition composes in the opposite order:
You can get the sum of two expressions in the Wolfram System just by typing x+y. Sometimes it is also worthwhile to consider performing operations like addition on operators.
Using Through, you can convert the expression to a more explicit form:
The Wolfram System does not automatically apply the separate pieces of the operator to an expression:
You can use Through to apply the operator:
Identity[expr] | the identity function |
Through[p[f1,f2][x],q] | give p[f1[x], f2[x]] if p is the same as q |
Operate[p,f[x]] | give p[f][x] |
Operate[p,f[x],n] | apply p at level n in f |
MapAll[p,expr,Heads->True] | apply p to all parts of expr, including heads |
Functions like Expand do not automatically go inside heads of expressions:
You can use Operate to apply a function specifically to the head of an expression:
The Wolfram System contains some powerful primitives for making structural changes to expressions. You can use these primitives both to implement mathematical properties such as associativity and distributivity, and to provide the basis for some succinct and efficient programs.
Here we describe various operations that you can explicitly perform on expressions. "Attributes" describes how some of these operations can be performed automatically on all expressions with a particular head by assigning appropriate attributes to that head.
You can use the Wolfram System function Sort[expr] to sort elements not only of lists, but of expressions with any head. In this way, you can implement the mathematical properties of commutativity or symmetry for arbitrary functions.
You can use Sort to put the arguments of any function into a standard order:
Sort[expr] | sort the elements of a list or other expression into a standard order |
Sort[expr,pred] | sort using the function pred to determine whether pairs are in order |
Ordering[expr] | give the ordering of elements when sorted |
Ordering[expr,n] | give the ordering of the first n elements when sorted |
Ordering[expr,n,pred] | use the function pred to determine whether pairs are in order |
OrderedQ[expr] | |
Order[expr1,expr2] | give 1 if expr1 comes before expr2 in standard order, and -1 if it comes after |
The second argument to Sort is a function used to determine whether pairs are in order. This sorts numbers into descending order:
Flatten[expr] | flatten out all nested functions with the same head as expr |
Flatten[expr,n] | flatten at most n levels of nesting |
Flatten[expr,n,h] | flatten functions with head h |
FlattenAt[expr,i] | flatten only the i th element of expr |
Flatten removes nested occurrences of a function:
You can use Flatten to "splice" sequences of elements into lists or other expressions:
You can use Flatten to implement the mathematical property of associativity. The function Distribute allows you to implement properties such as distributivity and linearity.
Distribute[f[a+b+…,…]] | distribute f over sums to give f[a,…]+f[b,…]+… |
Distribute[f[args],g] | distribute f over any arguments which have head g |
Distribute[expr,g,f] | distribute only when the head is f |
Distribute[expr,g,f,gp,fp] | distribute f over g, replacing them with fp and gp, respectively |
In general, if f is distributive over Plus, then an expression like f[a+b] can be "expanded" to give f[a]+f[b]. The function Expand does this kind of expansion for standard algebraic operators such as Times. Distribute allows you to perform the same kind of expansion for arbitrary operators.
This applies distributivity over lists, rather than sums. The result contains all possible pairs of arguments:
This distributes over lists, making sure that the head of the whole expression is f. In the result, it uses gp in place of List, and fp in place of f:
Related to Distribute is the function Thread. What Thread effectively does is to apply a function in parallel to all the elements of a list or other expression.
Thread[f[{a1,a2},{b1,b2}]] | thread f over lists to give {f[a1,b1],f[a2,b2]} |
Thread[f[args],g] | thread f over objects with head g in args |
Thread applies the function "in parallel" to each element of the lists:
As mentioned in "Collecting Objects Together", and discussed in more detail in "Attributes", many built‐in Wolfram System functions have the property of being "listable", so that they are automatically threaded over any lists that appear as arguments.
Built‐in mathematical functions such as Log are listable, so that they are automatically threaded over lists:
Log is, however, not automatically threaded over equations:
You can use Thread to get functions applied to both sides of an equation:
Outer[f,list1,list2] takes all possible combinations of elements from list1 and list2, and combines them with f. Outer can be viewed as a generalization of a Cartesian product for tensors, as discussed in "Tensors".
You can use Outer on any sequence of expressions with the same head:
Outer, like Distribute, constructs all possible combinations of elements. On the other hand, Inner, like Thread, constructs only combinations of elements that have corresponding positions in the expressions it acts on.
Here is a structure built by Inner:
The function Flatten allows you to explicitly flatten out all sublists:
FlattenAt lets you specify at what positions you want sublists flattened:
Sequence objects automatically get spliced in, and do not require any explicit flattening:
Sequence[e1,e2,…] | a sequence of arguments that will automatically be spliced into any function |
Sequence works in any function:
Here is a common way that Sequence is used: