MathGroup Archive 2011

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

Search the Archive

Re: Creating Objects

  • To: mathgroup at smc.vnet.net
  • Subject: [mg122303] Re: Creating Objects
  • From: "Oleksandr Rasputinov" <oleksandr_rasputinov at hmamail.com>
  • Date: Mon, 24 Oct 2011 05:14:15 -0400 (EDT)
  • Delivered-to: l-mathgroup@mail-archive0.wolfram.com
  • References: <j80qhd$afd$1@smc.vnet.net>

On Sun, 23 Oct 2011 11:33:17 +0100, Gabriel Landi <gtlandi at gmail.com>  
wrote:

> Dear MathGroup (and perhaps some Wolfram employees),
>
> Does anyone know how to create Objects in Mathematica, such as those  
> produced by LinearModelFit or NDSolve`ProcessEquations?
> For instance, let data be a set of statistical data. Then data["Mean"]  
> would give me the mean, data["Values"] would return me the original  
> values, etc.
>
> Here is an example from LinearModelFit:
>
> 	data = {{0, 1}, {1, 0}, {3, 2}, {5, 4}};
> 	lm = LinearModelFit[data, x, x];
> 	Save["Model.txt",lm];
>
> The saved output is
>
> lm = FittedModel[{"Linear", {0.18644067796610198, 0.6949152542372881},
>       {{x}, {1, x}}, {0, 0}}, {{1., 1., 1., 1.}}, {{0, 1}, {1, 0}, {3,  
> 2},
>       {5, 4}}, {{1., 0.}, {1., 1.}, {1., 3.}, {1., 5.}},
>      Function[Null, Internal`LocalizedBlock[{x}, #1], {HoldAll}]]
>
> Which has, basically, only the data and the fitted parameters.  
> Nonetheless, by doing lm["FitResiduals"] it computes the residuals for  
> me.
>
> In my impression, this is as close as one can get to object-oriented  
> programming in Mathematica, and would be a quite useful feature to use.
> I haven't found any documentation on this. Anyone knowns?
>
> Sincerely,
>
> Gabriel Landi
>

There isn't actually any special trick to this, as the "objects" are just  
ordinary expressions with OwnValues, UpValues, DownValues, SubValues, etc.  
implementing whatever functionality is required. You can create your  
object by assigning these values inside a Module, amongst other  
approaches. Here's a simple example of a FIFO queue object with named  
("tagged") sub-queues:

In[1] :=
TagQueue /: New[TagQueue] :=
   Module[{TagQueue},

(* Define the sequence numbers for the head and tail of each subqueue \
on the first attempt to access their values. This ensures that an \
enqueue/dequeue operation cannot fail due to the lack of these \
definitions and thus avoids the need for a test as to whether a \
subqueue has been initialized before it is used. *)
    TagQueue[tag_]["Head" | "Tail"] :=
     TagQueue[tag]["Head"] = TagQueue[tag]["Tail"] = 1;

(* A new element is enqueued as in a typical FIFO queue by \
incrementing the sequence number of the tail element of the \
corresponding subqueue. A DownValue is then defined for the new item \
using the sequence number of the tail element as it previously was. *)
    TagQueue /: Enqueue[TagQueue[tag_], expr_] :=
     TagQueue[tag, TagQueue[tag]["Tail"]++] := Unevaluated[expr];

(* Dequeuing an element from a subqueue removes the DownValue for the \
dequeued element and increments the sequence number of the head \
element to refer to the next item in the subqueue. *)
    With[{NonEmptyQ = #["Head"] < #["Tail"] &},
     TagQueue /: Dequeue[TagQueue[tag_]?NonEmptyQ] :=
      Block[{val = TagQueue[tag, TagQueue[tag]["Head"]]},
       TagQueue[tag, TagQueue[tag]["Head"]++] =.; val
       ]
     ];

(* If a subqueue is empty (as in default case) then attempts to \
dequeue from it return $Failed. *)
    TagQueue /: Dequeue@TagQueue[tag_] = $Failed;

(* Mechanism for determining the number of elements currently held in \
a given subqueue. *)
    TagQueue /: Length@TagQueue[tag_] :=
     TagQueue[tag]["Tail"] - TagQueue[tag]["Head"];

(* Return the TagQueue (with Temporary attribute due to Module). *)
    TagQueue
    ];

In[2] :=
(* Instantiating the object *)
q = New[TagQueue]

Out[2] =
TagQueue$772

In[3] :=
(* Calling some methods *)
Enqueue[q["items"], "item 1"]

In[4] :=
Dequeue@q["items"]

Out[4] =
"item 1"

In[5] :=
Enqueue[q["numbers"], #] & /@ Range[10];

In[6] :=
Length@q["numbers"]

Out[6] =
10

In[7] :=
Dequeue@q["numbers"]

Out[7] =
1

In[8] :=
Table[Dequeue@q["numbers"], {10}]

Out[8] =
{2, 3, 4, 5, 6, 7, 8, 9, 10, $Failed}

In[9] :=
(* Destroying the object *)
Remove@Evaluate[q]

For more information about object-oriented programming in Mathematica  
(defining classes, etc.), I would recommend Roman Maeder's books: either  
"The Mathematica Programmer", or chapter 14 of "Computer Science with  
Mathematica" (see http://www.mathconsult.ch/showroom/pubs/CSM/). You may  
also like to have a look at this page, where the Classes.m file used in  
these books is available for download along with a (brief) usage example:  
http://library.wolfram.com/infocenter/Articles/3243/.



  • Prev by Date: Re: Difficulty with patterns
  • Next by Date: Re: a bug in Integrate (2nd message)
  • Previous by thread: Re: Creating Objects
  • Next by thread: Re: Creating Objects