5

Related A problem in Mathematica 8 with function declaration

Clear["Global`*"]

model = 4/Sqrt[3] - a1/(x + b1) - a2/(x + b2)^2 - a3/(x + b3)^4;
fit = {a1 -> 0.27, a2 -> 0.335, a3 -> -0.347, b1 -> 4.29, b2 -> 0.435,
b3 -> 0.712};

functionB1[x_] = model /. fit;
functionB2[x_] := model /. fit;

The evaluation difference between functionB1 and functionB2 can be revealed by Trace command in mma, as below:

functionB1[Sqrt[0.2]] // Trace
functionB2[Sqrt[0.2]] // Trace 

I have no question about functionB1. what puzzles me is that because functionB2[Sqrt[0.2]] doesn't even gives a numeric result but gives a function of x 4/Sqrt[3] - 0.335/(0.435 + x)^2 + 0.347/(0.712 + x)^4 - 0.27/( 4.29 + x), and then how its plot Plot[functionB2[Sqrt[x]], {x, 0, 1}] is possible?

I mean when you run Plot[functionB2[Sqrt[x]], {x, 0, 1}], what happens inside mma is:

x takes a number, say, 0.2, then 0.2 is finally passed to functionB2, but functionB2 gives a function, not a number. Then how is the following figure generated?

enter image description here

And its trace result ( Plot[functionB2[Sqrt[x]], {x, 0, 1}] // Trace ) seems very unreadable. I wonder the clear plotting process of functionB2. Can anybody show it?

thanks~ :)

Community
  • 1
  • 1
FreshApple
  • 592
  • 3
  • 14

3 Answers3

5

SetDelayed acts as a scoping construction. Arguments are localized if necessary. Any variables that explicitly match the arguments are bound within this scope, others are not.

In[78]:= a[x_] := x^2 + b
         b = x^4;

(* the first x^2 is explicitly present and bound to the argument. 
   The x in x^4 present via b is not bound *)

In[80]:= a[x]

Out[80]= x^2 + x^4 (* this is what you would expect *)

In[81]:= a[y]

Out[81]= x^4 + y^2 (* surprise *)

In[82]:= a[1]

Out[82]= 1 + x^4 (* surprise *)

So, what you could do is one of two things:

  • Use Evaluate: functionB2[x_] := Evaluate[model /. fit];
  • Make dependence of model on x explicit:

    In[68]:= model2[x_] = 4/Sqrt[3] - a1/(x + b1) - a2/(x + b2)^2 - a3/(x + b3)^4;

    In[69]:= functionB3[x_] := model2[x] /. fit;

    In[85]:= functionB3[Sqrt[0.2]]

    Out[85]= 2.01415

Edit because of question update
Because of your definition of functionB2 any argument value yields the same result, as explained above:

In[93]:= functionB2[1]

Out[93]= 4/Sqrt[3] - 0.335/(0.435 + x)^2 + 0.347/(0.712 + 
   x)^4 - 0.27/(4.29 + x)

In[94]:= functionB2["Even a string yields the same ouput"]

Out[94]= 4/Sqrt[3] - 0.335/(0.435 + x)^2 + 0.347/(0.712 + 
   x)^4 - 0.27/(4.29 + x)

However, this expression contains x's and therefore it can get a numerical value if we provide a numerical value for x:

In[95]:= functionB2["Even a string yields the same ouput"] /. x -> 1

Out[95]= 2.13607

Well, this basically what Plot does too. This is why you still get a plot.

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
  • @belisarius Thanks! Shouldn't you remind me of the three things we normally do? ;-) – Sjoerd C. de Vries May 19 '11 at 13:04
  • Thanks, Sjoerd. So, the first step mma deals with the plot command is: evaluating the function to be plotted? I previously thought that the fist step when plotting a function is x takes a value, then the function catch this value and returs a result. If this were right, then func = Sin[x]; Plot[func,{x,0,1}] will never yield a figure. Sorry for this stupid misunderstanding. -_-! – FreshApple May 19 '11 at 13:07
  • 1
    @FreshApple Plot holds its arguments (it has the `HoldAll` attribute). That's why you have to use `Plot[Evaluate[Table[Sin[k x], {k, 1, 5}]], {x, 0, Pi}]` instead of `Plot[Table[Sin[k x], {k, 1, 5}], {x, 0, Pi}]` (at least, if you want the different graphs have different colors). Your func example works because when `Plot` starts throwing values at func it sets up an environment in which x gets its successive values and in which func is evaluated as long as mma knows transformations rules for it. In this case func -> Sin[x] -> Sin of whatever value x has in this environment. – Sjoerd C. de Vries May 19 '11 at 14:13
  • Sjoerd, congratulations on 3000 rep! – Mr.Wizard May 20 '11 at 00:42
5

The definition:

functionB2[x_] := model /. fit

is an instruction to Mathematica to replace all future occurrences of an expression that looks like functionB2[x_] with the result of substituting the value of the argument for every occurrence of x in the expression model /. fit. But there are no occurrences of x in model /. fit: the only symbols in that expression are model and fit (and, technically, ReplaceAll). Therefore, the definition returns a fixed result, model /. fit, irrespective of the argument. Indeed, the definition could just simply be:

functionB2a[] := model /. fit

If you plot functionB2a[], you will get the same result as if you plotted functionB2[anything]. Why? Because Plot will evaluate that expression while varying the symbol x over the plot range. It so happens that model /. fit evaluates to an expression involving that symbol, so you get the exhibited plot.

Now consider functionB1:

functionB1[x_] = model /. fit

It too says to replace all occurrences of x on the right-hand side -- but this time the right-hand side is evaluated before the definition is established. The result of evaluating model /. fit is an expression that does contain the symbol x, so now the definition is sensitive to the passed argument value. The net result is as if the function were defined thus:

functionB1a[x_] := 4/Sqrt[3]-0.335/(0.435+x)^2+0.347/(0.712+x)^4-0.27/(4.29+x)

So, if you plot functionB1[Sqrt[x]], the Plot command will see the expression:

4/Sqrt[3]-0.335/(0.435 +Sqrt[x])^2+0.347/(0.712 +Sqrt[x])^4-0.27/(4.29 +Sqrt[x])

Formal Symbols

When establishing definitions using SetDelayed, the name of the formal argument (x in this case) is independent of any occurrences of the same symbol outside of the definition. Such definitions can use any other symbol, and still generate the same result. On the other hand, definitions established using Set (such as functionB1) rely on the result of evaluating the right-hand side containing the same symbol as the formal argument. This can be a source of subtle bugs as one must take care not use symbols that accidentally have pre-existing down-values. The use of formal symbols (described in Letters and Letter-like Forms) for argument names can help manage this problem.

WReach
  • 18,098
  • 3
  • 49
  • 93
  • 1
    The situation is actually somewhat more complex since both `Set` and `SetDelayed` are scoping constructs and sometimes rename their pattern variables, while of course they will not be renamed for the r.h.s. of `Set`. This may not be apparent for the top-level definitions (where such renamings are not so aggressive), but this causes subtle problems for example inside `Module`, as discussed e.g. in this thread: http://forums.wolfram.com/mathgroup/archive/2010/May/msg00102.html. My own conclusion is that such implicit constructs as `f[x_]=a/.b` are not reliable and should not be used. – Leonid Shifrin May 19 '11 at 17:04
  • @Leonid Agreed. There are situations when it is useful to establish a generated definition, but it would be safer to set up such definitions using other strategies, e.g. ones that involve `With`, localized symbols and/or formal symbols. As you say in the cited post, the `Set` strategy can be very useful in an interactive context, but it is another one of those things (like evaluation leakage) that is hard to get correct when writing library code. – WReach May 19 '11 at 17:37
  • Yes, that's exactly what I meant. I do not mind doing the "scope surgery" and manipulating the variable bindings in scoping constructs, but my feeling is that such manipulations generally fall under 2 distinct categories - well-defined (those where you have the full control over the result), and not well-defined (those where you only have partial control). And I try to avoid the latter, while the example under discussion clearly belongs to it. Also, in my experience, it was always possible to achieve the goal with methods available within the first category. – Leonid Shifrin May 19 '11 at 17:46
2

You can understand what is going on by trying:

Table[functionB2[Sqrt[y]],{y,0.5,.5,.5}]
Table[functionB2[Sqrt[x]],{x,0.5,.5,.5}]
(*
{4/Sqrt[3] - 0.335/(0.435+ x)^2 + 0.347/(0.712+ x)^4 - 0.27/(4.29+ x)}
{2.03065}
*)

What is getting replaced is the x inside the definition of functionB2, not a formal argument.

Edit

The plot you are getting is not what you want. The Sqrt[x] is disregarded in functionB2[...] and the implicit x is replaced, as you can see here:

enter image description here

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190