Почему результаты выражения разные?

выполняя программу на perl, я получаю разные результаты:

$ perl -e 'my $i = 2; $i += 2 + $i++; print "$i\n"'
7
$ perl -e 'my $i = 2; $i += $i + $i++; print "$i\n"'
8

Почему результаты разные? что я упустил во втором случае? Я ожидаю 7 в обоих случаях.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сначала выполняется автоинкремент, оставляя $i + $i++ равным 3 + 2.

Вы не должны использовать подобные выражения, если семантика неясна. Вы должны разделить расчет на несколько операторов.

да .... Perl не определяет, когда переменная увеличивается или уменьшается: my $i = 1; $i += $i + $i++ + $i++; print "$i\n". Результат: 8

Eugen Konkov 01.04.2018 13:21

@EugenKonkov смешно, я ожидал либо 7, либо 9. Но да, волосатый и не то, с чем стоит играть :)

hobbs 01.04.2018 23:46
Ответ принят как подходящий

Perl не гарантирует какой-либо конкретный результат ни для одного из опубликованных вами фрагментов, и вам следует избегать такого кода.


Тем не менее, поведение одинаково для всех существующих сборок Perl.

Хотя это не задокументировано и не гарантировано, Perl всегда оценивает левую часть (LHS) операторов сложения перед их правой частью (RHS).

$ perl -MO=Concise,-exec -e 'my $i = 2; $i += $i + $i++; print "$i\n"'
...
8  <0> padsv[$i:1,2] s       > LHS
9  <0> padsv[$i:1,2] sRM     \ RHS
a  <1> postinc[t2] sK/1      /
b  <2> add[t3] sK/2
...

Так почему же кажется, что это не так?

Ключ к пониманию того, что происходит, заключается в том, что стек Perl содержит только скаляры (SV*, включая подтипы, такие как AV*). Это означает, что $i помещает в стек фактический скаляр, связанный с $i, а не просто значение 2.[1].

Это означает, что даже если $i оценивается и помещается в стек до того, как $i++ оценивается и помещается в стек, обновленное значение $i будет использоваться оператором сложения.

    $i  stack
    --  -----
     2
$i
     2  $i
$i
     2  $i,$i
postinc
     3  $i,2
add
     3  5

При желании вы можете отследить, что происходит с репликацией интерпретатора Perl:

use Data::Alias qw( alias );

my @stack;
my @pad;

sub padsv {
   my $padidx = shift;
   alias push @stack, $pad[$padidx];
}

sub postinc {
   alias my $sv = pop(@stack);
   alias my $result = $sv++;
   alias push @stack, $result;
}

sub add {
   alias my ($lhs, $rhs) = splice(@stack, -2);
   alias my $result = $lhs + $rhs;
   alias push @stack, $result;
}


{ my $i = 2; say $i + $i++; }  # 5

$pad[0] = 2;
padsv(0); padsv(0); postinc(); add();
say pop(@stack);  # 5


{ my $i = 1; say $i + $i++ + $i++; }  # 5

$pad[0] = 1;
padsv(0); padsv(0); postinc(); add(); padsv(0); postinc(); add();
say pop(@stack);  # 5

Альтернатива:

sub postinc :lvalue { $_[0]++ }
sub add :lvalue { $_[0] + $_[1] }

{ my $i = 2; say $i + $i++;            } # 5
{ my $i = 2; say add($i, postinc($i)); } # 5

{ my $i = 1; say $i + $i++ + $i++;                       } # 5
{ my $i = 1; say add(add($i, postinc($i)), postinc($i)); } # 5

  1. Обратите внимание, что это противоречит предыдущий ответ. Его объяснение того, что происходит, полностью неверно и опровергается $i + $i++ + $i++.

  2. Это сделано из соображений производительности. Создание копии скаляра стоит дорого, и выполнение этого для каждого скаляра, помещенного в стек, будет иметь серьезное негативное влияние на производительность.

Почему, когда у нас есть значение в определенной точке, Perl не помещает его в стек вместо скаляра? Было бы правильнее?

Eugen Konkov 01.04.2018 23:21

Я имею в виду, что мы можем поместить копию скаляра со значением в это время. и не помещайте исходный скаляр снова и снова.

Eugen Konkov 01.04.2018 23:34

Изготовление копии - дорогое удовольствие. Копирование всего, что помещено в стек, может серьезно повлиять на производительность. Вместо этого языки не определяют точную семантику для такого кода, как ваш. [Этот добавленный комментарий был включен в мой ответ.]

ikegami 01.04.2018 23:41

И нет, «правильнее» не было бы. Для любого подхода нет математической основы.

ikegami 02.04.2018 00:23

«каждый» - многовато будет. Просто поместите копию для изменяемого скаляра (операции присваивания, увеличения, уменьшения). Несмотря на отсутствие математической основы, было бы интересно этим заняться. Я должен попробовать это! =) Спасибо

Eugen Konkov 02.04.2018 09:15

Другие вопросы по теме