33

I'm trying to understand Perl context and I tripped over a rock;

Given the code;

#!/usr/bin/perl

my $b = (33,22,11);
print "$b\n";

my $b = () = (33,22,11);
print "$b\n";

my @b = (33,22,11);
print "@b\n";

my @b = () = (33,22,11);
print "@b\n";

The results are (the last line is blank);

 11  

 3 

 33 22 11

 <>

Since the 2nd print returned the length of the list I was assuming that somewhere an array was generated since an array in a scalar context evaluates to its length. But the 4th print seems to belie that assumption. I was expecting to see '33 22 11' printed but got nothing instead. What's happening here?

Bulletmagnet
  • 5,665
  • 2
  • 26
  • 56
wwas
  • 331
  • 3
  • 3
  • 2
    This is a good question -- +1 -- but I think it's really a duplicate of [List Assignment in Scalar Context](http://stackoverflow.com/questions/9307137/list-assignment-in-scalar-context), which has great answers that you should go look at. – ruakh Jun 16 '13 at 18:39
  • 2
    `use warnings` would have given you some clues. – Bill Ruppert Jun 16 '13 at 22:42

3 Answers3

19

You're confused because you think = is a single operator while it can result in two different operators: a list assignment operator or a scalar assignment operator. Mini-Tutorial: Scalar vs List Assignment Operator explains the differences.

my $b = (33,22,11);
------------------            Scalar assign in void context.
        ----------            List literal in scalar context. Returns last.

my @b = (33,22,11);
------------------            List assign in void context.
        ----------            List literal in list context. Returns all.

my $b = ( () = (33,22,11) );
---------------------------   Scalar assign in void context.
        -------------------   List assign in scalar context. Returns count of RHS
               ----------     List literal in list context. Returns all.

my @b = ( () = (33,22,11) );
---------------------------   List assign in void context.
        -------------------   List assign in list context. Returns LHS.
               ----------     List literal in list context. Returns all.

As for your title, forcing list context is impossible per se. If a function returns a list when a scalar is expected, it results in extra values on the stack, which leads to operators getting the wrong arguments.

You can, however, do something like:

( EXPR )[0]

or

( EXPR )[-1]

EXPR will be called in list context, but the whole will return just one element of the returned list.

ikegami
  • 367,544
  • 15
  • 269
  • 518
8

List assignment in scalar context returns the number of elements on the right hand side (case 2). The case 4 first assigns (33, 22, 11) to (), and then assigns () (whose value has not changed) to @b.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • I thought a list in a scalar context returns the last item of the list. I'll consult another reference to double check. – wwas Jun 16 '13 at 20:08
  • 4
    a list *assignment* in scalar context; not a list in scalar context (see my answer) – ysth Jun 16 '13 at 20:28
4

Answering the implicit question in the title:

Perl provides a scalar builtin to force scalar context when it would not otherwise be used; a similar list builtin doesn't exist, I imagine largely because it could mean a number of different things, e.g.:

list { code to execute in list context };

might be defined:

sub list (&) { scalar( () = $_[0]->() ) } # return count of elements
sub list (&) { ( $_[0]->() )[0] } # return first element
sub list (&) { ( $_[0]->() )[-1] } # return last element
sub list (&) { for( $_[0]->() ) {} } # return nothing

Answering the body of the question:

When confused by context, remember that operators impose context on operands, not the reverse. In your problem case, you have a scalar assignment, assigning to a scalar the result of a list assignment, which puts the list assignment in a scalar context. To know what happens, simply look up what the list assignment will return in scalar context. (In general, operators return what makes the most common sense, which means there isn't an easy to remember rule that applies to all operators.)

ysth
  • 96,171
  • 6
  • 121
  • 214
  • 8
    According to [the documentation for `scalar`](http://perldoc.perl.org/functions/scalar.html), "There is no equivalent operator to force an expression to be interpolated [sic] in list context because in practice, this is never needed. If you really wanted to do so, however, you could use the construction `@{[ (some expression) ]}` , but usually a simple `(some expression)` suffices." – ruakh Jun 16 '13 at 21:56
  • 2
    my recollection is that `scalar(()=...)` is more efficient, but if the arrayref approach is clearer, use it – ysth Jun 16 '13 at 22:14