6

I have a function that returns an array, and I'd like to get just the first item from that array without ever declaring a variable for the array. Basically, it should be something like:

functionReturningArray()[1]

Except that doesn't work.

I really don't want to waste space declaring the whole array since I don't need it, and I'd rather not waste an extra line of code. Anyway to do this in one line?

Eli
  • 36,793
  • 40
  • 144
  • 207

3 Answers3

12
my $item = (function_returning_list())[0];

Functions cannot return arrays, they can return the contents of an array (i.e. a list). Lists are indexed starting at 0, so the first item of a function's return is (func)[0]. You could also say

my ($item) = function_returning_list();

This puts function_returning_list in list context and does assignment (in order) to the variables in the left-hand-side list.

It is important to note that

sub function_returning_list {
    return ("a", "b", "c")
}

my $item = function_returning_list();

will likely not do what you expect. The $item variable will be assigned "c". This is because there is no such thing as a list in scalar context. Instead, you have the comma operator in scalar context, and that evaluates the lefthand expression in void context, discards the result, then evaluates the righthand side in scalar context.

I call a sequence (rather than a list) and is useful if a few situations like C-style for loops.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • 8
    There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. There's no such thing as a list in scalar context. – brian d foy Sep 16 '10 at 16:52
  • 2
    You have the comma operator in scalar context. See [Know the difference between a list and an array](http://www.effectiveperlprogramming.com/blog/39) – brian d foy Sep 16 '10 at 16:53
  • @brian d foy That is what "This is called a sequence (rather than a list)" means. The concept of a list in scalar context is a useful lie. It is a lot easier to explain to people. Especially in situations like `sub f { return @a } my $x = f();` where no comma is in sight. – Chas. Owens Sep 16 '10 at 16:59
  • A cautionary note: If `function_returning_list` returns an empty list, then in list context `(function_returning_list())[0]` will evaluate to **nothing** (it's equivalent to `()`, not `undef`). That bit me once. – cjm Sep 16 '10 at 17:14
  • 1
    @cjm, I assume it was when you were trying to use it has a hash key or value? In scalar context `()` turns into `undef`, so it is only in list context that it can bite you. Always remember to say `scalar((function_returning_list())[0])` if you aren't sure if you are in scalar or list context. – Chas. Owens Sep 16 '10 at 17:24
  • @chas: you said "list in a scalar context". Those are the literal words in your post. There's no such thing. – brian d foy Sep 16 '10 at 18:09
  • **@brian d foy:** there's no such thing as list in scalar context. – vol7ron Sep 16 '10 at 18:29
  • @brian d foy Yes, I did, and I will continue to do so because it is a **useful lie**. I always follow it up with an explanation of what is really going on (i.e. it is a sequence not a list). The fact that is a lie does not mean it isn't useful. – Chas. Owens Sep 16 '10 at 19:12
  • 2
    It's not useful. I've been teaching Perl long enough to know that saying something completely false is what people pick up on and use. When you say it, they believe it because they don't really understand it. We can have an edit war, but it would be much better for you to use words that aren't lies, just as I do in the post I pointed you at. – brian d foy Sep 16 '10 at 19:14
  • @Chas. Owens, I was using it as a parameter to a function, and when I got an empty list, all the following parameters were suddenly off by one position. I just didn't realize it worked that way. I thought it would work like `$array[0]`, which is _never_ equivalent to `()` in list context, even if `@array` is empty. – cjm Sep 16 '10 at 20:07
  • @cjm Yeah, parameter lists of functions without prototypes provide list context. If you want an empty list to turn into an `undef`, you can provide scalar context with `scalar`: `f( scalar( ("a")[4] ) )`. – Chas. Owens Sep 16 '10 at 20:30
  • @brian d foy To convince me to not use the lie I have found useful in teaching people Perl, you will need to come up with a scenario where the lie bites you. The best argument against the lie I can think of is `if (@a, @b) {}`. If you could have a list in scalar context then `@a` & `@b` would flatten and you would be testing the last item in the list, but it is really testing the number of items in `@b` because it is a sequence, not a list. But I have never seen that construct in real code and can't imagine when it would be useful (even if it worked the way the lie would lead you to believe). – Chas. Owens Sep 16 '10 at 20:50
  • @vol7ron I can't tell if you are being a parrot or trying to make an [appeal to authority](http://en.wikipedia.org/wiki/Argument_from_authority). In the first case, I am fully aware that there is no such thing as a list in scalar context; in the second, that is a logical fallacy. I know who brian is, his stature in the Perl community has no bearing on this discussion. – Chas. Owens Sep 16 '10 at 21:06
  • 1
    @Chas: the scenario is in the FAQ and in the link I post to. Really, this isn't hard. There's no need to lie in this case. – brian d foy Sep 16 '10 at 21:26
  • **@Chas. Owens:** there's no such thing as list in scalar context. – vol7ron Sep 16 '10 at 21:57
  • my comment was far too long so I put it in an answer: http://stackoverflow.com/questions/3728337/how-do-i-get-the-first-item-from-a-function-that-returns-an-array-in-perl/3732386#3732386 – Eric Strom Sep 17 '10 at 02:55
  • @brain d foy Nothing in that link conflicts with what I said. Look at the description of the behavior, not the label attached to it. Telling a user that `sub f { return ("a", "b", "c") }` returns a list or sequence depending on context baffles them at a time when they are already very confused. If they can call that construct a list, then they have one less thing to worry about. What matters is the user knowing the behavior of the code, not label attached to the items in the code. Come up with a reason the label is bad or stop replying. – Chas. Owens Sep 17 '10 at 12:44
2

First, [1] is the second item in the list since Perl uses 0-based indices.

Second, you need the function's return to be evaluated in list context in order to have a list to access.

(functionReturningArray())[0]
jamessan
  • 41,569
  • 8
  • 85
  • 85
2

Going one step further than the discussion below Chas's answer, there's no such thing as a list period.

Unlike Python which has the tuple type filling this role, Perl's lists are entirely a construct of the stacks in the compiler/interpreter. This is why you can not take a reference to a list.

Just like python's use of tuples, perl automatically pushes and shifts its stack to move around groups of variables when a construct such as the = assignment operator is used. (Perl's precedence rules are what require the parenthesis, since = binds tighter than ,). The macro level difference between Perl's and Python's mechanisms is that context propagates through the entire expression.

So when in scalar context, that context is distributed to each of the , operators and the operator executes its left side in void context and its right side in scalar context which it then returns. However in list context the , operator pushes its arguments onto the stack, all of which are executed in list context. The stack is then passed across the assignment operator and then shifted into the lvalue. The stack/list itself is immutable to Perl level code, it's modification tools are effectively all of Perl's syntax.

So while it is incorrect to refer to a list in scalar context (because such a thing can not happen), you can refer to the behavior of list like syntactic constructs (constructed by the , operator, constructed by an array slice or list slice). In scalar context, the behavior of list like constructs is to return the last element of the list. How this is achieved is up to the operators in the expression. this is of course in contrast to the behavior of arrays in scalar context which return their length.

To clarify with an example and finally obey the rules and actually answer the question:

Assuming my @array = (10, 11, 12);

  • scalar context

      my $val = (10, 11, 12);        # $val is 12
      my $val = (10 .. 20)[0 .. 5];  # $val is 15      
      my $val = @array;              # $val is 3
      my $val = function();          # $val is the last executed expression
    
    • so if function ends with 10, 11, 12 then the value will be 12
    • if it instead is @array then the value will be 3
    • and if it ends with 10, 11, 12, @array the value is also 3

  • list context

      my ($val) = (10, 11, 12);        # $val is 10
      my ($val) = (10 .. 20)[0 .. 5];  # $val is 10
      my ($val) = @array;              # $val is 10
      my ($val) = function();          # $val is the first executed expression
                                       # in the last statement.
    
    • so if function ends with 10, 11, 12 then the value will be 10
    • if it instead is @array then the value will be 10
    • and if it ends with 10, 11, 12, @array the value is also 10

  • and for completeness, list context grabbing the whole list into an array

      my @val = (10, 11, 12);        # @val is 10, 11, 12
      my @val = (10 .. 20)[0 .. 5];  # @val is 10, 11, 12, 13, 14, 15
      my @val = @array;              # @val is 10, 11, 12
      my @val = function();          # @val is the last executed statement
    
    • so if function ends with 10, 11, 12 then @val will be 10, 11, 12
    • if it instead is @array then @val will be 10, 11, 12
    • and if it ends with 10, 11, 12, @array then @val will be 10, 11, 12, 10, 11, 12
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • 1
    Don't confuse the language with its implementation. – brian d foy Sep 17 '10 at 07:39
  • I fail to see how pointing out exactly what "lists" are in perl is confusing, its not as if there is some other implementation.... is there anything wrong with what I wrote? – Eric Strom Sep 17 '10 at 08:23