Scalar vs list context in Perl

2.9k views Asked by At

I found an example in an O'Reilly book a little weird:

@backwards = reverse qw/ yabba dabba doo /;
print "list context: @backwards\n";
$backward = reverse qw/ yabba dabba doo /;
print "scalar1 context: $backward\n";
$notbackward = qw/ yabba dabba doo /;
print "scalar2 context: $notbackward\n";
print "print context: ",reverse qw/ yabba dabba doo /;
print "\n";

The output is:

list context: doo dabba yabba
scalar1 context: oodabbadabbay
scalar2 context: doo
print context: doodabbayabba

The one I do not understand is the scalar1 context:

The book says 'reverse something' gives a list context, so I guess qw/ yabba dabba doo / is seen as a list and reverse qw/ yabba dabba doo / as ('doo', 'dabba', 'yabba').

So comes the $backward = something which implies something is a scalar, so I was expecting the result 'doo dabba yabba', but it is différent: 'oodabbadabbay'.

I thought, the reason was because one cannot set a list to a scalar directly. So I made the scalar2 test: only the latest item in the list is printed. Why? Why not in the scalar1 test?

How do the scalar tests output work?

4

There are 4 answers

0
JohnC On BEST ANSWER

For the line:

$backward = reverse qw/ yabba dabba doo /;

You are requesting a scalar here from reverse. The perldoc for reverse says:

In scalar context, concatenates the elements of LIST and returns a string value with all characters in the opposite order.

So it returns each of the letters reversed.

For $notbackward = qw/ yabba dabba doo /; the perldoc for qw// says:

Evaluates to a list of the words extracted out of STRING, using embedded whitespace as the word delimiters. It can be understood as being roughly equivalent to:

               split(’ ’, q/STRING/);

the differences being that it generates a real list at compile time, and in scalar context it returns the last element in the list.

So requesting the scalar only returns the last item in the list.

0
cjm On

First of all, qw/ yabba dabba doo / is just syntactic sugar for ('yabba', 'dabba', 'doo'). They mean the same thing.

The reverse function takes a list. In list context, it reverses the list. In scalar context, it does join('', @list) and then reverses the characters in that string and returns it.

Remember that

$backward = reverse qw/ yabba dabba doo /;
$notbackward = qw/ yabba dabba doo /;

means

$backward = reverse ('yabba', 'dabba', 'doo');
$notbackward = ('yabba', 'dabba', 'doo');

The reverse function supplies list context and $notbackward = gives us scalar context. This means that the comma operator is in list context on the first line and scalar context on the second. In list context, the comma operator creates a list. In scalar context, it evaluates both operands and returns the right hand one. This means that the value of ('yabba', 'dabba', 'doo') in scalar context is 'doo', and that's what's assigned to $notbackward.

0
mob On

All Perl functions, including any sub's that you define, can detect whether they are being called in "scalar" or "list" context, and there are many functions that change their behavior based on this context.

There are very few conventions, other than Do What I Mean, about when and how a function treats the two contexts differently (this whole thread on perlmonks has a good discussion of these issues), so you need to rely on the documentation of each function to guess what a function will do in a particular context.

Specifically for your four examples,

1. @backwards = reverse qw/ yabba dabba doo /
2. $backward = reverse qw/ yabba dabba doo /
3. $notbackward = qw/ yabba dabba doo /;
4. print ..., reverse qw/ yabba dabba doo /;

the behaviors are

1. reverse function, list context:     returns list of the elements in reverse order

2. reverse function, scalar context:   concatenate argument list to a string,
                                       return reverse of that string

3. list assignment to scalar:          returns last element in list (*)

4. also reverse function, list context    same as #1

(*) - note list assignment to scalar is different from array assignment to scalar -- this is one of the most important distinctions between lists and arrays:

@a = ("x", "y", "z");  $n = @a;   # array assignment,  $n is array size, or "3"
$n = ("x", "y", "z");             # list assignment,   $n is last element, or "z"
0
Christopher Biggs On

To simplify the other answers, reverse actually does two separate things, list reversal and string reversal.

This has proven to be so confusing that perl6 splits them out to differently named functions.