# Author: Chao-Kuei Hung # For more info, including license, please see doc/index.html package Vector; # Mathematical Vector use strict; use Carp; use vars qw(@ISA); @ISA = qw(); my (%generated); BEGIN { my ($functemplate) = q{ sub { my ($self, $that) = @_; my ($r) = bless [], ref($self); my ($i); if (ref $that) { croak "dimension mismatch (", $#$self+1, " vs ", $#$that+1, ") in " unless $#$self == $#$that; for ($i=0; $i<=$#$self; ++$i) { $r->[$i] = $self->[$i] $that->[$i]; } } else { for ($i=0; $i<=$#$self; ++$i) { $r->[$i] = $self->[$i] $that; } } return $r; } }; my (%functab) = ( add => '+', sbt => '-', mul => '*', div => '/', ); my ($name, $op); while (($name, $op) = each %functab) { my ($t) = $functemplate; $t =~ s//$op/g; $generated{$name} = eval $t; } } # see perldoc overload, especially the "MAGIC AUTOGENERATION" section use overload '=' => '_clone', '""' => 'stringify', '+' => $generated{add}, '-' => $generated{sbt}, 'neg'=> 'negate', '*' => $generated{mul}, '/' => $generated{div}, 'fallback' => undef ; sub pw_mul { return $generated{mul}->(@_); } sub pw_div { return $generated{div}->(@_); } # Different from "Perl Cookbook", chap 13.6, p.461 "cloning objects" # See Randal Schwartz's "Constructing Objects" at # http://www.stonehenge.com/merlyn/UnixReview/col52.html # (search for "three camps") sub new { my ($proto, @data) = @_; my ($class) = ref $proto || $proto; # if (ref $data[0] eq "Vector") { if (ref $proto) { return bless [ @$proto ], $class; } else { return bless [@data], $class; } } # Copy constructor is very tricky. It is _not_ called until # just before a mutator is applied to one of the reference # variables sharing the same copy. See perldoc overload, # especially the "Copy Constructor" section. sub _clone { my ($a, $b, $switch) = @_; print STDERR "Vector::_clone : switch is undef!\n" unless defined $switch; # print STDERR $switch ? "+" : "-"; # always prints "-" return $switch ? bless([@$a],"Vector") : bless([@$b],"Vector"); } sub stringify { my ($self) = @_; my ($r) = sprintf "[ %8g", $self->[0]; foreach (@{$self}[1..$#$self]) { $r .= sprintf(", %8g", $_); } return $r . " ]"; } sub negate { my ($self) = @_; return bless [map { -$_ } @$self], ref $self; } sub x { return $_[0]->[0]; } sub y { return $_[0]->[1]; } sub z { return $_[0]->[2]; } sub dot { # dot product my ($t) = $_[0]->pw_mul($_[1]); my ($s, $i); for ($i=0; $i<=$#$t; ++$i) { $s += $t->[$i]; } return $s; } sub norm { my ($self) = @_; return sqrt($self->dot($self)); } sub angle_cos { my ($self, $b) = @_; return $self->dot($b)/$self->norm()/$b->norm(); } sub cob { # change of basis my ($self, $b) = @_; die unless ($#$b == $#$self and $#$b == $#{$b->[0]}); my ($r) = $self->new(); map { $_ = 0; } @$r; my ($i); for ($i=0; $i<=$#$self; ++$i) { $r += $b->[$i]->pw_mul($self->[$i]); } return $r; } if ($0 =~ /Vector.pm$/) { # being tested as a stand-alone program, so run test code. my ($p, $q, $r); $p = Vector->new(4,-3); $q = Vector->new(5,12); print $p+$q, ",", $p-$q, "\n"; $r = $p; $r += $q; $q = $q->pw_div(2); print $p, ",", $q, ",", $r, ",", $p->pw_mul(3), ",", -$p, "\n"; } 1;