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 > >