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

As far as I can tell, I'm not using any array references, yet I get a runtime error of
Can't use an undefined value as an ARRAY reference at make_change.pl l +ine 12.
when I run this code:
use strict; use warnings; use re 'eval'; my @denoms = (10, 6, 5, 1); my $expr = join '', map "((?:$_)*)", map '1' x $_, @denoms; print "E=$expr\n"; sub greedy_change { my $change = shift; my $coin_count = 0; my @combo; $_ = ('1' x $change); print "Trying to change $change with @denoms...\n"; print "$expr on $_\n"; if ( m{^$expr$ (?{ print "Code doesn't get run after once.\n"; my @lengths = ($+[1]); my $new_count = 0; $lengths[$_] = $+[$_+1] - $+[$_] for 1..$#denoms; $new_count += $lengths[$_]/$denoms[$_] for 0..$#denoms; if (!$coin_count or $new_count < $coin_count) { print "$change: $new_count is better than $coin_count\n"; $coin_count = $new_count; @combo = @lengths; } }) }x) { print "Matched!\n"; } if ($coin_count) { print "C=$coin_count\n"; } else { print "No matching combination found\n"; } @combo; } for my $num (12, 14, 36) { my @change = greedy_change($num); print "$num: @change\n"; }
I know I'm doing some weird stuff, but the errors I've been getting just make no sense. Now my eyes are fried, and I need some fresh ones to help me out.

Caution: Contents may have been coded under pressure.

Replies are listed 'Best First'.
Re: Undefined value as array reference
by dragonchild (Archbishop) on Mar 11, 2005 at 02:31 UTC
    Try the following. It runs - I don't know if it works.
    sub greedy_change { my $change = shift; my $coin_count = 0; my @combo; my $blah = sub { print "Code doesn't get run after once.\n"; my @lengths = ($_[1]); my $new_count = 0; $lengths[$_] = $_[$_+1] - $_[$_] for 1..$#denoms; $new_count += $lengths[$_]/$denoms[$_] for 0..$#denoms +; if (!$coin_count or $new_count < $coin_count) { print "$change: $new_count is better than $coin_co +unt\n"; $coin_count = $new_count; @combo = @lengths; } }; print "Trying to change $change with @denoms...\n"; print "$expr on $_\n"; $_ = ('1' x $change); if ( m{^$expr$ (?{ $blah->(@+) }) }x) { print "Matched!\n"; } if ($coin_count) { print "C=$coin_count\n"; } else { print "No matching combination found\n"; } @combo; }

    It looks like there's some re-entrancy issues. The ARRAY ref thingy is because @_ is borked. If you put a print "@_\n"; as the first line, you'll get the same thing. If you put a print "Hello\n"; as the first line, you'll get a Signal 1 error (whatever that is).

    Either way, you've found a bug in Perl. I would guess it's in the experimental use re 'eval'; feature you're (ab)using.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Undefined value as array reference
by ikegami (Patriarch) on Mar 11, 2005 at 02:28 UTC

    $_ is quite often a reference to something. It must be here too. When $_ = ('1' x $change); is executed, perl tries to change that to which $_ points, and it can't.

    Change
    $_ = ('1' x $change);
    to
    local $_ = ('1' x $change);

    It's very good practice to localize $_ in your subs if you use it for situations like this. for/foreach with no variable localizes it for you, so you rarely need to localize it explcitily.

      That doesn't work. However, if you chaneg the $_ = ('1' x $change); to my $x = ('1' x $change);, change the regex, and add a local $_; to the inner function, it runs to completion.

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Undefined value as array reference
by Zaxo (Archbishop) on Mar 11, 2005 at 02:32 UTC

    I don't get the error on 5.8.4-linux. Output with unmodified code:

    $ perl change.pl E=((?:1111111111)*)((?:111111)*)((?:11111)*)((?:1)*) Trying to change 12 with 10 6 5 1... ((?:1111111111)*)((?:111111)*)((?:11111)*)((?:1)*) on 111111111111 Code doesn't get run after once. 12: 3 is better than 0 Matched! C=3 12: Trying to change 14 with 10 6 5 1... ((?:1111111111)*)((?:111111)*)((?:11111)*)((?:1)*) on 11111111111111 Code doesn't get run after once. Matched! No matching combination found 14: Trying to change 36 with 10 6 5 1... ((?:1111111111)*)((?:111111)*)((?:11111)*)((?:1)*) on 1111111111111111 +11111111111111111111 Code doesn't get run after once. Matched! No matching combination found 36: $
    Perhaps you're running the wrong copy? (I do that a lot)

    After Compline,
    Zaxo

      I got the error on perl, v5.8.5 built for cygwin-thread-multi-64int

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

        I got the error on perl, v5.8.3 built for i586-linux-thread-multi

      I got the error on 5.8.4 for mswin32-x86-multi-thread (activestate binary build 810). If time permits, I'll try later on my freebsd partition.

      Dragonchild's rewritten greedy_change() works without producing that error, however I do get one "Use of uninitialized value in concatenation (.) or string at test.pl line 30" warning.

      Update: 5.8.6 mswin32-x86-multi-thread (AS bin build 811) also affected.

      /renz.
      "Call on God, but row away from the rocks."
      --Hunter S. Thompson.

Re: Undefined value as array reference
by graff (Chancellor) on Mar 12, 2005 at 15:48 UTC
    Like Zaxo, I was able to run the code unmodified, on 5.8.5 built for i386-freebsd-64int, but when I tried it with 5.8.1 on macosx (built for darwin-thread-multi-2level), I got the behavior you cited.

    When I tried stepping through it with "perl -d" on the mac, I found that at some point within the regex-eval code, it got completely lost and went into something that strongly resembled a tight infinite loop (chewed up tons of cpu, and couldn't be stopped with "^C") -- I had to go to another shell, look up the pid for the "perl -d" job, and kill it.

    Interestingly, when I tried stepping through it with the debugger in 5.8.5 freebsd, it went fine up to a point: it output "12: 3 is better than 0", went a couple more steps, then died with:

    Out of memory during ridiculously large request at 438502.pl line 17.

    Presumably, my line numbers might not match yours -- 17 is the start of the "if (..." that contains that truly bizarre regex containing a whole subroutine worth of executable code as part of a match operation.

    So what's really strange, beyond the fact that "older" builds can't run this code at all, is that a relatively new build will blow up only if you run the code in debug mode.

    Patient: Doc, it really hurts every time I do this.

    Doctor: Well, don't do that.

    Really, why put all that code into the regex? Even in the 5.8.5 "perlre" manual, the description of the "(?{code})" feature starts with:

    WARNING: This extended regular expression feature is considered highly experimental, and may be changed or deleted without notice.
    If you really can't resist "playing with matches", then at the very least, simplify the regex to an actual subroutine call, and put all that code into a sub. Maybe it won't change what perl utlimately does when faced with your experimental code, but it'll make it easier for you to refactor the code into something that makes sense (and actually runs).

    BIG UPDATE: So I tried this advice myself, and rewrote the code as follows:

    Three very interesting discoveries: First, when stepping through your original code with any version of debugger, I was never able to inspect the values of variables that were lexically scoped within the regex code (i.e. "my @lengths, my $new_count" were not accessible for inspection in the debugger); when I put the code into a sub, this problem went away, and I was able to check these variables as I stepped along.

    Second, with the regex code block separated into a sub, the script behaved the same on 5.8.1 darwin as it did on 5.8.5 freebsd -- that is, it ran and did not die. (Whether it actually produced output that you would consider "correct and intended" is another question, and I have doubts about that.)

    Third, I saw some striking evidence to indicate why this new regex/eval feature is considered experimental, and why it should not be trusted yet. Consider the following snippet from the debugging session:

    ... DB<1> s main::regex_sub(438502.pl:40): print "Code doesn't get run after +once.\n"; DB<1> s Code doesn't get run after once. main::regex_sub(438502.pl:41): my @lengths = ($+[1]); DB<1> p scalar @+ 5 DB<2> p join ":",@+ 12:10:10:10:12 DB<3> s main::regex_sub(438502.pl:42): my $new_count = 0; DB<3> p @lengths 10 DB<4> p join ":",@+ 23:22:23
    Like, WHOA! What's up with that? I can't begin to guess. I'd stop right there and try a completely different approach that avoids the experimental feature.

    (One more update: I fixed a misspelling in the "(?{...})" subroutine call (initially had "$coing_count" as first parameter). Since it was in an eval-like block, it wasn't caught by "strict". Also, I found that the odd behavior of @+ in the debugger was not related to any particular step in the code -- it's simply an issue of getting one result the first time you inspect @+ within the eval block, and getting a different result the second time:

    main::regex_sub(438502.pl:40): print "Code doesn't get run after +once.\n"; DB<1> s Code doesn't get run after once. main::regex_sub(438502.pl:41): my @lengths = ($+[1]); DB<1> p scalar @+ 5 DB<2> p scalar @+ 3
    Just the fact that it's hard to debug this approach makes me feel strongly about choosing a different approach.)