Операторы компиляции и постфиксные префиксы

Мне было интересно, почему следующие выходы 7 7 6 7 вместо 5 6 6 7

my $a = 5;
printf("%d %d %d %d",$a,++$a , $a++ , $a);

Я уверен, что он имеет какое-то отношение к порядку компиляции параметров

Спасибо,

+7
источник поделиться
1 ответ

Прежде чем начать, позвольте мне указать, что обычно следует избегать ситуаций, когда вы оба устанавливаете и читаете переменную в выражении.


Сначала рассмотрим порядок оценки операндов. Это не определено для многих операторов, но оно определено для оператора списка. Он документировал для оценки своих операндов в порядке слева направо [1]. Это означает, что аргументы printf оцениваются в следующем порядке:

  • "%d %d %d %d"
  • $a
  • ++$a
  • $a++
  • $a

Ключ заключается в понимании того, что $a не помещает копию стопа $a в стек. Он ставит сам скаляр (a SV*, в терминах C). В Perl-жаргоне мы говорим, что элемент стека имеет псевдоним $a [2]. В теории вычислений вы бы сказали, что аргументы передаются по ссылке.

И то же самое относится к ++$a, но $a++ обязательно помещает копию $a в стек.

Это означает, что мы можем рассматривать вышеуказанный вызов printf как эквивалентный

use Data::Alias qw( alias );

{
    local @_;
    alias $_[0] = "%d %d %d %d";
    alias $_[1] = $a;    # Places $a on the stack.
    alias $_[2] = ++$a;  # Adds one to $a and places $a on the stack.
    alias $_[3] = $a++;  # Places a copy of $a on the stack and adds one to $a.
    alias $_[4] = $a;    # Places $a on the stack.
    &CORE::printf;
 }

К тому времени, когда вызывается $a++, $a содержит 6..

К моменту появления printf $a содержит 7.


Обходной путь состоит в том, чтобы сделать копии значений.

$ perl -le'$a = 5; my @b = ($a, ++$a, $a++, $a); print "@b";'
7 7 6 7

$ perl -le'$a = 5; my @b = (0+$a, 0+(++$a), $a++, $a); print "@b";'
5 6 6 7

  • Из perlop," В контексте списка это просто разделитель аргументов списка и вставляет оба его аргумента в список. Эти аргументы также оцениваются слева направо.

  • Из perlsyn, "Любые аргументы, переданные в массиве @_, поэтому, если вы вызвали функция с двумя аргументами, они будут храниться в $_[0] и $_[1]. Массив @_ является локальным массивом, но его элементы являются псевдонимами для реальных скалярных параметров."

+14
источник

Посмотрите другие вопросы по меткам или Задайте вопрос