IV. Rules, patterns and functions
4.4 Functions - starting examples and syntax
4.4.1 A definition and a simple example
By function we will mean a pair: any normal (non-atomic) Mathematica expression which contains pat-terns, and a rule in a global rule base, reflected by the DownValues command, which tells what should replace the first expression when it is encountered (we will ignore functions defined by SubValues, for the time being).
For example, this defines a function:
Clear@f, xD; f@x_D:=x ^ 3;
Here f[x_] is a normal expression (we see a Head <f> and single square brackets - the characteristics of the normal expression), the pattern is <x_> (we can see that it is a pattern by the presence of the under -score, which is one of the symbols that distinguish patterns; <x_> stands for exactly one argument), and one can check the presence of the global rule in a rule base by checking the DownValues command on a symbol <f> (see Chapter 1, section 1.2.3):
DownValues@fD
We can now make sure that the function works as intended : 8f@aD, f@PiD, f@EinsteinD<
9a3,Π3, Einstein3=
We see that the function as defined above works on any single expression.
By a single expression I mean single argument - for example <f> will not work in this situation:
f@a, bD f@a, bD
This is not an error, but Mathematica simply does not know what to do with such an expression, and thus returns it back, in accordance with its general evaluation strategy.
Notice by the way, that all the ingredients needed to define a function we have already encountered before - patterns, assignment operator, etc. No new syntax is needed for function definitions - indeed because they are just some type of global rules, similar to variables. The non - trivial part here is in the action of an assignment operator (SetDelayed or sometimes Set): it decides whether or not the l.h.s. of the emerging global rule is legitimate, and if so, what type of global rule the new rule will be. When the l.h.s. contains patterns and the resulting global rule is stored in DownValues, we say that we have defined a function.
Often one needs to perform type or more general argument checks. They are very easy to implement in Mathematica and we will discuss them in the section on conditional patterns.
Clear@fD;
4.4.2 More on function names and evaluation surprises Consider a previous example:
Clear@fD; f@x_D:=x ^ 3;
Note that the straightforward attempt to check the Head (name of the function in this case) will give not what we would naively expect :
Head@f@tDD Power
It is very easy to understand what happened by using the tracing command Trace:
Trace@Head@f@tDDD
99f@tD, t3=, HeadAt3E, Power=
We see that since the expression f[t] matches the pattern f[x_], the rule applied. Recall that the evaluation process by default starts from the leaves of the nested expression (from inside out - see section 2.5.6).
Thus, when the Head command started to evaluate, its "content" has already changed from f[t] to t^3. The full internal form of t^3 is:
FullForm@t ^ 3D
Power@t, 3D
This explains the end result. Going ahead of time, let me mention that there is a way to force the evalua-tion process to start in the opposite direcevalua-tion, from "branches" to "leaves" (non-standard evaluaevalua-tion , section 2.5.6), which will lead to the expected result for a function name:
Head@Unevaluated@f@tDDD f
We have already discussed this construction in the section on variables (section 2.2.1).
Clear@fD;
4.4.3 On the necessity of patterns
So, the name of the function is its Head - the symbol outside the square brackets in its definition, which contains a pattern. We may ask if it is possible to define a function without a pattern. The answer is that it is possible but the object so defined will not be a function in the normal sense and will have a behavior different from what we probably want. Here is an example:
Clear@fD; f@xD:=x ^ 3;
This definition does not contain a pattern (no uderscore or other pattern ingredients). Let us check it:
This definition does not contain a pattern (no underscore or other pattern ingredients). Let us check it:
8f@xD, f@yD<
9x3, f@yD=
Since we did not have a pattern, the class of expressions on which the corresponding rule will match has been narrowed down to just literal f[x]. In particular, it will not work on any other parameters :
8f@1D, f@2D, f@PiD, f@xD<
9f@1D, f@2D, f@ΠD, x3=
Moreover, if we then define the global value for an < x > variable, it will not work on < x > either : x=5;
f@xD f@5D
(We should already be able to understand the last result : x evaluated to 5 before f had any chance to
"look" at it).
The object f[x] here could be interpreted as an indexed variable (section 2.2.4) rather than a function, but even in this interpretation, it is a very error - prone practice to use symbols as indices in indexed variables.
In any case, it has nothing to do with the behavior of the real function.
This behavior explains why we need patterns to define functions: patterns widen the class of expressions on which the rule will match. In particular, when we write
Clear@f, xD; f@x_D:=x ^ 3;
the pattern < x_ > means "any expression" and < x > here becomes a name attached to a placeholder where the actual input parameter will be placed. When we later call the function, normally the input parameters are evaluated first, and then the action of the function is to replace them with whatever the action of the r.h.s. of the corresponding rule should be.
Clear@fD;
4.4.4 More on the correct syntax of the function calls
Calling a function by name without the square brackets, or with parentheses used instead, will not give a desired result (and is a logical mistake in most cases):
8Sin, Sin HPiL<
8Sin, ΠSin<
In both cases, Sin was interpreted by Mathematica not as a function, but just as some symbolic object. In the latter case, parentheses were interpreted as a multiplication, which is easy to see with the help of FullForm :
FullForm@8Sin, Sin HPiL< D
List@Sin, Times@Pi, SinDD
As we mentioned already, function calls are syntactically just a special case of Mathematica normal expressions, and thus have to obey the standard syntax rules. Thus, the single square brackets.
While just using a function name will not be a mistake in many languages (in C this will be a function pointer), in (strongly) typed languages this will lead to a type conflict and will probably cause a compiler warning or error message. Not so in Mathematica, which means that one has to be more careful. In ver-sion 6, the red highlighting will usually warn that the syntax may be wrong.
4.4.5 On function definitions and assignment operators
4.4.5.1 Use SetDelayed to define a function in most cases
What will happen, if we use the Set (=) command instead of SetDelayed (:=), when defining a function?
This depends on the state of global variables present or defined in the system at the given moment. Here is an example:
Clear@f, xD; f@x_D =x ^ 2;
8f@1D, f@2D, f@PiD, f@yD<
91, 4,Π2, y2=
The function works fine, but this is so only because by the moment of the definition, the variable < x > did not have any global value (no global rule was associated with it), and thus the r.h.s. x^2 evaluated trivially (to itself) and was recorded in the rule for function < f > in this way. This is what happens when < x > has a value at the moment of assignment :
Clear@f, xD; x=5;
f@x_D =x ^ 2;
8f@1D, f@2D, f@PiD, f@yD<
825, 25, 25, 25<
To understand it better, we can look at DownValues of < f >, which reflect the way the definitions (rules) for < f > are stored in the system :
DownValues@fD
8HoldPattern@f@x_DD ¦25<
8HoldPattern@f@x_DD ¦25<
We see that now any input expression, regardless of its structure, will be replaced by 25. This behavior is in full agreement with the principles of operation of Set ( = ) assignment operator. It allows the r.h.s. of the definition to evaluate. This evaluation happens as usual, using the values for all global variables or expressions which exist in the system at the moment of the definition. Then Set uses the result of this evaluation as a r.h.s for the new global rule, associated with the l.h.s. of the assignment (See chapter 2 section 2.4.1). Since <x> had a global value 5, it was used in the calculation of the r.h.s, which then became the r.h.s. of the global rule associated with function <f> (definition of f).
in full agreement with the principles of operation of Set ( = ) assignment operator. It allows the r.h.s. of the definition to evaluate. This evaluation happens as usual, using the values for all global variables or expressions which exist in the system at the moment of the definition. Then Set uses the result of this evaluation as a r.h.s for the new global rule, associated with the l.h.s. of the assignment (See chapter 2 section 2.4.1). Since <x> had a global value 5, it was used in the calculation of the r.h.s, which then became the r.h.s. of the global rule associated with function <f> (definition of f).
So, the conclusion is that in the majority of cases functions must be defined with SetDelayed (:=) rather than Set (=). Since SetDelayed does not evaluate the r.h.s of an assignment, we are safe in this case.
4.4.5.2 When Set is more appropriate
There are instances when Set operator is more appropriate do define a function however. In particular, this happens when a function may be symbolically precomputed so that it is stored in a form which allows a more efficient computation. Consider for instance a function defined as an indefinite integral, like the following one :
The point is, this integral can be computed in a closed form, and it absolutely makes sense to do it only once and then store the already computed definition. But with SetDelayed (as above), it will be recom -puted every time afresh, according to a general rule of delayed evaluation. This is the case to use Set :
Clear@x, g1D;
g1@x_D =Integrate@Sqrt@1 +z ^ 2D, 8z, 0, x<D;
The result is almost instantaneous this time ( I cheated a bit by not including the time it took to compute the integral, but for a large number of function calls it will be in most cases negligible):
Table@g1@iD, 8i, 10<D Short@ð, 2D&Timing :0.,:1
2 J 2 +ArcSinh@1DN,8, 1
2 J10 101 +1N>>
However, notice that we had to be careful and Clear the variable < x > . To be completely on the safe side, one can use one of the scoping constructs (discussed at the end of this chapter) to localize the variable :
Clear@g2D;
Module@8x<, g2@x_D =Integrate@Sqrt@1 +z ^ 2D, 8z, 0, x<DD; Table@g2@iD, 8i, 10<D Short@ð, 2D&
:1
2 J 2 +ArcSinh@1DN,8, 1
2 J10 101 +1N>
4.4.6 Assigning values to function symbols (names)
Since function symbols are just normal symbols, they can be used as variables and in particular can be assigned values. When the function is called on some argument, these values are computed before any other computation takes place. Consider an example:
Clear@fD; f@x_D:=x ^ 2;
f = Sin;
f@5D Sin@5D
Notice that this does not mean that the previous rule for < f > disappeared - it is still in the rule base, as can be checked with DownValues :
DownValues@fD
9HoldPattern@f@x_DD ¦x2=
It is just that < f > now has also OwnValue < Sin >, which is computed in this case before any arguments are considered, and then the DownValue rule has no chance to apply :
OwnValues@fD
8HoldPattern@fD ¦Sin<
We can see what happens, with the help of the Trace command : Trace@f@5DD
88f, Sin<, Sin@5D<
To "restore" the function in this case, we obviously can not use Clear, since then also the DownValues of
< f > will be cleared. In such a case, use Unset (section 2.2.6) : f =.;
f@5D 25
In general, the above behavior means that one has to be careful and make sure that the symbol which is going to be used as a function name, does not have an OwnValue (unless this is what is desired, which is a rare case) - otherwise the newly defined function will not work properly.
4.4.7 Advanced topic: parameter passing
4.4.7.1 How parameters are passed
Let us look a bit closer at the way the parameters (which are the pattern tags and stand with blanks or other patterns on the l.h.s, such as x_) are passed to functions. The three main questions to address are these: what is the mechanism of parameter passing, is it possible to modify the passed parameters within a function such that the actual expressions being passed are modified after the function returns (pass-by-reference), and what are the rules for name collisions with the local variables. Since we did not systemati-cally discuss the Mathematica scoping constructs yet, we will postpone the third question until such a discussion (sections 4.8, 4.10), and deal with the first two.
So, how are the parameters passed to the function? It turns out that the rule is very simple: their values (evaluated or not, depending on the presence of Hold attributes attached to the function) are textually substituted into the r.h.s of the function (before or after evaluation of the function itself takes place, again depending on the evaluation being standard or not). This happens somewhat similarly to the C preproces -sor substitutions. What is important is that they never become local variables (in the sense of C), with the consequences we will describe in a second. We could say that the arguments are always passed by value, but the notion of value depends on whether or nor the function evaluates arguments in a standard way (presence or absence of Hold attributes).
The next question is whether or not the passed parameters can be modified inside a function. This depends on whether or not the passed object represents an L-value. The passed object will represent an L-value in 2 cases:
1. The evaluation order is standard, but what is passed evaluates (before being passed, according to the standard evaluation strategy that arguments are evaluated first) to a global symbol (which can be used as a variable in the sense described in section 2.2), with no assigned value.
2. What is passed is also a global symbol in the above sense, possibly with some global rule (definition) assigned to it, but the order of evaluation is non-standard and this symbol is passed unevaluated.
If the global symbol above is composite, and its head does not carry the Protected attribute, then the result of an assignment will be a DownValue or SubValue for the head.
In both of these cases it is possible to assign a value to a global symbol being passed to the function, from within the function, and thus modify it. Modification of the symbol in the first case has no direct analogs in languages such as C, just because it requires some symbol (which we pass) to hang around in a global name space but not have any value at all, which is only possible in a symbolic environment. In the second case, effectively the pass-by-reference semantics is simulated.
Finally, if what is passed does not represent an L - value, no assignments to it are possible. Again, this reflects the fact that what really happens is a textual substitution in a body of a function rather than say allocating variables on the stack. This textual substitution is similar to that performed by a scoping con-struct With (section 4.8.3).
Also, this means that there is no way of changing the value of the parameter locally (without global parame -ter modification) - either it represents an L-values and then is changed globally, or it does not and then no changes are at all possible. If one needs to change a passed parameter locally, one may introduce a local variable, initialize it with this parameter value, and then change a local variable instead (local variables will be described later in this chapter).
variable, initialize it with this parameter value, and then change a local variable instead (local variables will be described later in this chapter).
It is time now to illustrate the rather obscure statements I just made.
4.4.7.2 Illustration: standard evaluation
We start with the following function which attempts to assign a value to the parameter passed to it : Clear@f, x, a, b, c, d, hD;
f@x_D := x = 5;
We start with a symbol which does not have a global value : a
a f@aD; a 5
We see that it was modified. This corresponds to the case 1 above. Now consider:
b = c;
f@bD; 8b, c<
85, 5<
It may be not immediately obvious, but what happened really is that only < c > received the numerical value, but not < b >, which has the same definition as before:
? b
Global‘b
b=c
In particular, if we now Clear[c], b will no longer evaluate to a number : Clear@cD;
b c
What happened at the moment of the function call is that first, < b > evaluated to < c >, and then < c >
was passed and subsequently modified, because it did not have any rule associated with it (if it had, then the r.h.s of this rule would be passed to the function or be evaluated further). Let us now repeat the first experiment again:
Clear@aD; f@aD 5
And call again : f@aD
Set::setraw : Cannot assign to raw object 5.
5
The point is that after the first call, the symbol < a > received a value < 5 >, to which it evaluated before being passed to the function. The function then attempted to assign 5 to 5, which does not work since the l.h.s. is not an L - value.
Consider now a different attempt : Clear@aD;
f@2 aD
Set::write : Tag Times in 2 a is Protected.
5
Here, the object passed is < 2 a >, which is not an L value either despite the presence of symbolic quan -tity < a > . It is easy to understand if we use the FullForm :
FullForm@2 aD
Times@2, aD
Therefore, what we really attempted to do was to define a rule for a built - in function Times, which is protected against this (if it weren’t protected, the input would represent an L-Value, and the result of the assignment would be a DownValue rule for the head of the input; more on Protected attribute in section 4.9.5).
If we return to the second experiment : Clear@b, cD;
b = c;
f@bD; now we try again :
f@bD
Set::setraw : Cannot assign to raw object 5.
5
We have the same story. The symbol < b > really acts as a middleman which simply hands < c > to the function. And the story with < c > is the same as what we had for < a > before.
Finally, let us consider the following input:
Clear@aD; f@h@aDD; Check :
h@aD 5
DownValues@hD
8HoldPattern@h@aDD ¦5<
Although we have created a DownValue for < h >, < h > did not really become a function in the normal sense, since the l.h.s of the rule does not contain a pattern. Rather, we made a definition for a composite symbol < h[a] >, much like in our discussion in section 4.4.3.
4.4.7.3 Illustration: non-standard evaluation
Now we will modify our function to have a Hold attribute, which will mean that it will receive whatever argument we pass, in unevaluated form :
ClearAll@ffD;
SetAttributes@ff, HoldFirstD; ff@x_D := x = 5;
We try now : Clear@aD; ff@aD; a 5
And the second time : ff@aD; a 5
We can modify < a > and call again : a = 10;
ff@aD; a 5
So, the function really does modify the variable which has a global value. There is no mystery here: uneval-uated simply means that the symbol < a >, rather than the r.h.s of the global rule associated with it, is passed to the body of the function, and thus modified. The symbol <a> here resembles the pointer to a variable in C.
How about our second experiment? We try:
Clear@b, cD; b = c;
ff@bD; 8b, c<
85, c<
We see that the result is completely different. Now < b > was assigned a value, and not < c > . But this had to be expected : unevaluated means in this case that the symbol < b >, rather than the r.h.s. of the rule associated with it (< c >), was textually substituted in the body of the function and thus modified. In particular, the previous definition < b = c > is lost now :
had to be expected : unevaluated means in this case that the symbol < b >, rather than the r.h.s. of the rule
had to be expected : unevaluated means in this case that the symbol < b >, rather than the r.h.s. of the rule