Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Recursive method calls and references

by Grimy (Monk)
on Jul 28, 2012 at 08:54 UTC ( #984162=perlquestion: print w/ replies, xml ) Need Help??
Grimy has asked for the wisdom of the Perl Monks concerning the following question:

Howdy, monks. I hope you can help me understand this doozy:
use strict; use warnings; sub test_a { my $arg = shift; ref($arg) ? test_a$$arg[0] : $arg } sub test_b { my $arg = shift; ref($arg) ? test_a$$arg[0] : $arg } print test_b[42]; # prints '42' print test_a[42]; # dies 'Not a SCALAR reference at test line 3
Okay, so those two subs are exactly identical, yet one runs just fine and the other crashes. My only guess is that the recursive nature of test_a somewhat has an impact on the context given to the reference. Adding parentheses around $$arg[0] fixes that alright, but I don't care. I just want to understand.

Comment on Recursive method calls and references
Download Code
Re: Recursive method calls and references
by Anonymous Monk on Jul 28, 2012 at 08:58 UTC
    You got typos, test_b recurses into test_a, never test_b

      The problem is ## HELLO

      $ perl -MO=Deparse,-p grimy sub test_a { use warnings; use strict 'refs'; (my $arg = shift()); (ref($arg) ? $$arg->test_a([0]) : $arg); ## HELLO } sub test_b { use warnings; use strict 'refs'; (my $arg = shift()); (ref($arg) ? test_a($$arg[0]) : $arg); } use warnings; use strict 'refs'; test_b([42]); test_a([42]); grimy syntax OK

      The problem is indirect object syntax, to fix you need a forward declaration ( sub test_a; ) or parenthesis

        Thanks for your answer :)
      Nope, I don't have typos, and nope, test_b doesn't 'recurse', it just calls test_a. Only test_a is recursive, and only test_a crashes, which is why I assumed that it is the recursivity that causes problems, as you can read in my first post.
Re: Recursive method calls and references
by moritz (Cardinal) on Jul 28, 2012 at 09:09 UTC
    Adding parentheses around $$arg[0] fixes that alright, but I don't care.

    But you should care. If adding parenthesis helps, it's a parsing problem. Here's what B::Deparse has to say to the code:

    sub test_a { use warnings; use strict; my $arg = shift(); ref $arg ? $$arg->test_a([0]) : $arg; } sub test_b { use warnings; use strict; my $arg = shift(); ref $arg ? test_a($$arg[0]) : $arg; }

    So the call to test_a inside test_a is parsed as indirect method call syntax. Why? Because it's not predeclared. In Perl 5, a name only becomes visible in the statement after the declaration, which is why you can't write

    my $sub = sub { ...; $sub->(); .. };

    So, in test_b you call test_a, which has already been declared. So either use parens after the function name, or predeclare it with

    sub test_a; sub test_a { ...; recurse into test_a here };
Re: Recursive method calls and references
by tobyink (Abbot) on Jul 28, 2012 at 09:13 UTC

    This works:

    use strict; use warnings; sub test_a; sub test_a { my $arg = shift; ref($arg) ? test_a$$arg[0] : $arg } sub test_b { my $arg = shift; ref($arg) ? test_a$$arg[0] : $arg } print test_b[42]; # prints '42' print test_a[42]; # prints '42'

    The reason your original one fails is that while perl is parsing the body of sub test_a, it encounters test_a$$arg[0] and isn't quite sure how to parse it. You'd want to parse it like:

    test_a($$arg[0])

    But it actually gets parsed like this:

    ${ $arg }->test_a([0])

    That is, it assumes you're calling the test_a method on $$arg via the indirect method syntax.

    It makes this assumption because while the body of test_a is being parsed, there is no function called test_a in the symbol table. So it assumes that you cannot really mean test_a($$arg[0]).

    Pre-declaring sub test_a; (as per my example) solves this. The other solution is to avoid the ambiguity in your syntax and add parentheses.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://984162]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (9)
As of 2014-09-02 19:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (29 votes), past polls