• No results found

Functions with multiple definitions

IV. Rules, patterns and functions

4.7 Functions with multiple definitions

We have seen such functions many time in our examples already, but here we will treat them more system-atically. So, In Mathematica a function can have more than one definition. More precisely, there can be more than one rule associated with a function symbol, with different rules applying on different forms of arguments. In particular, one and the same function can be defined differently on different number and types of arguments. All this is possible because patterns are used for function definitions. To start with, consider an example:

Ÿ 4.7.1 Example: a discontinuous function

Consider a function which is 1 on integer numbers and - 1 on those which are not integer (in the spirit of the Dirichlet function - the latter is very different of course, being defined differently on rational and irrational numbers) :

Clear@fD;

f@x_IntegerD:=1;

f@x_D:= -1;

We check :

8f@1D, [email protected], f@2D, [email protected], f@4D, f@PiD, f@ED<

81, -1, 1, -1, 1, -1, -1<

Notice that in the second part of our definition in this case we don’ t necessarily need to use a pattern that is exact opposite of the first one, which would look like f[x_ /; Not[IntegerQ[x]]]. This is so because when the first pattern does not match, the second will match automatically, since it matches any single expression.

It is interesting that if we plot this function, the values on the integers (1) are not visible. One may think that this is because the probability that the sample point in the Plot procedure becomes exactly integer is very small (integers represent a set of measure 0). However, the truth is simpler: the numerical values for the sample points will never match the _Integer pattern just syntactically.

Plot@f@xD, 8x, 0, 10<D

2 4 6 8 10

-2.0 -1.5 -1.0 -0.5

Ÿ 4.7.2 Adding more definitions

Let us now add another definition to our function, so that it will give 2 on every even number :

f@x_Integer ? EvenQD:=2;

Check now :

8f@1D, [email protected], f@2D, [email protected], f@4D, f@PiD, f@ED<

81, -1, 1, -1, 1, -1, -1<

We see that nothing changed - it does not work (It is interesting that the result is correct in version 6). The reason can be seen by looking at function definitions:

? f

Global‘f

f@x_IntegerD:=1 f@x_Integer? EvenQD:=2

f@x_D:= -1

By the way, the question mark in this context means the Information command, and returns the informa-tion contained in the global rule base on a given symbol (secinforma-tion 2.2.2).

We see that the reason for the above behavior is that Mathematica was able to figure out that the pattern f[x_Integer?EvenQ] is more specific than the pattern f[x_], but unable to figure out that it is also more specific than f[x_Integer] (this refers to versions prior to 6.0. In 6.0, the pattern-matcher does figure out the latter fact as well). The simplest thing one can do is to redefine the function, by placing definitions in a different order:

Clear@fD;

f@x_Integer ? EvenQD:=2;

f@x_IntegerD:=1;

f@x_D:= -1;

Check now :

8f@1D, [email protected], f@2D, [email protected], f@4D, f@PiD, f@ED<

81, -1, 2, -1, 2, -1, -1<

Ÿ 4.7.3 Changing definitions selectively

The above pattern - based mechanism of function definitions allows them to be very flexible. In particular, it is quite possible to change or delete a given definition corresponding to the specific pattern, without introducing changes in other definitions associated with this function.

To change an already existing definition for some pattern, to a new one, one just needs to redefine a function on this particular pattern with a new right hand side. For example, we want our first definition for < f > from the previous example to return not 2, but 4 on even numbers. We simply redefine :

f@x_Integer ? EvenQD:=4;

Observe :

? f

Global‘f

f@x_Integer? EvenQD:=4 f@x_IntegerD:=1

f@x_D:= -1

It is not required that the pattern tags (names) in a new pattern are literally the same as those for the old one (but otherwise the patterns have to be the same if we want to replace old definition with the new one) :

f@y_Integer ? EvenQD:=6;

Check now :

? f

Global‘f

f@y_Integer? EvenQD:=6 f@x_IntegerD:=1

f@x_D:= -1

As we see, the old definition still got replaced by a new one, since the pattern essentially did not change, and Mathematica can see that (this wasn’t the case in some early versions).

Ÿ 4.7.4 Warning: a common mistake

It is quite common during the development of some function to change the patterns for the function’ s arguments. However, if one does not remove the old definition, it will remain in the rule base and may lead to errors when testing the function. Always make sure that you clear old definitions when you change a definition (argument patterns) of the function you are developing. One way to automate this is to always start with a line Clear[f] before any definition for < f > is entered - this is the practice I usually adhere to.

Ÿ 4.7.5 Selective removal of the definitions

If we would like to remove the definition of the function < f > associated with some pattern < pattern >, there is a special built - in command tailor - made for this : Unset. Its short - hand notation is <= .> (equal dot). Thus, we have to use either f[pattern] =., or Unset[f[pattern]]. This will remove a given definition.

Let us for instance remove a first definition of the above function < f > . This is done as follows:

f@y_Integer ? EvenQD =. We now check :

? f

Global‘f

f@x_IntegerD:=1

f@x_D:= -1

As a side remark, it is interesting that the above possibilities of selective changes and/or removals of function definitions can be used in quite an unusual way : the function itself may (temporarily, for instance) change part of its own definitions during its execution. One reason why this may be useful is that sometimes it is a possible workaround to avoid an infinite recursion.

Ÿ 4.7.6 Case study: changing the weights of words

Ÿ The problem

Consider some set of words, on pairs of which we will define a model "mutual attraction" function which will be equal to the number of common letters in the given pair of words. As a model set of words we will take the one we have used already :

wlist = ToLowerCasež 8"Most", "of", "this", "Part", "assumes", "no",

"specific", "prior", "knowledge", "of", "computer", "science",

"Nevertheless", "some", "of", "it", "ventures", "into",

"some", "fairly", "complicated", "issues", "You", "can",

"probably", "ignore", "these", "issues", "unless", "they",

"specifically", "affect", "programs", "you", "are", "writing"<; (we have converted words letters to the lowercase).

Ÿ The solution

Ÿ

The solution

Our function will be a function of two strings - words. It is very easy to write - split words to characters, and compute a length of the intersection of the character lists:

Clear@wordFunctionD;

wordFunction@x_String, y_StringD:=

Length@Intersection@Characters@xD, Characters@yDDD; For example :

wordFunction@"word", "word"D 4

Ÿ Testing the solution

Let us now make a list of our words together with the weights that these words have with respect to some fixed word, say "computer" :

wlist1=Table@8wlist@@iDD, wordFunction@"computer", wlist@@iDDD<, 8i, 1, Length@wlistD<D

88most, 3<, 8of, 1<, 8this, 1<, 8part, 3<, 8assumes, 3<, 8no, 1<, 8specific, 3<, 8prior, 3<, 8knowledge, 2<, 8of, 1<, 8computer, 8<, 8science, 2<, 8nevertheless, 3<, 8some, 3<, 8of, 1<, 8it, 1<,

8ventures, 4<, 8into, 2<, 8some, 3<, 8fairly, 1<, 8complicated, 6<, 8issues, 2<, 8you, 2<, 8can, 1<, 8probably, 3<, 8ignore, 3<,

8these, 2<, 8issues, 2<, 8unless, 2<, 8they, 2<, 8specifically, 3<, 8affect, 3<, 8programs, 4<, 8you, 2<, 8are, 2<, 8writing, 2<<

We can now sort the words according to the highest weight:

Sort@wlist1, ð1@@2DD > ð2@@2DD&D

88computer, 8<, 8complicated, 6<, 8programs, 4<, 8ventures, 4<, 8affect, 3<, 8specifically, 3<, 8ignore, 3<, 8probably, 3<,

8some, 3<, 8some, 3<, 8nevertheless, 3<, 8prior, 3<, 8specific, 3<, 8assumes, 3<, 8part, 3<, 8most, 3<, 8writing, 2<, 8are, 2<,

8you, 2<, 8they, 2<, 8unless, 2<, 8issues, 2<, 8these, 2<, 8you, 2<, 8issues, 2<, 8into, 2<, 8science, 2<, 8knowledge, 2<, 8can, 1<, 8fairly, 1<, 8it, 1<, 8of, 1<, 8of, 1<, 8no, 1<, 8this, 1<, 8of, 1<<

Ÿ Manipulating weights of individual words

Suppose now that we want to bring some words up in the list, that is, change the "strength function" of these words with the word "computer" by hand. Such new definitions can be implemented according to the above described scheme - we just have to add specific definitions of our weight function on specific words. Let these words be "programs", "knowledge" and "science". Let us give them weights :

wordFunction@"computer", "programs"D =20;

wordFunction@"computer", "science"D =15;

wordFunction@"computer", "knowledge"D =10;

Now let us have a look on the new definitions of < wordFunction > :

? wordFunction

Global‘wordFunction

wordFunction@computer, knowledgeD =10 wordFunction@computer, programsD =20 wordFunction@computer, scienceD =15

wordFunction@x_String, y_StringD:=Length@HCharacters@xDL Ý HCharacters@yDLD

Let us note two things: first, in these latter definitions we used Set rather than SetDelayed (it does not matter much for constant r.h.s.), and second, that these definitions are placed before the more general one even though they were added later - Mathematica figured out their level of generality and positioned them accordingly. This means that they will be applied before the more general one, and thus the general one does not "threaten" the more specific ones. Let us check now :

Sort@Table@8wlist@@iDD, wordFunction@"computer", wlist@@iDDD<, 8i, Length@wlistD<D, ð1@@2DD > ð2@@2DD&D

88programs, 20<, 8science, 15<, 8knowledge, 10<, 8computer, 8<, 8complicated, 6<, 8ventures, 4<, 8affect, 3<, 8specifically, 3<, 8ignore, 3<, 8probably, 3<, 8some, 3<, 8some, 3<, 8nevertheless, 3<, 8prior, 3<, 8specific, 3<, 8assumes, 3<, 8part, 3<, 8most, 3<,

8writing, 2<, 8are, 2<, 8you, 2<, 8they, 2<, 8unless, 2<,

8issues, 2<, 8these, 2<, 8you, 2<, 8issues, 2<, 8into, 2<, 8can, 1<, 8fairly, 1<, 8it, 1<, 8of, 1<, 8of, 1<, 8no, 1<, 8this, 1<, 8of, 1<<

Now let us remove these definitions :

wordFunction@"computer", "programs"D =.;

wordFunction@"computer", "science"D =.;

wordFunction@"computer", "knowledge"D =.;

We check now :

? wordFunction

Global‘wordFunction

wordFunction@x_String, y_StringD:=Length@HCharacters@xDL Ý HCharacters@yDLD

Only the general one remains.

Ÿ Automating the process (advanced)

It is interesting that the process of giving new definitions to some function can be automated by another function. In particular, let us define :

Clear@giveDefinitionsD;

giveDefinitions@f_, args_List, values_ListD ; Length@argsD ŠLength@valuesD:=

HMapThread@Set, 8Unevaluated@f@Sequencežž ðDD&žargs, values<D;L;

This function is quite general (although one may write it more efficiently) : it takes the name of another function, a list of arguments and a list of values, and creates the new definitions for the supplied function accordingly. At the same time, any other definitions of this function will not be affected. This is our exam -ple :

giveDefinitions@wordFunction, 88"computer", "programs"<,

8"computer", "science"<, 8"computer", "knowledge"<<, 820, 15, 10<D

We check now :

? wordFunction

Global‘wordFunction

wordFunction@computer, knowledgeD =10 wordFunction@computer, programsD =20 wordFunction@computer, scienceD =15

wordFunction@x_String, y_StringD:=Length@HCharacters@xDL Ý HCharacters@yDLD

The technique just illustrated allows some functions to manipulate the definitions of other functions, which allows us to control the program execution in a very flexible way.

Functions like < giveDefinitions >, which in effect manipulate other functions, are called higher - order functions. Their use is quite common in the functional programming style. We will cover a lot of built - in higher - order functions in the chapter V.

Clear@wordFunction, wlist, wlist1D;