mlists一位大侠对上下文概念的阐述

mlists一位大侠对上下文概念的阐述

大家对Perl的上下文概念有所迷惑的,当认真读此前辈名言.

QUOTE:
>@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.

偶亦希望学Perl者,一定要弄清楚上下文这个基本概念.
thanks 学习了
认真的读一下
thanks very much
明白了表达的意思,只是不明白为什么会这样
print("\n" . reverse(@string_array) . "\n");
这一句居然reverse 了list本身顺序,因为分格符reverse 了 list中的元素倒是可以理解,
难道这个要强记下来么


QUOTE:
原帖由 penny_kan 于 2007-3-10 22:51 发表
明白了表达的意思,只是不明白为什么会这样
print("\n" . reverse(@string_array) . "\n");
这一句居然reverse 了list本身顺序,因为分格符reverse 了 list中的元素倒是可以理解,
难道这 ...

没听明白,想要表达what?
参考:
perl cookbook 2nd
Recipe 1.7 Reversing a String by Word or Character


QUOTE:
原帖由 兰花仙子 于 2007-3-10 23:57 发表


没听明白,想要表达what?

print("\n" . reverse(@string_array) . "\n");
没明白这句为什么会把list与scalar都reverse一遍。
举例,如果代码是这样

QUOTE:
@string_array = qw#how are you#;        

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

事实第二句的输出内容是
woherauoy
而我的理解是第二句应该输出为
uoyerawoh


~


QUOTE:
原帖由 penny_kan 于 2007-3-11 12:45 发表


print("\n" . reverse(@string_array) . "\n");
没明白这句为什么会把list与scalar都reverse一遍。
举例,如果代码是这样



事实第二句的输出内容是
woherauoy
而我的理解是第二 ...

这是因为reverse这个函数本身区分上下文环境.不同上下文返回值不同.
from perldoc -f reverse:

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

                   print reverse <>;           # line tac, last line first

                   undef $/;                   # for efficiency of <>
                   print scalar reverse <>;    # character tac, last line tsrif

受教了,谢谢