# HG changeset patch # User Robert T. Short # Date 1243185787 25200 # Node ID f8b8ab529913fc3291a563223f13fe26842e97f5 # Parent 80c299c847967200e320fc15e63c206223844706 improve OOP documentation diff -r 80c299c84796 -r f8b8ab529913 ChangeLog --- a/ChangeLog Mon May 25 07:35:19 2009 +0200 +++ b/ChangeLog Sun May 24 10:23:07 2009 -0700 @@ -1,3 +1,7 @@ +2009-05-24 Robert T. Short + + * examples/@FIRfilter: added FIRfilter class example. + 2009-05-22 Marco Atzeri * src/sysdep.cc: Removed CYGWIN_init diff -r 80c299c84796 -r f8b8ab529913 doc/ChangeLog --- a/doc/ChangeLog Mon May 25 07:35:19 2009 +0200 +++ b/doc/ChangeLog Sun May 24 10:23:07 2009 -0700 @@ -1,3 +1,7 @@ +2009-05-24 Robert T. Short + + * interpreter/oop.txi: Added inheritance documentatin + 2009-05-21 Rik * interpreter/Makefile.in, texmf.cnf: Change texi2pdf call to include local diff -r 80c299c84796 -r f8b8ab529913 doc/interpreter/oop.txi --- a/doc/interpreter/oop.txi Mon May 25 07:35:19 2009 +0200 +++ b/doc/interpreter/oop.txi Sun May 24 10:23:07 2009 -0700 @@ -51,6 +51,7 @@ * Manipulating Classes:: * Indexing Objects:: * Overloading Objects:: +* Inheritance and Aggregation:: @end menu @node Creating a Class @@ -151,7 +152,7 @@ @DOCSTRING(methods) @noindent -To enquiry whether a particular method is available to a user class, the +To inquire whether a particular method is available to a user class, the @code{ismethod} function can be used. @DOCSTRING(ismethod) @@ -251,7 +252,7 @@ @node Indexing Objects @section Indexing Objects -Objects in can be indexed with parentheses, either like +Objects can be indexed with parentheses, either like @code{@var{a} (@var{idx})} or like @code{@var{a} @{@var{idx}@}}, or even like @code{@var{a} (@var{idx}).@var{field}}. However, it is up to the user to decide what this indexing actually means. In the case of our polynomial @@ -274,7 +275,7 @@ @DOCSTRING(subsasgn) If you wish to use the @code{end} keyword in subscripted expressions -of an object. Then the user needs to define the @code{end} method for +of an object, then the user needs to define the @code{end} method for the class. @DOCSTRING(end) @@ -476,3 +477,140 @@ "double" class is in fact not necessary. +@node Inheritance and Aggregation +@section Inheritance and Aggregation + +Using classes to build new classes is supported by octave through the +use of both inheritance and aggregation. + +Class inheritance is provided by octave using the @code{class} +function in the class constructor. As in the case of the polynomial +class, the octave programmer will create a struct that contains the +data fields required by the class, and then call the class function to +indicate that an object is to be created from the struct. Creating a +child of an existing object is done by creating an object of the +parent class and providing that object as the third argument of the +class function. + +This is easily demonstrated by example. Suppose the programmer needs +an FIR filter, i.e. a filter with a numerator polynomial but a unity +denominator polynomial. In traditional octave programming, this would +be performed as follows. + +@example +octave:1> x = [some data vector]; +octave:2> n = [some coefficient vector]; +octave:3> y = filter (n, 1, x); +@end example + +The equivalent class could be implemented in a class directory +@@FIRfilter that is on the octave path. The constructor is a file +FIRfilter.m in the class directory. + +@classfile{@@FIRfilter,FIRfilter.m} + +As before, the leading comments provide command-line documentation for +the class constructor. This constructor is very similar to the +polynomial class constructor, except that we pass a polynomial object +as the third argument to the class function, telling octave that the +FIRfilter class will be derived from the polynomial class. Our FIR +filter does not have any data fields, but we must provide a struct to +the @code{class} function. The @code{class} function will add an +element named polynomial to the object struct, so we simply add a +dummy element named polynomial as the first line of the constructor. +This dummy element will be overwritten by the class function. + +Note further that all our examples provide for the case in which no +arguments are supplied. This is important since octave will call the +constructor with no arguments when loading ojects from save files to +determine the inheritance structure. + +A class may be a child of more than one class (see the documentation +for the @code{class} function), and inheritance may be nested. There +is no limitation to the number of parents or the level of nesting +other than memory or other physical issues. + +As before, we need a @code{display} method. A simple example might be + +@classfile{@@FIRfilter,display.m} + +Note that we have used the polynomial field of the struct to display +the filter coefficients. + +Once we have the class constructor and display method, we may create +an object by calling the class constructor. We may also check the +class type and examine the underlying structure. + +@example +octave:1> f=FIRfilter(polynomial([1 1 1]/3)) +f.polynomial = 0.333333 + 0.333333 * X + 0.333333 * X ^ 2 +octave:2> class(f) +ans = FIRfilter +octave:3> isa(f,"FIRfilter") +ans = 1 +octave:4> isa(f,"polynomial") +ans = 1 +octave:5> struct(f) +ans = +@{ +polynomial = 0.333333 + 0.333333 * X + 0.333333 * X ^ 2 +@} +@end example + +We only need to define a method to actually process data with our +filter and our class is usable. It is also useful to provide a means +of changing the data stored in the class. Since the fields in the +underlying struct are private by default, we could provide a mechanism +to access the fields. The @code{subsref} method may be used for both. + +@classfile{@@FIRfilter,subsref.m} + +The "()" case allows us to filter data using the polynomial provided +to the constructor. + +@example +octave:2> f=FIRfilter(polynomial([1 1 1]/3)); +octave:3> x=ones(5,1); +octave:4> y=f(x) +y = + + 0.33333 + 0.66667 + 1.00000 + 1.00000 + 1.00000 +@end example + +The "." case allows us to view the contents of the polynomial field. + +@example +octave:1> f=FIRfilter(polynomial([1 1 1]/3)); +octave:2> f.polynomial +ans = 0.333333 + 0.333333 * X + 0.333333 * X ^ 2 +@end example + +In order to change the contents of the object, we need to define a +@code{subsasgn} method. For example, we may make the polynomial field +publicly writeable. + +@classfile{@@FIRfilter,subsasgn.m} + +So that + +@example +octave:6> f=FIRfilter(); +octave:7> f.polynomial = polynomial([1 2 3]); +f.polynomial = 1 + 2 * X + 3 * X ^ 2 +@end example + + +Defining the FIRfilter class as a child of the polynomial class +implies that and FIRfilter object may be used any place that a +polynomial may be used. This is not a normal use of a filter, so that +aggregation may be a more sensible design approach. In this case, the +polynomial is simply a field in the class structure. A class +constructor for this case might be + +@classfile{@@FIRfilter,FIRfilter_aggregation.m} + +For our example, the remaining class methods remain unchanged. diff -r 80c299c84796 -r f8b8ab529913 examples/@FIRfilter/FIRfilter.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/@FIRfilter/FIRfilter.m Sun May 24 10:23:07 2009 -0700 @@ -0,0 +1,22 @@ +## -*- texinfo -*- +## @deftypefn {Function File} {} FIRfilter () +## @deftypefnx {Function File} {} FIRfilter (@var{p}) +## Creates an FIR filter with polynomial @var{p} as +## coefficient vector. +## +## @end deftypefn + +function f = FIRfilter (p) + + f.polynomial = []; + if (nargin == 0) + p = @polynomial ([1]); + elseif (nargin == 1) + if (!isa (p, "polynomial")) + error ("FIRfilter: expecting polynomial as input argument"); + endif + else + print_usage (); + endif + f = class (f, "FIRfilter", p); +endfunction diff -r 80c299c84796 -r f8b8ab529913 examples/@FIRfilter/FIRfilter_aggregation.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/@FIRfilter/FIRfilter_aggregation.m Sun May 24 10:23:07 2009 -0700 @@ -0,0 +1,23 @@ +## -*- texinfo -*- +## @deftypefn {Function File} {} FIRfilter () +## @deftypefnx {Function File} {} FIRfilter (@var{p}) +## Creates an FIR filter with polynomial @var{p} as +## coefficient vector. +## +## @end deftypefn + +function f = FIRfilter (p) + + if (nargin == 0) + f.polynomial = @polynomial ([1]); + elseif (nargin == 1) + if (isa (p, "polynomial")) + f.polynomial = p; + else + error ("FIRfilter: expecting polynomial as input argument"); + endif + else + print_usage (); + endif + f = class (f, "FIRfilter"); +endfunction diff -r 80c299c84796 -r f8b8ab529913 examples/@FIRfilter/display.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/@FIRfilter/display.m Sun May 24 10:23:07 2009 -0700 @@ -0,0 +1,6 @@ +function display (f) + + display(f.polynomial); + +endfunction + diff -r 80c299c84796 -r f8b8ab529913 examples/@FIRfilter/subsasgn.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/@FIRfilter/subsasgn.m Sun May 24 10:23:07 2009 -0700 @@ -0,0 +1,14 @@ +function out = subsasgn (f, index, val) + switch (index.type) + case "." + fld = index.subs; + if (strcmp (fld, "polynomial")) + out = f; + out.polynomial = val; + else + error ("@FIRfilter/subsref: invalid property \"%s\"", fld); + endif + otherwise + error ("FIRfilter/subsagn: Invalid index type") + endswitch +endfunction diff -r 80c299c84796 -r f8b8ab529913 examples/@FIRfilter/subsref.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/@FIRfilter/subsref.m Sun May 24 10:23:07 2009 -0700 @@ -0,0 +1,16 @@ +function out = subsref (f, x) + switch x.type + case "()" + n = f.polynomial; + out = filter(n.poly, 1, x.subs{1}); + case "." + fld = x.subs; + if (strcmp (fld, "polynomial")) + out = f.polynomial; + else + error ("@FIRfilter/subsref: invalid property \"%s\"", fld); + endif + otherwise + error ("@FIRfilter/subsref: invalid subscript type for FIR filter"); + endswitch +endfunction