# 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 <OP>" unless $#$self == $#$that;
for ($i=0; $i<=$#$self; ++$i) {
$r->[$i] = $self->[$i] <OP> $that->[$i];
}
} else {
for ($i=0; $i<=$#$self; ++$i) {
$r->[$i] = $self->[$i] <OP> $that;
}
}
return $r;
}
};
my (%functab) = (
add => '+',
sbt => '-',
mul => '*',
div => '/',
);
my ($name, $op);
while (($name, $op) = each %functab) {
my ($t) = $functemplate;
$t =~ s/<OP>/$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;
syntax highlighted by Code2HTML, v. 0.9.1