Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Recursive method calls and references

by Grimy (Pilgrim)
on Jul 28, 2012 at 08:54 UTC ( [id://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.

Replies are listed 'Best First'.
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 (Canon) 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'
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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-04-25 13:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found