ption is set, and not via C command.
If one attempts to print this value, then the overloaded operator
C<""> will be called, which will call C operator. The
result of this operator will be stringified again, but this result is
again of type C, which will lead to an infinite loop.
Add a pretty-printer method to the module F:
sub pretty {
my ($meth, $a, $b) = @{+shift};
$a = 'u' unless defined $a;
$b = 'u' unless defined $b;
$a = $a->pretty if ref $a;
$b = $b->pretty if ref $b;
"[$meth $a $b]";
}
Now one can finish the script by
print "side = ", $side->pretty, "\n";
The method C is doing object-to-string conversion, so it
is natural to overload the operator C<""> using this method. However,
inside such a method it is not necessary to pretty-print the
I $a and $b of an object. In the above subroutine
C<"[$meth $a $b]"> is a catenation of some strings and components $a
and $b. If these components use overloading, the catenation operator
will look for an overloaded operator C<.>; if not present, it will
look for an overloaded operator C<"">. Thus it is enough to use
use overload nomethod => \&wrap, '""' => \&str;
sub str {
my ($meth, $a, $b) = @{+shift};
$a = 'u' unless defined $a;
$b = 'u' unless defined $b;
"[$meth $a $b]";
}
Now one can change the last line of the script to
print "side = $side\n";
which outputs
side = [/ [- [sqrt [+ 1 [** [n 1 u] 2]] u] 1] [n 1 u]]
and one can inspect the value in debugger using all the possible
methods.
Something is still amiss: consider the loop variable $cnt of the
script. It was a number, not an object. We cannot make this value of
type C, since then the loop will not terminate.
Indeed, to terminate the cycle, the $cnt should become false.
However, the operator C for checking falsity is overloaded (this
time via overloaded C<"">), and returns a long string, thus any object
of type C is true. To overcome this, we need a way to
compare an object to 0. In fact, it is easier to write a numeric
conversion routine.
Here is the text of F with such a routine added (and
slightly modified str()):
package symbolic; # Primitive symbolic calculator
use overload
nomethod => \&wrap, '""' => \&str, '0+' => \#
sub new { shift; bless ['n', @_] }
sub wrap {
my ($obj, $other, $inv, $meth) = @_;
($obj, $other) = ($other, $obj) if $inv;
bless [$meth, $obj, $other];
}
sub str {
my ($meth, $a, $b) = @{+shift};
$a = 'u' unless defined $a;
if (defined $b) {
"[$meth $a $b]";
} else {
"[$meth $a]";
}
}
my %subr = ( n => sub {$_[0]},
sqrt => sub {sqrt $_[0]},
'-' => sub {shift() - shift()},
'+' => sub {shift() + shift()},
'/' => sub {shift() / shift()},
'*' => sub {shift() * shift()},
'**' => sub {shift() ** shift()},
);
sub num {
my ($meth, $a, $b) = @{+shift};
my $subr = $subr{$meth}
or die "Do not know how to ($meth) in symbolic";
$a = $a->num if ref $a eq __PACKAGE__;
$b = $b->num if ref $b eq __PACKAGE__;
$subr->($a,$b);
}
All the work of numeric conversion is done in %subr and num(). Of
course, %subr is not complete, it contains only operators used in the
example below. Here is the extra-credit question: why do we need an
explicit recursion in num()? (Answer is at the end of this section.)
Use this module like this:
require symbolic;
my $iter = symbolic->new(2); # 16-gon
my $side = symbolic->new(1);
my $cnt = $iter;
while ($cnt) {
$cnt = $cnt - 1; # Mutator '--' not implemented
$side = (sqrt(1 + $side**2) - 1)/$side;
}
printf "%s=%f\n", $side, $side;
printf "pi=%f\n", $side*(2**($iter+2));
It prints (without so many line breaks)
[/ [- [sqrt [+ 1 [** [/ [- [sqrt [+ 1 [** [n 1] 2]]] 1]
[n 1]] 2]]] 1]
[/ [- [sqrt [+ 1 [** [n 1] 2]]] 1] [n 1]]]=0.198912
pi=3.182598
The above module is very primitive. It does not implement
mutator methods (C<++>, C<-=> and so on), does not do deep copying
(not required without mutators!), and implements only those arithmetic
operations which are used in the example.
To implement most arithmetic operations is easy; one should just use
the tables of operations, and change the code which fills %subr to
my %subr = ( 'n' => sub {$_[0]} );
foreach my $op (split " ", $overload::ops{with_assign}) {
$subr{$op} = $subr{"$op="} = eval "sub {shift() $op shift()}";
}
my @bins = qw(binary 3way_comparison num_comparison str_comparison);
foreach my $op (split " ", "@overload::ops{ @bins }") {
$subr{$op} = eval "sub {shift() $op shift()}";
}
foreach my $op (split " ", "@overload::ops{qw(unary func)}") {
print "defining '$op'\n";
$subr{$op} = eval "sub {$op shift()}";
}
Since subroutines implementing assignment operators are not required
to modify their operands (see L above),
we do not need anything special to make C<+=> and friends work,
besides adding these operators to %subr and defining a copy
constructor (needed since Perl has no way to know that the
implementation of C<'+='> does not mutate the argument -
see L).
To implement a copy constructor, add C<< '=' => \&cpy >> to C