Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Return array from sub or return empty array

by gregzartman (Initiate)
on Jul 04, 2016 at 04:39 UTC ( [id://1167104]=perlquestion: print w/replies, xml ) Need Help??

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

I am doing some dev for a linux distro with a perl API for managing the configuration for the server. Quite a bit of the underling API are bits of perl code we call event and actions.

One of the "practices" many follow is assigning an empty value when a string is set so as not to end up with a undef value. Something like this:

$var = some_class->some_method() || ''

This is assuming some_method returns a scalar.

I've just always used this approach but found that this doesn't work when returning arrays "some_method" I'm hoping you all can help me understand what perl is doing when it returns and array.

Here's the bit of code I was testing with:

@array1 = get_array2() || (); foreach (@array1) {print "$_\n";} sub get_array2 { @array2 = ("greg","Zartman"); return (@array2); }

I was thinking that @array1 gets assigned @array2 via the get_array2 function unless undef, in which case @array1 get assigned the empty array (). However, I can see from this test code that my thinking is wrong.

Thanks!

Replies are listed 'Best First'.
Re: Return array from sub or return empty array
by AnomalousMonk (Archbishop) on Jul 04, 2016 at 05:02 UTC
    @array1 = get_array2() || ();

    The problem with this statement is that the  || (logical-or) operator causes  get_array2() to be invoked in scalar context. So, of course, evaluation of  @array2 in the return statement of the function is in scalar context and you get the number of elements of the array as the returned value.

    There may be a conceptual disjunction here: while a scalar can be undefined or hold an empty string (or a few other things), an array only ever holds zero or more scalar elements. There is no such thing as an "undefined" array. (Update: Of course, an array element, if any exist, is always a scalar, so you could have an array with one or more undefined elements.)

    Update: Take a look at Context tutorial in the PerlMonks Tutorials.

    Update 2: Another conceptual problem with the quoted statement is rooted in the fact that a function only returns a list of zero or more elements. So what the statement is saying (or trying to say) is "assign the return list of the function call to the array unless the list is empty, in which case assign the literal empty list," which kinda doesn't make a lot of sense.

    Update 3: Changed "zero or more elements" to "zero or more scalar elements" in second paragraph above just for sake of clarity.


    Give a man a fish:  <%-{-{-{-<

Re: Return array from sub or return empty array (updated)
by haukex (Archbishop) on Jul 04, 2016 at 06:37 UTC

    Hi gregzartman,

    You've already got some good replies (it's all about Perl's concept of context); just to provide a different angle: what do you want the variables to hold?

    When you write my $foo;, the variable initially holds undef, and it's understandable and common to write something like $foo = func() // ''; (that is, set $foo to the scalar return value of func(), but when that return value is undef, set $foo to the empty string instead). Whether it's good practice depends on the situation; the point of this might be to prevent later warnings about $foo being undefined when interpolated into a string, (Update 2:) or to detect whether $foo has been assigned to or not. Note that the // defined-or operator became available in Perl 5.10.

    However, when you write my @bar;, that variable doesn't contain undef, it simply contains zero values. Also, a function which returns a list or array would normally not return undef to mean "no results", it would return an empty list*. So there is no need to do something like "assign an empty value to an array if the function doesn't return anything", since the array will simply continue to contain zero values, and there are no warnings about that.

    Now, a different question might be: what if a function returning zero values were to represent an error condition that you wanted to detect? To do that, I would suggest to first fetch the return value of the function into an array, and then test it, for example: @bar = func2(); if (!@bar) { warn "func2 returned nothing" }.

    * Update 2: Some really interesting discussions come up when you search for "return undef", for example this thread seems to be very relevant, also this one.

    Hope this helps,
    -- Hauke D

    Updated: Typo fix and minor rewordings.

      Mostly for the sake of the OP and anyone else who thinks error return codes are a good idea.

      The old school technique of returning a special value to indicate an error condition is kinda OK, but far better is to throw at the point the error condition is raised. That makes tracking down bugs related to detected errors much easier. The down side with ignoring errors is you may never notice the error condition and the code will quite happily complete - often very fast. That is of course an excellent outcome if you don't care that the result is bogus.

      In cases where you know that something may fail but really don't care, wrap the called code in an eval and ignore the error. At least then you are explicitly ignoring the error rather than maybe just forgetting to test the result.

      Premature optimization is the root of all job security

      Thanks for all the great replies guys. What you have explained confirms what I thought was going on

      Hauke, as you point out the issue is with undef. In some of the places in the API I'm working with an undef scalar can throw perl warnings in the log files. The simple solution is to just do:

       @array2 = a_sub_that_returns_a_list || '';

      My main reason for asking the question was to understand what perl was doing with the || in the context I was using it and you guys have explained it well. Thanks!

        Hi gregzartman,

        The simple solution is to just do:

        @array2 = a_sub_that_returns_a_list || '';

        As AnomalousMonk explained, I doubt that code will do something that is useful for you...?

        Why not just do

        @array2 = a_sub_that_returns_a_list();

        ?

        Regards,
        -- Hauke D

Re: Return array from sub or return empty array
by Anonymous Monk on Jul 04, 2016 at 05:49 UTC
    $var = some_class->some_method() || ''
    Don't do that. It will bite you if some_method returns zero. You can use the double-slash operator instead, if you have a reasonably recent perl installed. But you're probably better off either handling undefs properly, or just turning off warnings.
Re: Return array from sub or return empty array
by Cow1337killr (Monk) on Jul 05, 2016 at 03:28 UTC

    So, in most cases, the downstream code wants a scalar to either be some value or an empty string. The downstream code wants an array to either be some non-empty array or an empty array.

    Now, maybe, I have obtained some subroutines or methods that return scalars and other subroutines or methods that return arrays. I want to use those subroutines or methods without modifying them because, if I change them, I might make a mistake and then they won’t do what I thought they were supposed to do (and everyone else on the project thought they would do).

    So, wouldn’t it make sense to wrap those subroutines or methods in a subroutine that will make sure that the downstream code gets what it expects?

    sub somefunc_for_scalars { my $answer = some_class->some_method(); return defined($answer) ? $answer : ''; }

    Digression: If the reader wants to use Perl's // operator in the return statement, go ahead. However, it may confuse other programmers. See http://stackoverflow.com/questions/23873379/what-does-the-double-forward-slash-mean-here for a nice explanation of Perl's // operator, which is available with Perl 5.10 or later.

    sub somefunc_for_arrays { my @answer = get_array2(); return @answer; }

    However, somefunc_for_arrays is superfluous if it is correct for get_array2() to return some non-empty array or an empty array.

    In some rare instances, the downstream code does not want an empty array. So, code somefunc_for_arrays thusly:

    sub somefunc_for_arrays { my @answer = get_array2(); if (!(scalar @answer)) { die 'get_array2() returned an empty array +.' }. return @answer; }

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (4)
As of 2024-03-29 06:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found