📄 overload.3
字号:
\& sub pretty {\& my ($meth, $a, $b) = @{+shift};\& $a = \*(Aqu\*(Aq unless defined $a;\& $b = \*(Aqu\*(Aq unless defined $b;\& $a = $a\->pretty if ref $a;\& $b = $b\->pretty if ref $b;\& "[$meth $a $b]";\& }.Ve.PPNow one can finish the script by.PP.Vb 1\& print "side = ", $side\->pretty, "\en";.Ve.PPThe method \f(CW\*(C`pretty\*(C'\fR is doing object-to-string conversion, so itis natural to overload the operator \f(CW""\fR using this method. However,inside such a method it is not necessary to pretty-print the\&\fIcomponents\fR \f(CW$a\fR and \f(CW$b\fR of an object. In the above subroutine\&\f(CW"[$meth $a $b]"\fR is a catenation of some strings and components \f(CW$a\fRand \f(CW$b\fR. If these components use overloading, the catenation operatorwill look for an overloaded operator \f(CW\*(C`.\*(C'\fR; if not present, it willlook for an overloaded operator \f(CW""\fR. Thus it is enough to use.PP.Vb 7\& use overload nomethod => \e&wrap, \*(Aq""\*(Aq => \e&str;\& sub str {\& my ($meth, $a, $b) = @{+shift};\& $a = \*(Aqu\*(Aq unless defined $a;\& $b = \*(Aqu\*(Aq unless defined $b;\& "[$meth $a $b]";\& }.Ve.PPNow one can change the last line of the script to.PP.Vb 1\& print "side = $side\en";.Ve.PPwhich outputs.PP.Vb 1\& side = [/ [\- [sqrt [+ 1 [** [n 1 u] 2]] u] 1] [n 1 u]].Ve.PPand one can inspect the value in debugger using all the possiblemethods..PPSomething is still amiss: consider the loop variable \f(CW$cnt\fR of thescript. It was a number, not an object. We cannot make this value oftype \f(CW\*(C`symbolic\*(C'\fR, since then the loop will not terminate..PPIndeed, to terminate the cycle, the \f(CW$cnt\fR should become false.However, the operator \f(CW\*(C`bool\*(C'\fR for checking falsity is overloaded (thistime via overloaded \f(CW""\fR), and returns a long string, thus any objectof type \f(CW\*(C`symbolic\*(C'\fR is true. To overcome this, we need a way tocompare an object to 0. In fact, it is easier to write a numericconversion routine..PPHere is the text of \fIsymbolic.pm\fR with such a routine added (andslightly modified \fIstr()\fR):.PP.Vb 3\& package symbolic; # Primitive symbolic calculator\& use overload\& nomethod => \e&wrap, \*(Aq""\*(Aq => \e&str, \*(Aq0+\*(Aq => \e#\&\& sub new { shift; bless [\*(Aqn\*(Aq, @_] }\& 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 = \*(Aqu\*(Aq unless defined $a;\& if (defined $b) {\& "[$meth $a $b]";\& } else {\& "[$meth $a]";\& }\& }\& my %subr = ( n => sub {$_[0]},\& sqrt => sub {sqrt $_[0]},\& \*(Aq\-\*(Aq => sub {shift() \- shift()},\& \*(Aq+\*(Aq => sub {shift() + shift()},\& \*(Aq/\*(Aq => sub {shift() / shift()},\& \*(Aq*\*(Aq => sub {shift() * shift()},\& \*(Aq**\*(Aq => 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);\& }.Ve.PPAll the work of numeric conversion is done in \f(CW%subr\fR and \fInum()\fR. Ofcourse, \f(CW%subr\fR is not complete, it contains only operators used in theexample below. Here is the extra-credit question: why do we need anexplicit recursion in \fInum()\fR? (Answer is at the end of this section.).PPUse this module like this:.PP.Vb 4\& require symbolic;\& my $iter = new symbolic 2; # 16\-gon\& my $side = new symbolic 1;\& my $cnt = $iter;\&\& while ($cnt) {\& $cnt = $cnt \- 1; # Mutator \`\-\-\*(Aq not implemented\& $side = (sqrt(1 + $side**2) \- 1)/$side;\& }\& printf "%s=%f\en", $side, $side;\& printf "pi=%f\en", $side*(2**($iter+2));.Ve.PPIt prints (without so many line breaks).PP.Vb 4\& [/ [\- [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.Ve.PPThe above module is very primitive. It does not implementmutator methods (\f(CW\*(C`++\*(C'\fR, \f(CW\*(C`\-=\*(C'\fR and so on), does not do deep copying(not required without mutators!), and implements only those arithmeticoperations which are used in the example..PPTo implement most arithmetic operations is easy; one should just usethe tables of operations, and change the code which fills \f(CW%subr\fR to.PP.Vb 12\& my %subr = ( \*(Aqn\*(Aq => 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\*(Aq\en";\& $subr{$op} = eval "sub {$op shift()}";\& }.Ve.PPDue to \*(L"Calling Conventions for Mutators\*(R", we do not need anythingspecial to make \f(CW\*(C`+=\*(C'\fR and friends work, except filling \f(CW\*(C`+=\*(C'\fR entry of\&\f(CW%subr\fR, and defining a copy constructor (needed since Perl has noway to know that the implementation of \f(CW\*(Aq+=\*(Aq\fR does not mutatethe argument, compare \*(L"Copy Constructor\*(R")..PPTo implement a copy constructor, add \f(CW\*(C`\*(Aq=\*(Aq => \e&cpy\*(C'\fR to \f(CW\*(C`use overload\*(C'\fRline, and code (this code assumes that mutators change things one leveldeep only, so recursive copying is not needed):.PP.Vb 4\& sub cpy {\& my $self = shift;\& bless [@$self], ref $self;\& }.Ve.PPTo make \f(CW\*(C`++\*(C'\fR and \f(CW\*(C`\-\-\*(C'\fR work, we need to implement actual mutators,either directly, or in \f(CW\*(C`nomethod\*(C'\fR. We continue to do things inside\&\f(CW\*(C`nomethod\*(C'\fR, thus add.PP.Vb 4\& if ($meth eq \*(Aq++\*(Aq or $meth eq \*(Aq\-\-\*(Aq) {\& @$obj = ($meth, (bless [@$obj]), 1); # Avoid circular reference\& return $obj;\& }.Ve.PPafter the first line of \fIwrap()\fR. This is not a most effectiveimplementation, one may consider.PP.Vb 1\& sub inc { $_[0] = bless [\*(Aq++\*(Aq, shift, 1]; }.Ve.PPinstead..PPAs a final remark, note that one can fill \f(CW%subr\fR by.PP.Vb 10\& my %subr = ( \*(Aqn\*(Aq => 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)}") {\& $subr{$op} = eval "sub {$op shift()}";\& }\& $subr{\*(Aq++\*(Aq} = $subr{\*(Aq+\*(Aq};\& $subr{\*(Aq\-\-\*(Aq} = $subr{\*(Aq\-\*(Aq};.Ve.PPThis finishes implementation of a primitive symbolic calculator in50 lines of Perl code. Since the numeric values of subexpressionsare not cached, the calculator is very slow..PPHere is the answer for the exercise: In the case of \fIstr()\fR, we need noexplicit recursion since the overloaded \f(CW\*(C`.\*(C'\fR\-operator will fall backto an existing overloaded operator \f(CW""\fR. Overloaded arithmeticoperators \fIdo not\fR fall back to numeric conversion if \f(CW\*(C`fallback\*(C'\fR isnot explicitly requested. Thus without an explicit recursion \fInum()\fRwould convert \f(CW\*(C`[\*(Aq+\*(Aq, $a, $b]\*(C'\fR to \f(CW\*(C`$a + $b\*(C'\fR, which would just rebuildthe argument of \fInum()\fR..PPIf you wonder why defaults for conversion are different for \fIstr()\fR and\&\fInum()\fR, note how easy it was to write the symbolic calculator. Thissimplicity is due to an appropriate choice of defaults. One extranote: due to the explicit recursion \fInum()\fR is more fragile than \fIsym()\fR:we need to explicitly check for the type of \f(CW$a\fR and \f(CW$b\fR. If components\&\f(CW$a\fR and \f(CW$b\fR happen to be of some related type, this may lead to problems..Sh "\fIReally\fP symbolic calculator".IX Subsection "Really symbolic calculator"One may wonder why we call the above calculator symbolic. The reasonis that the actual calculation of the value of expression is postponeduntil the value is \fIused\fR..PPTo see it in action, add a method.PP.Vb 5\& sub STORE {\& my $obj = shift;\& $#$obj = 1;\& @$obj\->[0,1] = (\*(Aq=\*(Aq, shift);\& }.Ve.PPto the package \f(CW\*(C`symbolic\*(C'\fR. After this change one can do.PP.Vb 3\& my $a = new symbolic 3;\& my $b = new symbolic 4;\& my $c = sqrt($a**2 + $b**2);.Ve.PPand the numeric value of \f(CW$c\fR becomes 5. However, after calling.PP.Vb 1\& $a\->STORE(12); $b\->STORE(5);.Ve.PPthe numeric value of \f(CW$c\fR becomes 13. There is no doubt now that the modulesymbolic provides a \fIsymbolic\fR calculator indeed..PPTo hide the rough edges under the hood, provide a \fItie()\fRd interface to thepackage \f(CW\*(C`symbolic\*(C'\fR (compare with \*(L"Metaphor clash\*(R"). Add methods.PP.Vb 3\& sub TIESCALAR { my $pack = shift; $pack\->new(@_) }\& sub FETCH { shift }\& sub nop { } # Around a bug.Ve.PP(the bug is described in \*(L"\s-1BUGS\s0\*(R"). One can use this new interface as.PP.Vb 3\& tie $a, \*(Aqsymbolic\*(Aq, 3;\& tie $b, \*(Aqsymbolic\*(Aq, 4;\& $a\->nop; $b\->nop; # Around a bug\&\& my $c = sqrt($a**2 + $b**2);.Ve.PPNow numeric value of \f(CW$c\fR is 5. After \f(CW\*(C`$a = 12; $b = 5\*(C'\fR the numeric valueof \f(CW$c\fR becomes 13. To insulate the user of the module add a method.PP.Vb 1\& sub vars { my $p = shift; tie($_, $p), $_\->nop foreach @_; }.Ve.PPNow.PP.Vb 3\& my ($a, $b);\& symbolic\->vars($a, $b);\& my $c = sqrt($a**2 + $b**2);\&\& $a = 3; $b = 4;\& printf "c5 %s=%f\en", $c, $c;\&\& $a = 12; $b = 5;\& printf "c13 %s=%f\en", $c, $c;.Ve.PPshows that the numeric value of \f(CW$c\fR follows changes to the values of \f(CW$a\fRand \f(CW$b\fR..SH "AUTHOR".IX Header "AUTHOR"Ilya Zakharevich <\fIilya@math.mps.ohio\-state.edu\fR>..SH "DIAGNOSTICS".IX Header "DIAGNOSTICS"When Perl is run with the \fB\-Do\fR switch or its equivalent, overloadinginduces diagnostic messages..PPUsing the \f(CW\*(C`m\*(C'\fR command of Perl debugger (see perldebug) one candeduce which operations are overloaded (and which ancestor triggersthis overloading). Say, if \f(CW\*(C`eq\*(C'\fR is overloaded, then the method \f(CW\*(C`(eq\*(C'\fRis shown by debugger. The method \f(CW\*(C`()\*(C'\fR corresponds to the \f(CW\*(C`fallback\*(C'\fRkey (in fact a presence of this method shows that this package hasoverloading enabled, and it is what is used by the \f(CW\*(C`Overloaded\*(C'\fRfunction of module \f(CW\*(C`overload\*(C'\fR)..PPThe module might issue the following warnings:.IP "Odd number of arguments for overload::constant" 4.IX Item "Odd number of arguments for overload::constant"(W) The call to overload::constant contained an odd number of arguments.The arguments should come in pairs..IP "`%s' is not an overloadable type" 4.IX Item "`%s' is not an overloadable type"(W) You tried to overload a constant type the overload package is unaware of..IP "`%s' is not a code reference" 4.IX Item "`%s' is not a code reference"(W) The second (fourth, sixth, ...) argument of overload::constant needsto be a code reference. Either an anonymous subroutine, or a referenceto a subroutine..SH "BUGS".IX Header "BUGS"Because it is used for overloading, the per-package hash \f(CW%OVERLOAD\fR nowhas a special meaning in Perl. The symbol table is filled with nameslooking like line-noise..PPFor the purpose of inheritance every overloaded package behaves as if\&\f(CW\*(C`fallback\*(C'\fR is present (possibly undefined). This may createinteresting effects if some package is not overloaded, but inheritsfrom two overloaded packages..PPRelation between overloading and \fItie()\fRing is broken. Overloading istriggered or not basing on the \fIprevious\fR class of \fItie()\fRd value..PPThis happens because the presence of overloading is checked too early,before any \fItie()\fRd access is attempted. If the \s-1\fIFETCH\s0()\fRed class of the\&\fItie()\fRd value does not change, a simple workaround is to access the valueimmediately after \fItie()\fRing, so that after this call the \fIprevious\fR classcoincides with the current one..PP\&\fBNeeded:\fR a way to fix this without a speed penalty..PPBarewords are not covered by overloaded string constants..PPThis document is confusing. There are grammos and misleading languageused in places. It would seem a total rewrite is needed.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -