Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

dokkeldepper's scratchpad

by dokkeldepper (Friar)
on Dec 07, 2005 at 13:35 UTC ( [id://514832] : scratchpad . print w/replies, xml ) Need Help??

Adding Regression diagnostics and more tests to Statistics::OLS

Writing Inverters for Math::Business::BlackScholes

Writing Module for better Order statistics and Tests (say Statistics::Ranking )

Writing Module for Fisher LDA (say Statistics::FisherLDA)

Doing Statistics and Data Mining with perl...

Currently rethougt: my age old pluggable-perl-dataminer sketch (pSteiger)

Parsing SPSS Syntax in Perl

Parsing SAS Macros in Perl

Work in progress

An overloaded, planar geometry meditation on points and locations

In the beginning...

This is what perlmonkey left out of the tutorial

Currently, I played around to make an interactive, programmable display for higher dimensional datasets. Doing this, I stumbled over an elementary problem generating a usable class hierarchy for points/locations in high(>1)-dimensional spaces: How can I compare the location of two or more different points? How can I overload the ususal comparison operators correctly? Moreover, how can I find out if a point is situated between (whatever this means) two other points? Although there is a bunch of computational geometry literature around, these simple questions are not discussed. And even if this topic is covered, nothing is said on a sensible OO-implementation. Consquently, I started My Own Thing. To make things easy I begun with the plane, i.e. 2 dimensions.

... a new Location

A location (point) on the plain consists of a pair (X,Y) of coordinates. Because I deal with raster images only integers are uses. Thats why I decided to implement a location a a two-dimensional array.

Selfish as I am I use some constructs that are ... hmmm ... not really recomended. See the $AUTOLOAD thing for example.

package Location; use 5.008001; use strict; use vars '$AUTOLOAD'; use warnings; use Carp; use UNIVERSAL qw/ isa /; use integer; #we use integer coordinates use constant nl=>"\n"; use constant X=>0; # these are the array fields use constant Y=>1;

For the ease of use I need some sensible defaults. Thse are implemented as class data.

# defaults { my $default_location=[ 0, 0 ]; sub get_default_location{ return [$default_location->[X],$default_location->[Y]]; } sub get_default_X{ return $default_location->[X]; } sub get_default_Y{ return $default_location->[Y]; } }

And now the constructor...

sub new{ use integer; my ($self,$x,$y)=@_; # if $x is an arrayref use this if (isa($x,'ARRAY')){ return bless $x,$self; } # otherwise try the parameters or use defaults return bless[ $x||get_default_X, $y||get_default_Y ],$self; }

... then the getters ...

sub get_location{ my $self=shift; return [$self->[X],$self->[Y]]; } sub get_X{ my $self=shift; return $self->get_location->[X]; } sub get_Y{ my $self=shift; return $self->get_location->[Y]; }

... and setters.

sub set_location{ my ($self,$x,$y)=@_; if(isa($x,"PLocation")){ $self->[X]=$x->get_X; $self->[Y]=$x->get_Y; } elsif(defined $x and defined $y){ $self->[X]=$x; $self->[Y]=$y; } else { croak "Insufficient Arguments: set_location(PLocation->new($x,$ +y)) or set_location($x,$y)".nl; } } sub set_X{ my ($self,$x)=@_; $self->[X]=$x; } sub set_Y{ my ($self,$y)=@_; $self->[Y]=$y; } # so far so nice 1;

The basic things are done. Did you do some testing? Yes? Fine, we can continue then. Let's start to mangle all the points a bit.

Comparing points

Dealing with points basically comes down to the question: How are the points situated with respect to each other? If we have two points we might want ot know if they are identical, if they are different and if so, where we have to go from one point to the other.

Lets behave like an idiot and start coding before thinking. Of course, nobody really works like this, do we?

Because we have a class whith possibly comparable objects we can use the overload module.

To do this we have to change the modules header:

use integer; #we use integer coordinates use constant nl=>"\n"; use constant X=>0; # these are the array fields use constant Y=>1; # overload => thats new ! use overloead '<=>' => 'compare';

Now we have to write a sub handles the comparison. In other words, we need an overload handler.

By the way, have your read this carefully? I wrote "sub" not "method". Remember, that in a method the first parameter ($_[0]) is the class (=package) name. Remember this!

sub compare { my ($loc1,$loc2,$rev)=@_; croak "Cant do this with autoloaded comparison\n" if $rev; if($loc1->get_X < $loc2->get_X and $loc1->get_Y< $loc2->get_Y){ return 1; } elsif($loc1->get_X > $loc2->get_X and $loc1->get_Y> $loc2->get_Y ){ return -1; } elsif($loc1->get_X = $loc2->get_X and $loc1->get_Y= $loc2->get_Y ){ return 0; return ; }

The sub compare is an overload handler which is called with three arguments. The first two are the locations to compare, the third indicates if the order of the arguments was swapped for technical reasons. If this was the case something incomparable was tried, we croak then.

To make the spaceship (<=>) work here the handler has to return the values -1 if the left location is smaller than the right one, 0 if they are equal and 1 if the left operand ist larger than the right operator.

Now we try this: