Christina has asked for the wisdom of the Perl Monks concerning the following question:
I know that I can't take reference to the return value of a subroutine directly. But I want to know why? Here is an example.
sub foo{ return (0, 1); } my $var = \(foo()); # $var is not a reference to the array (0, 1).
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Reference to return value of a subroutine
by haukex (Archbishop) on Apr 02, 2017 at 14:25 UTC | |
the array (0, 1) I think this might be the misunderstanding here: (0, 1) isn't an array, in this form it's a literal list. While you obviously use lists to populate arrays, arrays and literal lists behave differently in different contexts. For example, an array in scalar context returns its size, whereas a list returns its last value, and taking a reference to an array creates an arrayref, while taking a reference to a list works as follows. What is going on here is that your sub foo is returning the list (0, 1), and the \ (backslash) referencing operator is "distributive" when applied to lists, meaning that \(0, 1) is the same thing as (\0, \1). So your assignment basically becomes my $var = (\0, \1);. Then, the Comma Operator in scalar context "evaluates its left argument, throws that value away, then evaluates its right argument and returns that value", which is why $var is being assigned \1. You could do several things, as the other monks have shown: [foo()], return [0, 1], or also possible would be return \@array. However, note that \ has a special-case behavior which means that sub foo { my @array = (0,1); return @array }; my $var = \(foo()); would not work, as "\(@foo) returns a list of references to the contents of @foo, not a reference to @foo itself." (perlref). Update: See also this recent thread, the FAQ What is the difference between a list and an array?, and perhaps one or two other PerlMonks threads such as What is the difference between a list and an array?. | [reply] [d/l] [select] |
|
Re: Reference to return value of a subroutine
by golux (Chaplain) on Apr 02, 2017 at 13:56 UTC | |
To make it more obvious, try this:
What's happening is that you're returning a list that gets evaluated in scalar context, whose behavior is to take the last item of the list. If you want to capture the whole list, try returning an ARRAY reference instead (then you don't even need the extra reference):
| [reply] [d/l] [select] |
|
Re: Reference to return value of a subroutine
by stevieb (Canon) on Apr 02, 2017 at 13:49 UTC | |
Well, you can, but you just have to use an alternate syntax:
In this case, you should know in advance that foo() returns a list that you want to take as an array reference. If a scalar is returned, it will automatically be populated as the first element in the array reference, regardless of its type. | [reply] [d/l] [select] |
|
Re: Reference to return value of a subroutine
by huck (Prior) on Apr 02, 2017 at 13:52 UTC | |
Does this help Result
| [reply] [d/l] [select] |
|
Re: Reference to return value of a subroutine
by davido (Cardinal) on Apr 02, 2017 at 16:56 UTC | |
Your example code takes a reference to each individual list element returned by foo(), and then retains the last one. If you want a reference to an array, you have two choices. First, you could modify the subroutine to return a reference to an array:
Second, you could instantiate an array reference upon receiving the return value of the subroutine:
In the second example, the [...] square brackets are creating a reference to an anonymous array that is being populated by the list returned from foo(). For large lists, the first example could be more efficient since it passes a single value (the array reference) back from the subroutine call, rather than a potentially large list which must then be pulled into a new reference. We all love to hate wantarray, but how about this?
Yeah.... that code kind of smells because it has a strong potential of surprising the caller. Just a thought, but you'll probably want the first or second example and just keep the third as an example of something that Perl can do but maybe shouldn't. ;) Dave | [reply] [d/l] [select] |
by shmem (Chancellor) on Apr 03, 2017 at 07:51 UTC | |
We all love to hate wantarray This is flat out wrong. I for one don't. wantarray is probably the most perlish of all functions, since it is the most linguistic one. It gives elements (subroutines) which are ambiguous the ability to reveal their meaning depending on context. Bank of England, river bank, mining bank (this is itself ambiguous), bank of a billard table, battery bank, road bank - in all elements of this comma separated list the word 'bank' chooses its appropriate meaning out of all possibilities depending on its context. It is the only function which makes perl's ambiguity and context awareness useful for the programmer. Without a way to determine the calling context inside a sub, all the context sensivity of perl would be just a nuisance, and we would do better without it. So, wantarray is at the core of one of perl's most fundamental concepts. If I hated the core of perl, I'd probably program python. Yeah.... that code kind of smells because it has a strong potential of surprising the caller. Why? Does localtime smell, because it may be surprising for the caller? And then - what is more surprising for someone: a) getting the last element of the returned list, in scalar context, or b) getting a scalar reference to an array holding the elements of the returned list? It is all a matter of taste, convention and documentation, e.g. this foo() - this function returns a list of bars. When called in scalar context, you get the last element by default. or this foo() - this function returns a list of bars. If called in scalar context, it returns a reference to an array holding that list. Quick, which one is more intuitive for the unaware? So no, there is no smell here, except for those who are ashamed of some aspects of perl which are its core features and rather would weed out all ambiguities and clamp it all under the harness of e.g. PerlCritic.
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [d/l] |