Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

how do subroutines work

by baxy77bax (Deacon)
on Mar 22, 2008 at 13:22 UTC ( [id://675624]=perlquestion: print w/replies, xml ) Need Help??

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

hi, well i have one beginners question. can someone explain to me why does this work, and how it works:
my %list = ( pas => 'vau-vau', macak => 'mijau', krava => 'muuuuu', pile => 'pi-pi', mago => 'ia-ia' ); sub zivad { my $animal = shift; my $list = $list{$animal} || die "no animal"; return $list; } print zivad('mago');
and it prints out 'ia-ia'. the confusing part is : what does the subroutine loads into @_ does it loads the whole list of just keys or keys and values... and what does it do with that list ?

Replies are listed 'Best First'.
Re: how do subroutines work
by Pancho (Pilgrim) on Mar 22, 2008 at 13:32 UTC

    The zivad sub gets one parameter 'mago' in the array @_, which is stores in my $animal as a result of the default use of shift. It is shifting the leftmost scalar in the array @_. The my $list variable does not store a list but a scalar, namely the value of the %list hash for the key $animal. then the scalar $list is returned and printed...

    This should be clearer

    my %hash = ( pas => 'vau-vau', macak => 'mijau', krava => 'muuuuu', pile => 'pi-pi', mago => 'ia-ia' ); sub zivad { my $animal = shift; my $hash_value = $hash{$animal} || die "no animal"; return $hash_value; } print zivad('mago');

    Not that I would use those variable names but they give you an idea of what the variables contain.

    Pancho
Re: how do subroutines work
by FunkyMonk (Chancellor) on Mar 22, 2008 at 13:42 UTC
    If you add print "@_\n"; between sub zivad { and my $animal = shift;, perl will tell you exactly what is in @_. In this case you'd see mago (which comes from print zivad('mago');).

    %list can be accessed anywhere after my in the snippet you posted, and that's just what your subroutine is doing.

    This is what happens step-by-step

    • the subroutine call zivad('mago') puts "mago" into @_ and starts running the code in the subroutine zivad.
    • my $animal = shift takes the first element of @_ (which will be "mago") and puts it in to $animal.
    • my $list = $list{$animal} || die  "no  animal" does a few things:
      • Looks up $animal in %list and returns the value associated with "mago" ("ia-ia").
      • If the animal wasn't in %list, the script will stop (die) with the "no animal" error message.
      • The value returned from %list is assigned to $list.
    • return $list returns "ia-ia" to the print that displays it on the screen.

Re: how do subroutines work
by hipowls (Curate) on Mar 22, 2008 at 21:24 UTC

    The explanations given are correct. To elaborate on nefigah's point you will often find data structures which logically belong inside a subroutine defined outside. That is because if it is defined inside the subroutine the variable has to be initialized every time the subroutine is called. Unfortunately you can't just stick the variable next to the subroutine as the variable must be initialized before the subroutine is called.

    eval { print speak('sheep'), "\n"; }; print $@ if $@; my %says = ( sheep => 'baa', monkey => 'foo', ); eval { print speak('monkey'), "\n"; }; print $@ if $@; sub speak { my $animal = shift; die unless defined $says{$animal}; return $says{$animal}; } __END__ Died at Perl-1.pl line 21. foo
    Placing that data structure before calling speak in the example is a bad practice, it spacially separates things that are related and gives wide access to a data structure that is private to a subroutine.

    Perl 5.10 has state variables for just this purpose, they are initialized only on the first time a function is called.

    use 5.010_000; eval { say speak('sheep'); }; print $@ if $@; eval { say speak('monkey'); }; print $@ if $@; sub speak { my $animal = shift; state $says = { sheep => 'baa', monkey => 'foo', }; die unless defined $says->{$animal}; return $says->{$animal}; } __END__ baa foo
    Note that state variables can only be scalars (at least at 5.10.0) and I've had to change %says = () to $says = {}. I also get to use say ... instead of print ..., "\n"

    If you have to do this in earlier versions then wrap the data structure and sub routine in a BEGIN block so the data is initialized during compilation.

    BEGIN { my %says = ( sheep => 'baa', monkey => 'foo', ); sub speak { my $animal = shift; die unless defined $says{$animal}; return $says{$animal}; } }
    If initializing the data is expensive and you may not need it or you can't initialize it at compile time then you can do
    { my $says; sub speak { my $animal = shift; $says |= initialize_says(); die unless defined $says->{$animal}; return $says->{$animal}; } }
    $says will only be initialized if it is not already initialized. If available state is much cleaner;)
    sub speak { my $animal = shift; state $says = initialize_says(); die unless defined $says->{$animal}; return $says->{$animal}; }

Re: how do subroutines work
by nefigah (Monk) on Mar 22, 2008 at 20:34 UTC

    You should note that your subroutine here didn't "load" any part of the %list hash into @_ at all; instead you are relying on the fact that somewhere out there is a hash called %list that works how you want it to. As you can imagine, this is not necessarily the safest way to code. You would be better off either making the hash in the subroutine itself (if it's the only thing that uses it) or passing the hash as a parameter (or an element of the hash as a parameter, depending on what is needed) to the sub. This way everything doesn't break if things get moved around or messed with.

    Also, as Pancho pointed out, names are important, especially when you're a beginner to help understand what's going on. I imagine the line:

    my $list = $list{$animal}

    is quite confusing, because you are making a new scalar variable that has the same name ('list') as a hash that you are accessing an element of (meaning you have to use the $ in front of both). This is legal but confusing. Also confusing is that you are calling the scalar $list, when in fact it's not a list at all.

    I'm not sure if this is more advice than you bargained for, but do keep it in mind; it will make what's going on more obvious!


    I'm a peripheral visionary... I can see into the future, but just way off to the side.

Re: how do subroutines work
by baxy77bax (Deacon) on Mar 22, 2008 at 14:31 UTC
    Thank you!!
Re: how do subroutines work
by alpha (Scribe) on Mar 23, 2008 at 02:11 UTC
    Oh man. You should really read that Learning Perl book through and through before asking any questions.
      Or perlintro
      Writing subroutines Writing subroutines is easy: sub log { my $logmessage = shift; print LOGFILE $logmessage; } What's that "shift"? Well, the arguments to a subroutine are available to us as a special array called @_ (see perlvar for more on that). The default argument to the "shift" function just happens to be @_. So "my $logmessage = shift;" shifts the first item off the list of arguments and assigns it to $logmessage. We can manipulate @_ in other ways too: my ($logmessage, $priority) = @_; # common my $logmessage = $_[0]; # uncommon, and ugly Subroutines can also return values: sub square { my $num = shift; my $result = $num * $num; return $result; } For more information on writing subroutines, see perlsub.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2024-04-18 04:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found