I've been doing the Perl Weekly
Challenges. This one dealt with
finding the intersection of two lines and doing worrying things to
innocent variables.
Write a script to find the intersection of two straight lines. The
co-ordinates of the two lines should be provided as command line
parameter. For example:
The two ends of Line 1 are represented as co-ordinates (a,b) and
(c,d).
The two ends of Line 2 are represented as co-ordinates (p,q) and
(r,s).
The script should print the co-ordinates of point of intersection of
the above two lines.
The most interesting bit of this is the special and degenerate cases.
The point of intersection of finite lines must lie within both
lines, or it doesn't really count as an intersection.
Also the formula given on Wikipedia is subtly wrong.
I'm wasting slot 0 in each array, for convenience of transcription.
my @x;
my @y;
$x[1]=shift @ARGV;
$y[1]=shift @ARGV;
$x[2]=shift @ARGV;
$y[2]=shift @ARGV;
$x[3]=shift @ARGV;
$y[3]=shift @ARGV;
$x[4]=shift @ARGV;
$y[4]=shift @ARGV;
We can learn a bit just from the denominator, so calculate that first.
my $d=($x[1]-$x[2])*($y[3]-$y[4]) - ($y[1]-$y[2])*($x[3]-$x[4]);
if ($d==0) {
die "Lines are parallel or coincident.\n";
}
t and u are the relative positions of the intersections along each
segment (t=0 would put the intersection at (x1,y1), t=1 at (x2,y2)) so
this lets us see whether the intersection falls outside the segment.
my $t=( ($x[1]-$x[3])*($y[3]-$y[4]) - ($y[1]-$y[3])*($x[3]-$x[4]) )/$d;
if ($t<0 || $t>1) {
die "No intersection.\n";
}
my $u=( ($y[1]-$y[2])*($x[1]-$x[3]) - ($x[1]-$x[2])*($y[1]-$y[3]) )/$d;
if ($u<0 || $u>1) {
die "No intersection.\n";
}
$x[0]=$x[1]+$t*($x[2]-$x[1]);
$y[0]=$y[1]+$t*($y[2]-$y[1]);
print "$x[0] $y[0]\n";
The perl6 version is basically the same except for minor syntax changes.
The other challenge was a bit more surprisingā¦
Write a script that allows you to capture/display historical data.
It could be an object or a scalar. For example
my $x = 10; $x = 20; $x -= 5;
It turns out there's a perl module that lets you arbitrarily tie stuff
to variables. This is frankly terrifying in its potential for action
at a distance, and I would think very carefully about using it in
production codeā¦
use Variable::Magic qw(wizard cast getdata);
use Storable qw(dclone);
my @history;
my $wiz=wizard(
set => sub { if (ref ${$_[0]}) {push @history,dclone(${$_[0]})} else {push @history,${$_[0]}} },
);
cast my ($x),$wiz;
$x=10;
$x+=5;
$x=[20];
# push @{$x},25; # this doesn't work
$x={a => 30};
# $x->{b}=30; # this doesn't work either
use Data::Dumper;
print Dumper(\@history);
This almost works. Modifying a reference by means other than simply
assigning a completely new value doesn't get caught. If the variable
didn't have to be generic, one could apply it directly to a hash or list:
cast my (%x),$wiz;
with appropriate modifications to store into the history array, but I
can't see a way to generalise this into working on an arbitrary object.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.