выполняя программу на 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 в обоих случаях.





Сначала выполняется автоинкремент, оставляя $i + $i++ равным 3 + 2.
Вы не должны использовать подобные выражения, если семантика неясна. Вы должны разделить расчет на несколько операторов.
@EugenKonkov смешно, я ожидал либо 7, либо 9. Но да, волосатый и не то, с чем стоит играть :)
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
Обратите внимание, что это противоречит предыдущий ответ. Его объяснение того, что происходит, полностью неверно и опровергается $i + $i++ + $i++.
Это сделано из соображений производительности. Создание копии скаляра стоит дорого, и выполнение этого для каждого скаляра, помещенного в стек, будет иметь серьезное негативное влияние на производительность.
Почему, когда у нас есть значение в определенной точке, Perl не помещает его в стек вместо скаляра? Было бы правильнее?
Я имею в виду, что мы можем поместить копию скаляра со значением в это время. и не помещайте исходный скаляр снова и снова.
Изготовление копии - дорогое удовольствие. Копирование всего, что помещено в стек, может серьезно повлиять на производительность. Вместо этого языки не определяют точную семантику для такого кода, как ваш. [Этот добавленный комментарий был включен в мой ответ.]
И нет, «правильнее» не было бы. Для любого подхода нет математической основы.
«каждый» - многовато будет. Просто поместите копию для изменяемого скаляра (операции присваивания, увеличения, уменьшения). Несмотря на отсутствие математической основы, было бы интересно этим заняться. Я должен попробовать это! =) Спасибо
да .... Perl не определяет, когда переменная увеличивается или уменьшается:
my $i = 1; $i += $i + $i++ + $i++; print "$i\n". Результат: 8