Associating Definitions with Different Symbols
When you make a definition in the form
f[args]=rhs or
f[args]:=rhs,
Mathematica associates your definition with the object
f. This means, for example, that such definitions are displayed when you type
?f. In general, definitions for expressions in which the symbol
f appears as the head are termed
downvalues of
f.
Mathematica however also supports
upvalues, which allow definitions to be associated with symbols that do not appear directly as their head.
Consider for example a definition like
Exp[g[x_]]:=rhs. One possibility is that this definition could be associated with the symbol
Exp, and considered as a downvalue of
Exp. This is however probably not the best thing either from the point of view of organization or efficiency.
Better is to consider
Exp[g[x_]]:=rhs to be associated with
g, and to correspond to an upvalue of
g.
| f[args]:=rhs | define a downvalue for f |
| f[g[args],...]^:=rhs | define an upvalue for g |
Associating definitions with different symbols.
This is taken to define a downvalue for f. |
You can see the definition when you ask about f. |
This defines an upvalue for g. |
The definition is associated with g. |
It is not associated with Exp. |
The definition is used to evaluate this expression.
| Out[6]= |  |
|
In simple cases, you will get the same answers to calculations whether you give a definition for
f[g[x]] as a downvalue for
f or an upvalue for
g. However, one of the two choices is usually much more natural and efficient than the other.
A good rule of thumb is that a definition for
f[g[x]] should be given as an upvalue for
g in cases where the function
f is more common than
g. Thus, for example, in the case of
Exp[g[x]],
Exp is a built-in
Mathematica function, while
g is presumably a function you have added. In such a case, you will typically think of definitions for
Exp[g[x]] as giving relations satisfied by
g. As a result, it is more natural to treat the definitions as upvalues for
g than as downvalues for
Exp.
This gives the definition as an upvalue for g. |
Here are the definitions for g so far. |
The definition for a sum of g's is used whenever possible.
| Out[9]= |  |
|
Since the full form of the pattern
g[x_]+g[y_] is
Plus[g[x_], g[y_]], a definition for this pattern could be given as a downvalue for
Plus. It is almost always better, however, to give the definition as an upvalue for
g.
In general, whenever
Mathematica encounters a particular function, it tries all the definitions you have given for that function. If you had made the definition for
g[x_]+g[y_] a downvalue for
Plus, then
Mathematica would have tried this definition whenever
Plus occurs. The definition would thus be tested every time
Mathematica added expressions together, making this very common operation slower in all cases.
However, by giving a definition for
g[x_]+g[y_] as an upvalue for
g, you associate the definition with
g. In this case,
Mathematica only tries the definition when it finds a
g inside a function such as
Plus. Since
g presumably occurs much less frequently than
Plus, this is a much more efficient procedure.
| f[g]^=value or f[g[args]]^=value |
| make assignments to be associated with g, rather than f |
| f[g]^:=value or f[g[args]]^:=value |
| make delayed assignments associated with g |
| f[arg1,arg2,...]^=value | make assignments associated with the heads of all the argi |
Shorter ways to define upvalues.
A typical use of upvalues is in setting up a "database" of properties of a particular object. With upvalues, you can associate each definition you make with the object that it concerns, rather than with the property you are specifying.
This defines an upvalue for square which gives its area.
| Out[10]= |  |
|
This adds a definition for the perimeter.
| Out[11]= |  |
|
Both definitions are now associated with the object square. |
In general, you can associate definitions for an expression with any symbol that occurs at a sufficiently high level in the expression. With an expression of the form
f[args], you can define an upvalue for a symbol
g so long as either
g itself, or an object with head
g, occurs in
args. If
g occurs at a lower level in an expression, however, you cannot associate definitions with it.
g occurs as the head of an argument, so you can associate a definition with it. |
Here g appears too deep in the left-hand side for you to associate a definition with it.
| Out[14]= |  |
|
| f[...]:=rhs | downvalue for f |
| f/:f[g[...]][...]:=rhs | downvalue for f |
| g/:f[...,g,...]:=rhs | upvalue for g |
| g/:f[...,g[...],...]:=rhs | upvalue for g |
Possible positions for symbols in definitions.
As discussed in
"The Meaning of Expressions", you can use
Mathematica symbols as "tags", to indicate the "type" of an expression. For example, complex numbers in
Mathematica are represented internally in the form
Complex[x, y], where the symbol
Complex serves as a tag to indicate that the object is a complex number.
Upvalues provide a convenient mechanism for specifying how operations act on objects that are tagged to have a certain type. For example, you might want to introduce a class of abstract mathematical objects of type
quat. You can represent each object of this type by a
Mathematica expression of the form
quat[data].
In a typical case, you might want
quat objects to have special properties with respect to arithmetic operations such as addition and multiplication. You can set up such properties by defining upvalues for
quat with respect to
Plus and
Times.
This defines an upvalue for quat with respect to Plus. |
The upvalue you have defined is used to simplify this expression.
| Out[16]= |  |
|
When you define an upvalue for
quat with respect to an operation like
Plus, what you are effectively doing is to extend the domain of the
Plus operation to include
quat objects. You are telling
Mathematica to use special rules for addition in the case where the things to be added together are
quat objects.
In defining addition for
quat objects, you could always have a special addition operation, say
quatPlus, to which you assign an appropriate downvalue. It is usually much more convenient, however, to use the standard
Mathematica Plus operation to represent addition, but then to "overload" this operation by specifying special behavior when
quat objects are encountered.
You can think of upvalues as a way to implement certain aspects of object-oriented programming. A symbol like
quat represents a particular type of object. Then the various upvalues for
quat specify "methods" that define how
quat objects should behave under certain operations, or on receipt of certain "messages".