mgatto has asked for the wisdom of the Perl Monks concerning the following question:

Working through Project Euler, I return the result as an array of bigints, but using it as an argument for max(), map() or anything from List::Util does not work. Here's the code:

# big_factors() is my sub finding all the primes of a number. my @result = big_factors(600851475143); say max @result;

I get this error, but Google has nothing for me:

Can't call method "can" on unblessed reference at C:/Languages/Perl/perl/lib/Math/BigInt.pm line 2693.

Using Data::Dumper shows me the actual integers, though. I'm at a loss: how can I operate on arrays of Bigints in Perl?

Edit 1: I'm using Strawberry Perl 5.14.2 (Windows, naturally).

Edit 2: Some interesting differences in output methods:

say "@result"; ARRAY(0x3faefc) 71 839 1471 6857 say $result[1]->numify; 71

This is indeed true.

print Dumper \@result;
$VAR1 = [ [], bless( { 'value' => [ 71 ], 'sign' => '+' }, 'Math::BigInt' ), bless( { 'value' => [ 839 ], 'sign' => '+' }, 'Math::BigInt' ), bless( { 'value' => [ 1471 ], 'sign' => '+' }, 'Math::BigInt' ), bless( { 'value' => [ 6857 ], 'sign' => '+' }, 'Math::BigInt' ) ];

Replies are listed 'Best First'.
Re: List::Util can't find max in array of BigInts
by Athanasius (Archbishop) on Apr 24, 2013 at 03:06 UTC

    Hello mgatto, and welcome to the Monastery!

    From the information given, I cannot reproduce your problem:

    #! perl use strict; use warnings; use bigint; use List::Util qw( max ); use Data::Dumper; my @result = prime_factors(600_851_475_143); #print Dumper \@result; print max @result; sub prime_factors { my ($number) = @_; my $divisor = 2; my @factors; while ($number > 1) { if (($number % $divisor) == 0) { push @factors, $divisor; $number /= $divisor; } elsif ($divisor == 2) { ++$divisor; } else { $divisor += 2; } } return @factors; }

    Output:

    12:58 >perl 609_SoPW.pl 6857 13:00 >

    However, the problem you are seeing may be related to the problem I experienced recently with bignum and the range operator: see Strange interaction of bigint and foreach.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks for the welcome, Athanasius. I like what you did with the number separators (600_851_475_143).

      #print Dumper \@result;

      I'm curious why this dumps the reference to @result. Is there an advantage to this way of doing it?

      Lastly, my run at the problem resulted in this code:

      use Modern::Perl '2012'; # "All operators (including basic math operations) are overloaded. use bigint lib => 'Calc, GMP'; use Regexp::Common; use Data::Dumper; use List::Util qw/ max /; use Carp qw/ confess /; $SIG{__DIE__} = sub { confess @_; }; sub big_factors { my $n = shift; my @factors = [ ]; my $d = 2; # ensure $n is an integer if ($n !~ /$RE{num}{real}/) { die "Argument was not a real number."; } while ($n > 1) { while ( 0 == $n % $d) { push @factors, $d; $n /= $d; } $d += 1; if ($d**2 > $n) { if ($n > 1) { push @factors, $n; last; } } } return @factors; } my @result = big_factors(600851475143); say max @result;
        I'm curious why this dumps the reference to @result.

        Consider:

        22:45 >perl -MData::Dumper -wE "my @c = ( 5, 7, 11 ); say Dumper @c; s +ay Dumper \@c;" $VAR1 = 5; $VAR2 = 7; $VAR3 = 11; $VAR1 = [ 5, 7, 11 ]; 22:46 >

        I prefer the second form, because I can more easily see the related things grouped together (but YMMV).

        my run at the problem resulted in this code:

        And now the problem can be diagnosed! It’s in this line:

        my @factors = [ ];

        The square brackets create an empty, anonymous array, and a reference to this anonymous array becomes the first element in the named array @factors. Later, when List::Util::max is called on @factors, it tries to compare the elements in the array to one another, but fails because it doesn’t know how to compare an array reference (that first element) to a Math::BigInt object.

        The solution is to simply remove the anonymous array reference, which isn’t needed anyway:

        my @factors;

        and the code now runs without error!

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: List::Utils can't find max in array of BigInts (XS--)
by tye (Sage) on Apr 24, 2013 at 03:57 UTC

    That's actually what I'd expect. List::Utils is full of XS code. XS code almost always gets simple things wrong. It almost never handles mundane Perl magic like overloading, which is what bigint.pm is a simple demonstration of.

    List::Util comes with non-XS implementations of at least most of its routines and those should have no problems with bigint values. Or you could just write your own 'max' in about 1 or 2 lines of Perl code.

    - tye        

      But List::Utils does handle magic in the XS version too. The code is there to see... though it may be buggy.

        Having XS code that attempts to deal with even mundane Perl magic is still never something I expect. (Dealing with overloading wasn't something List::Util's XS code even attempted for the first decade of its existence). But, yes, XS code is indeed much, much more likely to be buggy than Perl code. Worse, XS code is much, much more likely to be buggy in ways that you don't notice at first and then cause your code to fail in ways that don't even hint that the XS code is to blame.

        But, you know: speed! It is vitally important that your max() function run a fraction of 1ms faster and increasing the code size by an order of magnitude and the code complexity by two orders of magnitude is surely worth all of the time wasted by everybody when it means that a few scripts are running a few ms faster and reducing their total run time by 0.2%.

        Every professional software developer knows that "trivially faster code" trumps "correct code" every time.

        Me, I tend to care a lot about hidden complexity of code. And so I'm pretty unlikely to use List::Util (especially since v1.23_03: "Dropped the pure-Perl implementation of both Scalar::- and List::Util."). I'll use Perl code because mundane Perl magic gets heavily tested and heavily exercised there and so the odds of me having to dive into the Perl source code because of a bug are much lower.

        - tye        

        “It may be buggy” of course being the operative phrase here ...

        Sometimes pure-Perl brute force is just as good as anything else, because there’s only one way to find the maximum value in an unsorted list, and the overhead of the Perl interpreter running the loop to do that is probably worth ignoring.

      Dang, even a simple foreach produces the same unblessed reference error:

      my $max_bigint = $result[1]; foreach (@result) { if ($_ > $max_bigint) { $max_bigint = $_; } } say $max_bigint;
      -------
      Can't call method "can" on unblessed reference at C:/Languages/Perl/perl/lib/Math/BigInt.pm line 2693. at trial_division.pl line 12. main::__ANON__('Can\'t call method "can" on unblessed reference at C:/Languag...') called at C:/Languages /Perl/perl/lib/Math/BigInt.pm line 2693 Math::BigInt::objectify(2, 'Math::BigInt', 'ARRAY(0x3faefc)', 'Math::BigInt=HASH(0x3fb1bc)') called at C:/Languages/Perl/perl/lib/Math/BigInt.pm line 1050 Math::BigInt::bcmp('Math::BigInt', 'ARRAY(0x3faefc)', 'Math::BigInt=HASH(0x3fb1bc)') called at C:/Languages /Perl/perl/lib/Math/BigInt.pm line 65 Math::BigInt::__ANON__('Math::BigInt=HASH(0x3fb1bc)', 'ARRAY(0x3faefc)', 1) called at trial_division.pl line 55

      It the exact same stack trace as when I tried to use List::Util::max().