Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I can write either

Integrate[x^2, {x,0,3}]

or

f[x_] = x^2
Integrate[f[x],{x,0,3}]

and get the same computation. Suppose I wanted to define a function like that for myself - that is, a function into which I can pass either a defined function (like f[x] in the second example) or an expression (like x^2 in the first example), so I can do whatever I want to the function on some interval. For example, I can say

SetAttributes[i,HoldAll];
i[fn_,intvl_] := Block[{var = intvl[[1]], ii = Function[Evaluate[var],fn]},
                         p1 = Plot[ii[Evaluate[var]],intvl];
                         p2 = Plot[ii[Evaluate[var]]^2,intvl];
                         GraphicsRow[{p1,p2}] ];

Then either

i[x^2,{x,1,2}]

or

f[x_] = x^2
i[f[t],{t,1,2}]

will produce side-by-side plots of x^2 and x^4.

Is this the right (or even a right) way to do this?

share|improve this question
    
Could you please show usage of your function? What do you put in and what do you get out? Does it work as you expected? Also you start from Integrate which is a symbolic integration, but end up with Table inside your example. This is a bit confusing: are you trying to do numerical integration now with table? –  Vitaliy Kaurov Jul 30 '12 at 16:44
    
@VitaliyKaurov: I made some changes to hopefully clarify. No, I was not trying to do numerical integration - that was just an example. I'm trying to understand a general method. See expanded example above. –  rogerl Jul 30 '12 at 17:07
    
I updated my answer. The very top part relates to yuor function. –  Vitaliy Kaurov Jul 31 '12 at 5:40

2 Answers 2

up vote 9 down vote accepted

To define your own function with the same properties that (e.g.) Plot has, there are several ingredients which I'll try to develop step by step:

Start by trying the simplest thing:

i0[fn_, intvl_] := Module[{},
   p1 = Plot[fn, intvl];
   p2 = Plot[fn^2, intvl];
   GraphicsRow[{p1, p2}]];

Clear[x];

i0[x^2, {x, 0, 2}]

i0

Why HoldAll is needed

But now we immediately get in trouble when we do

x = 1;
i0[x^2, {x, 1, 2}]

Plot::itraw: Raw object 1 cannot be used as an iterator.

This never happens if you just call Plot[x^2, {x,1,2}].

The reason is that Plot has attribute Holdall, and you correctly implemented that already:

SetAttributes[i, HoldAll];

i[fn_, intvl_] := Module[{},
   p1 = Plot[fn, intvl];
   p2 = Plot[fn^2, intvl];
   GraphicsRow[{p1, p2}]];

x = 1;
i[x^2, {x, 0, 2}]

i0

Using SyntaxInformation and declaring local variables

The next thing you'd want to do is to give the user some feedback when the syntax is entered incorrectly:

SyntaxInformation[i] = {"LocalVariables" -> {"Plot", {2, 2}}, 
   "ArgumentsPattern" -> {_, _, OptionsPattern[]}
   };

This causes the variable x to be highlighted in turquois when it appears as the function variable, telling us visually that the global definition x = 1 should have no effect locally. The OptionsPattern is in there just in case you decide to implement it later.

But how does one really ensure that the x is local? In the function so far, we're lucky because we pass all arguments on to Plot which also has attribute HoldAll, so that we don't have to worry about the global value of x = 1 creeping in before we give control to the plotting functions.

Where your approach fails

Things are not so easy in general, though. This is where your construction fails, because as soon as you write intvl[[1]] the evaluator kicks in and replaces x by 1. To see this, just try to add axis labels x, y to the second plot, and modify the first plot to show the passed-in function plus x:

i[fn_, intvl_] := Block[{var = intvl[[1]]},
   p1 = Plot[fn + var, intvl];
   p2 = Plot[fn^2, intvl, AxesLabel -> {var, "y"}];
   GraphicsRow[{p1, p2}]];

x = 1;
i[x^2, {x, 0, 2}]

i not local

The first plot should show x^2 + x but shows x^2 + 1, and the second plot has horizontal label 1 instead of x. This happens because I've declared the global variable x = 1 and didn't prevent it from being used inside the function i. It's a somewhat artificial example, but I wanted to address the case where the independent variable really is needed by itself inside the function. When it appears as the first element of the second argument in i, a variable is supposed to be a dummy variable for local use only (that's how Plot treats it).

Wrapping arguments in Hold and its relatives

So we have to really make use of the HoldAll attribute of out function now, to prevent the variable from being evaluated.

Here is a way to do this for the argument pattern of the above example. The main thing is that it always keeps the external variable and function wrapped in Hold, HoldPattern or HoldForm so it never gets evaluated.

If you look at the Plot commands in the function i below, you'll see that I'm now using local variables: ii is the function, and localVar is the independent variable. This means I needed to replace the externally given symbol (say, x as above) by a new locally defined symbol localVar. To do that, I first need to identify the external variable symbol, called externalSymbol, from the first entry in the range intvl passed to the function. I chose to do this with a replacement rule that acts on Hold[intvl]and returns a held pattern that can be used later to replace the external variable in the given function fn by using the rule externalSymbol :> localVar. The result is the local function ii which I then plot using the minimum and maximum limits from Rest[intvl].

So here is how the function looks if I want it to do the same as in the last failed attempt (trying to plot x^2 + x when the given function is x^2):

i[fn_, intvl_] := Module[{
    ii, p1, p2,
    externalSymbol,
    localVar, min, max
    },
   externalSymbol = ReleaseHold[
     Hold[intvl] /. {x_, y__} :> HoldPattern[x]
     ];
   ii = ReleaseHold[
     Hold[fn] /. externalSymbol :> localVar];
   {min, max} = Rest[intvl];
   p1 = Plot[ii + localVar, {localVar, min, max}];
   p2 = Plot[ii^2, {localVar, min, max}, 
     AxesLabel -> {HoldForm @@ externalSymbol, "y"}];
   GraphicsRow[{p1, p2}]];

z = 1;
i[z^2, {z, 0, 2}]

Local vars work

So the symbol z that I used here has been handled properly as a local variable even though it had a global value.

share|improve this answer
    
A big +1 for the nice and detailed explanation. –  Ajasja Jul 31 '12 at 7:13
    
@Jens: Thanks for the great explanation. I guess I should have figured out that your simple first try worked...I keep confusing myself about the difference between x^2, f[x], and f when f[x_]:=x^2. –  rogerl Jul 31 '12 at 11:53
    
@Jens And, I learned a lot about holds and patterns from this answer. Great answer. –  rogerl Jul 31 '12 at 13:30

Simplifying your function

Your function works fine, but I am not sure why do you need to make it so complicated. Also you introduced global variables p1, p2 - which is not a good thing to do. Bloc, HoldAll, Evaluate - all this can be omitted. What about something like this:

f[f_, in_] := GraphicsRow[{Plot[f, in], Plot[f^2, in]}]

f[Sin[t], {t, 2, 12}]

enter image description here

Using pure form:

i[f_, in_] := Integrate[f@in[[1]], in]

i[Sin[#^2] &, {t, 1, 2}]

enter image description here

Using Currying

You probably want something like this:

f[a_, b_][f_] := Integrate[f[x], {x, a, b}]

then defining for example

g[t_] := Sin[t^2]

you can pass the name of the function as a variable

f[1, 2][g]

Sqrt[[Pi]/2] (-FresnelS[Sqrt[2/[Pi]]] + FresnelS[2 Sqrt[2/[Pi]]])

You of course could define it as f[a,b,f], f[f,{a,b}] or some other ways. The construct that I suggested (called Currying) allows you to do things like mapping of the list and similar:

f[1, 2] /@ {Sin, Cos, Tanh, g} // Column // TraditionalForm

enter image description here

share|improve this answer
    
This does not do what I want; perhaps I wasn't clear. I would like to be able to pass t^2 itself as the function to be integrated, and use a range of the form {t,1,2} to specify both the variable and the range. –  rogerl Jul 30 '12 at 15:51
    
@rogerl updated –  Vitaliy Kaurov Jul 30 '12 at 16:11
    
Sure, that works. And thanks. But what's wrong with my original post? It does exactly what I want in the cases I've tested, while yours does not. Is my original post incorrect, fragile, or OK? –  rogerl Jul 30 '12 at 16:12
    
@rogerl I think your formulation is a bit cumbersome. I posted a comment on your original question. I hope we will be able to answer if you explain more clearly what you need. –  Vitaliy Kaurov Jul 30 '12 at 16:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.