http://www.perlmonks.org?node_id=369010


in reply to Surviving 'Illegal division by zero'

use strict; use warnings; { package MyNumber; use overload '0+' => \&numify, '/' => \&division; use Scalar::Util; my %numbers; BEGIN {*MyNumber::__ = \&Scalar::Util::refaddr} sub DESTROY {delete $numbers {__ shift}} sub new {my $f; bless \$f => shift} sub set {$numbers {__ $_ [0]} = $_ [1]; $_ [0]} sub numify {$numbers {__ $_ [0]}} sub division {my $f = $numbers {__ $_ [0]}; my $s = ref ($_ [1]) =~ /MyNumber/ ? $numbers {__ $_ + [1]} : $_ [1]; ($f, $s) = ($s, $f) if $_ [2]; $s ? $f / $s : undef} } my $fig_1 = MyNumber -> new -> set (get_numeric_value_from_xml (...)) +; my $fig_2 = MyNumber -> new -> set (get_numeric_value_from_xml (...)) +; my $growth = 100 * $fig_1 / $fig_2 - 100;

Abigail

Replies are listed 'Best First'.
Re^2: Surviving 'Illegal division by zero'
by ViceRaid (Chaplain) on Jun 23, 2004 at 12:10 UTC

    ++ Thanks, that's a really interesting answer, though I think a simple function (see above) might be easier for whoever maintains my library to understand at first glance ;). It's also a shame that it isn't possible to directly re-open the method definitions of '/' and '+' for numeric scalars in Perl.

    As an aside, does this solution using numeric classes make anyone else pine for them in Perl (Float, Integer, Complex ...)?

      This will make your code easier to understand. And, this is the way to "directly re-open the method definitions". In fact, it's safer to do it this way than it is to redefine it for the whole program. That kind of "action-at-a-distance" is the source of more maintenance nightmares than anything else.

      Another way to look at it is that you can now parse, format, and handle anything to do with numbers in one place. If you want to change how you deal with numeric values, simply change the class. There's your global change, but it's nicely encapsulated.

      A last concept to leave you with - if someone were to take your code and wrap it in something else, which is the politer way to handle things?

      ------
      We are the carpenters and bricklayers of the Information Age.

      Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      I shouldn't have to say this, but any code, unless otherwise stated, is untested

        And, this is the way to "directly re-open the method definitions". In fact, it's safer to do it this way than it is to redefine it for the whole program. That kind of "action-at-a-distance" is the source of more maintenance nightmares than anything else.

        Totally agree. Before Abigail's post I hadn't really thought of it in OO (obj-oriented and op-overloading) terms. To put what you're saying in, uh, ahem, pseudocode, it's the difference between globally redefining a core method

        class Float alias :old_divide :/ def / (other) other == 0 ? nil : old_divide(other) end end

        and subclassing, which is all good

        class MyFloat < Float def MyFloat.new(from) from.to_f() end def / (other) other == 0 ? nil : super(other) end end MyFloat.new(4) / 0 # nil
        A last concept to leave you with - if someone were to take your code and wrap it in something else, which is the politer way to handle things?

        Sure, it's a library. I was wondering if there was something scoped lexically analogous to:

        use warnings; { no warnings qw/once/; $foo = 5 - $bar; } { no warnings qw/uninitialized/; $foo = 5 - $qux; }

        Thanks

        I agree, especially from the viewpoint of maintainability. Better to handle such situations (there may be cases other than division by zero which need special attention) in one encapsulated place. The other choices are to dig through all the code, restructuring where needed to handle the exceptional case, or to try to capture the exception (which tells you when the problem has happened rather than letting you avoid it).
Re^2: Surviving 'Illegal division by zero'
by dragonchild (Archbishop) on Jun 24, 2004 at 12:08 UTC
    What about the following syntactic sugar?
    sub new {my $f; bless \$f => shift; $f -> set(@_) if @_; $f}
    That would allow the following modification:
    my $fig_1 = MyNumber -> new (get_numeric_value_from_xml (...));

    I only propose it because most constructors also allow for values to be passed in, which keeps to the Principle of Least Surprise.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      I only propose it because most constructors also allow for values to be passed in, which keeps to the Principle of Least Surprise.

      I don't like constructors that are also intializers - constructors that are initializers make MI harder than it should be. I do have some coding guidelines (posted somewhere on this site), and there I explain I don't initialize objects from the constructor for exactly that reason - making MI harder (of course, if your object is anything other than "just a blessed reference", MI is doomed anyway).

      And sure allowing arguments to the constructor, and having the constructor call the initializer if arguments are given works, but it makes the constructor less simple, and hence, less elegant.

      The "everyone else does it" argument doesn't work for me, or else I had used blessed hashrefs for objects instead of just a blessed reference.

      Abigail