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'm trying to write a custom function in Mathematica, myFunc[x]. I want to be able to define properties of the function so that the user can call myFunc[x]["Property"] and have it return the value stored in Property.

Basically I'm looking for the same functionality as lmf=LinearModelFit[data,x,x] where you can use lmf["RSquared"] to return the correlation coefficient of the fit, but lmf[5] returns the y value for x=5.

Here's an example of the function that I'm trying to define:

myFunc[data_]:=Module[
   {localProperty, x},
   localProperty = "123abc";
   Return[data+3x];
];

And the output should look like:

(* User enters normal data *)
In[1]: foo = myFunc[5]
Out[1]: 5+3x

(* User enters the name of the property *)
In[2]: foo["localProperty"]
Out[2]: "123abc"

(* User wants to enter a value and evaluate *)
In[3]: foo[3]  (* 5+3*3 *)
Out[3]: 14

Is there a way to do this? I believe it's a built-in capability, as witnessed by the symbolic FittedModel object, but all my searches have so far yielded nothing of use. I've looked at Property, Option, Attributes, and just using If statements to try and catch the cases where the user types in a property name.

I'm using Mathematica 8.

Thanks,

Edit: Solution Found

Thanks to the guys below, I've found my solution and now have a function that will fit a linear model and plot up the data in one go. I can also extract the model or model parameters by entering different options. Below is the code if anyone is interested.

Clear[ListPlotWithTrendline];
ListPlotWithTrendline[data_, addopts___] := Module[
   {lmf, lmfPlot, plot, plots, dr, lmfPlotMinX, lmfPlotMaxX, pos, pr, addopts2, lmfpr, returnValue},
   dr = {Min[data[[All, 1]]], Max[data[[All, 1]]]};

   (* The workaround to get my 'opts' variable to work with Plot *)
   addopts2 = Flatten[{addopts}, 1];
   pos = Quiet[Check[Position[Map[StringSplit[ToString[#]][[1]] &, addopts2], "PlotRange"][[1, 1]], 0]];
   pr = Quiet[Check[ToExpression[addopts2[[pos, 2]]], dr]];
   (* Routine to extract plotrange values for use in the extrapolation *)
   lmfpr = If[
     pos > 0,
     Evaluate[Which[

       (* Both X and Y ranges are given *)
       Dimensions[pr] == {2, 2}, {pr[[1, 1]], pr[[1, 2]]},

       (* The X value is All or Automatic, so we use dataRange *)
       pr[[1]] === All || pr[[1]] === Automatic || pr[[1]] === Full, dr,

       (* The X range is given and Y is Full, All, or Automatic *)
       Length[pr] == 2 && Length[pr[[1]]] == 2, {pr[[1, 1]], pr[[1, 2]]},

       (* Only the Y range is given or there was some error, so use the dataRange *)
       True, dr
       ]],
     dr];
   lmf = LinearModelFit[data, x, x];
   lmfPlot = Plot[lmf[x], {x, lmfpr[[1]], lmfpr[[2]]}, #] &@addopts2;
   plot = ListPlot[data, #] &@addopts2;
   plots = Show[plot, lmfPlot];

   (* Define the return values, based on what property the user is interested in. *)
   returnValue["FittedModel"] = lmf;
   returnValue["Plot"] = plots;
   returnValue["FitPlot"] = lmfPlot;
   returnValue["ScatterPlot"] = plot;
   returnValue["RSquared"] = lmf["RSquared"];
   returnValue["Slope"] = lmf[[1, 2, 2]];
   returnValue["Intercept"] = lmf[[1, 2, 1]];
   Return[returnValue];
   ];

I'm sure it's not the most efficient or clean code around, but it works so I'm happy. :-) Here's an example: Example of ListPlotWithTrendline

share|improve this question

migrated from stackoverflow.com Dec 13 '13 at 21:33

This question came from our site for professional and enthusiast programmers.

    
Oh crap, thanks Szabolcs. I didn't notice that I was at stackoverflow. I'll flag it. –  dthor Dec 13 '13 at 20:14
    
It's not off topic here, it was just a suggestion that you post on Mathematica.SE as answers come more quickly there. –  Szabolcs Dec 13 '13 at 20:16
    
Yeah that's where I normally go. But your answer below works, so yay! :-) –  dthor Dec 13 '13 at 20:46

2 Answers 2

up vote 9 down vote accepted

Here's an example that should help:

In[1]:= 
makeFoo[] := foo @@ RandomInteger[10, 3]
foo[first_, _, _]["First"] := first
foo[_, second_, _]["Second"] := second
foo[_, _, third_]["Third"] := third
foo[a_, b_, c_][x_] := a x^2 + b x + c

In[6]:= f = makeFoo[]
Out[6]= foo[4, 2, 9]

In[7]:= f["Second"]
Out[7]= 2

In[8]:= f[x]
Out[8]= 9 + 2 x + 4 x^2

For the record:

Definitions of the form f[...] := ... are called DownValues.

Definitions of the form f[...][...] := ... (or with more levels, as in f[...][...]...[...] := ...) are called SubValues (the function doesn't seem to be documented but it works exactly the same way as DownValues).

share|improve this answer
    
Cool, thanks. It took a little bit of work and I had to add an option for what I wanted to do, but everything's working well thanks to this answer. I now have a function that will plot up a scatterplot and the fitted line of data, and can also return the slope, Rsq, and intercept. –  dthor Dec 13 '13 at 20:43

For your specific problem, the following piece of code will work

In[1]:=  myFunc[data_] := Module[{result},
            result["property"] = "123abc";
            result[v_] := data + v*3;
            Return[result];
         ];

In[2]:=  a=myFunc[10];
         a["property"]
         a[x]

Out[2]=  123abc
         10 + 3x
share|improve this answer
    
So this appears to just be another way of setting the downvalues that Szabolcs described. Would you agree? –  dthor Dec 13 '13 at 20:54
    
Yes, it is! In fact, it is the same with the definition of myFunc as well. The alternative would be to return a list of rules, then you can use result/.Property to retrieve it, similar to what you would find in the return of Solve. You will find it quite powerful and convenient either way. –  Xiaolei Zhu Dec 13 '13 at 21:30
    
You can safely replace Return[result]; by return (note the lack of semicolon). –  Szabolcs Dec 13 '13 at 21:55
    
@dthor yes, this solution creates a new unique symbol (using Module) and sets DownValues on it. It's a different solution. My solution uses SubValues and the very same head (foo) every time. So here you'll have "objects" named similar to result$123 and my solution will give you objects such as foo[...]. I think both are good solutions. –  Szabolcs Dec 13 '13 at 21:58

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.