порядок компиляции и операторы почтового префикса

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

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

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

Спасибо,


person snoofkin    schedule 15.05.2013    source источник


Ответы (1)


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


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

  1. "%d %d %d %d"
  2. $a
  3. ++$a
  4. $a++
  5. $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

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

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

person ikegami    schedule 15.05.2013
comment
(Ну, вы еще не можете сделать &CORE::printf, но вы поняли идею.) - person ikegami; 15.05.2013
comment
Благодарность! так что в основном это потому, что printf использует псевдонимы, а не сдвигает каждое значение? - person snoofkin; 15.05.2013
comment
Каждый подвызов использует псевдонимы, поэтому не имеет значения, сдвигает ли printf значения или нет. К моменту вызова printf $a уже содержит 7. Замените printf своим собственным сабвуфером, если хотите поэкспериментировать. - person ikegami; 15.05.2013
comment
@soulSurfer2010 Дело даже не только в подписках: то же самое происходит, например, с назначением. Проверьте @b после my @b = ($a, ++$a, $a++, $a);, это тоже 7767. ) - person raina77ow; 15.05.2013