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

Hello Monks, I am trying to return an array with no luck. I would like to return @L1 but am not sure how to do this. I get a "Can't return outside a subroutine" error. Code is as follows:

sub findLines; foreach $line (@data) { if ($line =~ /notice/) { if ($line =~ /rdy/) { $line =~ s/ /,/g; @L1 = split(/|notice|[[]|,mpmstats:,|[\t]|rdy,|bsy,|rd +,|wr,|ka,|log,|dns,|cls,/, $line); return @L1; } } }

The array contains this:

Wed,Jun,13,01:00:29,2012,777,23,0,15,6,0,0,2 Wed,Jun,13,01:01:29,2012,782,18,0,14,3,0,0,1 Wed,Jun,13,01:02:29,2012,787,13,0,10,3,0,0,0

What I would like to do is iterate through the array in another subroutine. What am I doing wrong?

Replies are listed 'Best First'.
Re: Returning Array
by MidLifeXis (Monsignor) on Jun 27, 2012 at 14:08 UTC

    • sub findLines; is an empty subroutine declaration with no body. Remove the ; and add curly braces.
    • You are not passing @data to the sub, but using a scoped / global variable of a fixed name.
    • Only the first line in @data that matches the nested if statements will be returned.
    • Also show how you call the sub, and how you are storing the returned value.

    --MidLifeXis

      Yeah I see that it only returns the first line in @data. How do I return all lines? I am just printing sub findLines for now. Thank you.

        Replace the return with a function (push, unshift, splice, as examples) that builds up a temporary array that you return.

        I would probably build it in two parts -- one that iterates over all of @data, one line at a time, and the other part that handles one line at a time. I might even put the part that iterates over @data back into the code that is calling your sub (perhaps with map or something similar).

        Your code is basically selecting a group of lines, and then transforming them. To me that says something along these lines (see grep and map):

        my @output = map { doTransformation( $_ ); } grep { shouldSelectData( $_ ); } @data;

        --MidLifeXis

        Am I right in assuming this is related to Perl substitution not working?

        As to answer your question... You claim to know that return EXPR returns the value of EXPR right away, without executing remaining code of the subroutine. With that knowledge in mind, it stands to reason not to return as soon as we find something, but only when we've searched everywhere.

        Imagine you were tasked to search every room in your house to find out how many power sockets there are, and where they are located, and report this to your roommate/mother/significant other/teddy bear, who isn't there at the moment so you'll have to give her a phone call.

        What do you do? Grab the phone as soon as you see a power socket? "Hey, yeah, it's me, AnonMonk. I found a socket in the kitchen. Bye!" Or do you go through the house systematically, keeping a list of sockets as you find them, and only grab the phone when you're confident you've covered every room. "Hey, yeah, it's me, AnonMonk. Okay, there are twenty-seven sockets: three in the kitchen, two in the bathroom, two in the master bedroom, etc etc."

        So that's what you have to do here, too. Keep track of the matching lines as you run into them, and then return the whole bunch when you're done iterating over the lines.

Re: Returning Array
by Anonymous Monk on Jun 27, 2012 at 14:04 UTC

    First error is here sub findLines;

    Fix is write sub findLines { ... }

    Explanation , use diagnostics

    $ perl -e " return " Can't return outside a subroutine at -e line 1. $ perl -Mdiagnostics -e " return " Can't return outside a subroutine at -e line 1 (#1) (F) The return statement was executed in mainline code, that is, w +here there was no subroutine call to return out of. See perlsub. Uncaught exception from user code: Can't return outside a subroutine at -e line 1. at -e line 1.
Re: Returning Array
by linuxkid (Sexton) on Jun 27, 2012 at 23:07 UTC
    sub findLines{ foreach $line (@data) { if ($line =~ /notice/) { if ($line =~ /rdy/) { $line =~ s/ /,/g; @L1 = split(/|notice|[[]|,mpmstats:,|[\t]|rdy,|bsy,|rd+,|wr,|k +a,|log,|dns,|cls,/, $line); } } } return @L1; }
    should look similar to that.

    --linuxkid


    imrunningoutofideas.co.cc

      Depending on where in the rest of the code @L1 lives, this would or wouldn't fail to compile under strictures. But let's say, for the sake of argument, that @L1 lives in a scope that encompasses the subroutine. What do you think that @L1 will contain by the time the foreach loop is done? That is, what do you think this subroutine will return?

      Valid answers are:

      1. The first line that matched /notice/ and /rdy/ - the first time @L1 was set, it was to that line, so it's gonna stick, right? You always remember your first time.
      2. The last line that matched /notice/ and /rdy/ - the last time @L1 was set, it was set to that line, so that's what's gonna stick, right?
      3. All lines that matched /notice/ and /rdy/ - each time a line matched, the line got put into @L1, which is an array, which can hold multiple pieces of data, so @L1 remembers all those lines that matched, right?