MathGroup Archive 2010

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: sq[x_]:=x*x vs sq=Function[x, x*x]

  • To: mathgroup at smc.vnet.net
  • Subject: [mg113867] Re: sq[x_]:=x*x vs sq=Function[x, x*x]
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Tue, 16 Nov 2010 05:06:23 -0500 (EST)

Hi kj,

The two forms are only superficially the same. There are many differences
in both the semantics and the use. Below I will illustrate some.

Generally, pure functions are more common as defined at run-time and used
as arguments to higher-order functions which then call them. Pure functions
need
not be stored in a variable or given a name (although one can do it - as you
did).
They are less suited for type-checking and dispatching based on different
forms
of the argument (overloading). For example, restricting your argument to be
numeric
will require something like

sq = Function[x,If[NumericQ[x], x*x, $Failed]];

and here I assumed that the non-numeric input is an error. But what if I
want the function
to return unevaluated for non-numeric input? There is no easy way to achieve
that with
pure functions, while this is often useful.

Functions defined with patterns, OTOH, are not functions really - they are
global rewrite
rules which imitate the function call semantics. They are well - suited for
overloading and
argument checks based on patterns. For example, I may restrict:

sq[x_?NumericQ]:=x^2

In the absence of other definitions for sq, it will remain unevaluated on
non-numeric inputs
(which may be desirable). I can add more definitions as well, for example:

sq[x_Symbol]:=x

or whatever I need, including the convenient catch-all conditions such as

sq[___]:=$Failed

You can add all this to pure function with If or Switch or Which, but this
would arguably be
a worse solution.

The next issue is multiple arguments. This issue is best illustrated by the
following:

In[22]:= Function[x, x^2][2, 3]

Out[22]= 4

What happens is that the extra arguments to pure functions are silently
ignored by them.
This is a source of subtle and hard to track bugs. The pattern-defined
functions give you more
safety and control - the sq defined with the patterns as above would either
return unevaluated or
throw and error if you have defined a catchall condition.

Besides, there is no way to define a pure function with a variable number of
arguments
*and* attach some attribute to it, unless you use the undocumented form
Function[Null, body,{attributes}],
with <body> referring to ## (SlotSequence). With pattern-defined functions,
it is as easy as using the BlankSequence (__) or BlankNullSequence (___)
patterns, like x__, and assigning attributes
to the function name.

Speaking of more advanced uses, pattern-defined functions allow you to share
local variables
between the body and the condition. To illustrate, here is a model example:
a function to return
its integer argument squared when the number of primes between 1 and the
input is larger than half
the input, otherwise return the input.

Clear[f];
f[n_Integer] :=
  Module[{nprimes = Length@Select[Range[n], PrimeQ]},
   n^2 /; nprimes >= n/2];
f[n_Integer] := n;

In[59]:= f /@ Range[20]

Out[59]= {1, 4, 9, 16, 25, 36, 49, 64, 9, 10, 11, 12, 13, 14, 15, 16, \
17, 18, 19, 20}

What matters is that by using this construct, some non-trivial computation
may happen
in the body of the first definition, but, if the condition (involving the
results of computation)
has not been satisfied, the function goes on to the next rule in the list of
definitions, as if
the computation of the body of the preceding ones never took place. In this
primitive example
this looks like a weird way to solve the problem, but there are situations
where this construct
is indispensable. With pure functions, it will be much harder to do,
especially given that the
list of definitions can be manipulated dynamically at run-time (memoization
is a prime example).

Speaking of memoization, dynamic programming is another place where
rule-based functions
give you the edge. Here is a well-known memoization-based implementation for
Fibonacci
numbers:

Clear[fib];
fib[0] = fib[1] = 1;
fib[n_Integer?Positive] := fib[n] = fib[n - 1] + fib[n - 2]

You will have a hard time using a pure function to implement memoization.
And besides,
this is the simple form of memoization. You can also use the form where new
rules
containing patterns are formed at run-time and added to the function, which
would be
even harder to reproduce with the pure functions.

On the other hand, pure functions are useful to produce closures - functions
which encapsulate
(part of the) state of the surrounding environment. Since you don't need to
introduce names
for them, and since they are well - suited to be created at run-time, they
are typically a better
fit for this than functions based on patterns.

One more advantage of pure functions is that, at least in their anonymous
form  (#-& notation),
they are typically faster than pattern-defined ones, since no
pattern-matching is taking place.
Moreover, they often can be compiled with Compile, which is not the case for
pattern-based
functions. This becomes now even more important for parts of the program
where speed is
relevant, given the new capabilities of Compile in version 8, such as
compilation to C or
automatic parallelization (multi-threading).

There are many more differences. I will mention just one more: the pattern -
defined functions
allow one to assign a wider set of attributes to them than the set of
attributes that make sense
to pure functions. Therefore, any problem which requires an attribute not
understood by pure
functions can not be solved with them (without re-implementing a part of the
pattern-matcher
and evaluator), but can be solved with the pattern-based ones.

To summarize, pure functions are useful in situations more typical for
programming / software
engineering problems, while pattern-defined functions (rules really) are
more useful for symbolic
programming. One more thing:  because patterns and rules are at the heart of
Mathematica
evaluator, rule-based defintions often give you the most direct solutions,
since they can leverage
the most powerful features of Mathematica such as its term-rewriting engine
and the pattern-matcher.

Hope this helps.

Regards,
Leonid




On Mon, Nov 15, 2010 at 1:52 PM, kj <no.email at please.post> wrote:

>
>
> One could define a squaring function as either
>
> sq[x_] := x*x
>
> or
>
> sq = Function[x, x*x];
>
> Why/when would one want to choose one form over the other?
>
> TIA!
>
> kj
>
>



  • Prev by Date: Re: command to save as .m file
  • Next by Date: Re: reading different locations of a matrix specified
  • Previous by thread: sq[x_]:=x*x vs sq=Function[x, x*x]
  • Next by thread: Re: sq[x_]:=x*x vs sq=Function[x, x*x]