From:		mjd@op.net (Mark-Jason Dominus)
Newsgroups:	comp.lang.perl.misc
Subject:	Re: Some help with the 'reverse' command please.....
Date:		24 Jan 1998 12:47:20 -0500
Summary:	Scalar vs. list context
Keywords:	glycerol payroll perceptual recitative
X-Motto:	Now Available in Thrilling `PloverVision!'
X-Project:	Eat
X-Subliminal-Catchphrase:	``Tohu Vavohu''

In article <34C97BC7.610B19B7@inav.net>, Steel Viper <sviper@inav.net> wrote:


>@string_array = <STDIN>;
>
>@string_array = reverse(@string_array);
>print("\n@string_array\n");
>print("\n" . reverse(@string_array) . "\n");
>
>
>If this is run, the first print statement prints out the lines in the
>opposite order they were entered, however the second print statement
>reverses the entire line one at a time. I do not understand what is
>going on. 

That is a very good question, and it goes right to the heart of a very important and unusual feature of the Perl language, so it'll take a little while to explain. Please bear with me through this explanation of one of Perl's central concepts.

Every Perl expression is in one of two `contexts', either `list context' or `scalar context', depending on whether it is expected to produce a list or a scalar. Many expressions have quite different behaviors in list context than they do in scalar context.

Here is a typical expression:

	EXPRESSION

Here it is again, in a list context:

	@array = EXPRESSION;   # Expression is expected to produce a list

Here is how it looks in a scalar context:

	$scalar = EXPRESSION;   # Expression is expected to produce a scalar

The values that a particular expression has in these two contexts might be quite different. For example, consider this simple expression:

	@q

In list context, this produces a list of the items in the array @q, but in scalar context it produces the number of items in the array @q.

	@a = @q;    # @q in list context produces a list of items from @q
	$s = @q;    # but in scalar context, it produces the number of items.

Similarly,

	# Note two very different behaviors of // depending on context

	# Put ('123.45', 'immediately') into @a
	@a = /The amount was \$(\d+\.\d\d)\.  You must pay ([\w\s]+)\./ 

	# Put 1 into $s, put '123.45' into $1, put 'immediately' into $2
	$s = /The amount was \$(\d+\.\d\d)\.  You must pay ([\w\s]+)\./ 

The main point here: An expression's behavior can be drastically different in list context than it is in scalar context.

Now we'll return to your example.


>     @string_array = reverse(@string_array);

Here, reverse is in list context, because it's being asked to produce a list to assign to @string_array. In list context, reverse reverses the order of its arguments, so that the lines come out in the reverse order.

Then you did

>    print("\n" . reverse(@string_array) . "\n");

Now, here reverse is in scalar context, because it is being asked to produce a scalar value, a string, which will be concatenated with \n on the front and back. In a scalar context, reverse first concatenates all its arguments, to get a single string, and then reverses that string. As you know by now.

How can you get the behavior you really want? The reverse here needs to be in a list context. The argument list of a function like `print' is always a list context, so if you get rid of the concatenation operators that are providing scalar context to `reverse', your problem will go away:

	print "\n", reverse(@string_array), "\n";

reverse is now expected to return a list, which will be inserted into the list which is passed to print. This is probably more efficient than what you were trying to do anyway, since print can print out the many strings without bothering to concatenate them first, as you were doing.

Again, the general rule is:

There is no general rule for deducing a function's behavior in scalar context from its behavior in list context.

This is the cause of a lot of common errors. One mistake you didn't make, but which we see a lot is something like this:

	@a = (reverse("part"));

The programmer was expecting @a to get a list containing "trap", but instead they got a list containing "part" again. Because reverse was in a list context, it reversed the order of its arguments, instead of reversing the arguments themselves, and since it only had one argument, it ended up doing nothing.

Perl has a special operator for just such circumstances. It's called scalar, and it forces its argument to be evaluated in scalar context. The last example should have been written as:

	@a = (scalar(reverse("part")));
	# @a now contains ("trap")

I hope this clears things up for you.


Return to: Universe of Discourse main page | What's new page | Perl Paraphernalia

mjd-perl-context@plover.com