Это не стенография.
+=
Символ появился на языке C в 1970 - х годах, а также - с идеей C «умного ассемблера» соответствует четко другому режиму команд машины и адресации:
Такие вещи, как " i=i+1
", "i+=1
"и" ++i
", хотя и на абстрактном уровне производят одинаковый эффект, на низком уровне соответствуют другому способу работы процессора.
В частности, эти три выражения, предполагая, что i
переменная находится в адресе памяти, хранящемся в регистре процессора (назовем его D
- воспринимайте его как «указатель на int»), а ALU процессора принимает параметр и возвращает результат в виде «Аккумулятор» (назовем его A - думайте как int).
С этими ограничениями (очень распространенными во всех микропроцессорах того периода) перевод, скорее всего, будет
;i = i+1;
MOV A,(D); //Move in A the content of the memory whose address is in D
ADD A, 1; //The addition of an inlined constant
MOV (D) A; //Move the result back to i (this is the '=' of the expression)
;i+=1;
ADD (D),1; //Add an inlined constant to a memory address stored value
;++i;
INC (D); //Just "tick" a memory located counter
Первый способ сделать это неоптимален, но он более общий при работе с переменными вместо константы ( ADD A, B
или ADD A, (D+x)
) или при переводе более сложных выражений (все они сводятся к операции push с низким приоритетом в стеке, вызывают высокий приоритет, pop и повторять, пока все аргументы не будут устранены).
Второй тип более типичен для «конечного автомата»: мы больше не «вычисляем выражение», а «работаем со значением»: мы все еще используем ALU, но избегаем перемещения значений, поскольку в результате можно заменить параметр. Этот вид инструкций не может использоваться, когда требуется более сложное выражение: i = 3*i + i-2
его нельзя использовать на месте, поскольку i
требуется больше раз.
Третий, даже более простой, даже не рассматривает идею «сложения», но использует более «примитивную» (в вычислительном смысле) схему для счетчика. Инструкция закорочена, загружается быстрее и выполняется немедленно, поскольку комбинаторная сеть, необходимая для модификации регистра, чтобы сделать его счетчиком, меньше и, следовательно, быстрее, чем у полного сумматора.
С современными компиляторами (см. C, к настоящему времени), позволяющими оптимизировать компилятор, соответствие может быть изменено в зависимости от удобства, но в семантике все еще есть концептуальное различие.
x += 5
означает
- Найдите место, обозначенное х
- Добавьте 5 к этому
Но x = x + 5
значит:
- Оценить х + 5
- Найдите место, обозначенное х
- Скопируйте х в аккумулятор
- Добавьте 5 к аккумулятору
- Сохранить результат в х
- Найдите место, обозначенное х
- Скопируйте аккумулятор к нему
Конечно, оптимизация может
- если «поиск x» не имеет побочных эффектов, два «поиска» можно выполнить один раз (и x станет адресом, хранящимся в регистре указателей)
- две копии могут быть опущены, если
&x
вместо аккумулятора применяется аккумулятор
таким образом, оптимизированный код должен совпадать с x += 5
одним.
Но это можно сделать только в том случае, если «поиск х» не имеет побочных эффектов, в противном случае
*(x()) = *(x()) + 5;
а также
*(x()) += 5;
семантически различаются, поскольку x()
побочные эффекты (признание x()
- это функция, выполняющая странные вещи и возвращающая int*
), будут создаваться дважды или один раз.
Следовательно, эквивалентность между x = x + y
и x += y
обусловлена частным случаем, когда +=
и =
применяются к прямому l-значению.
Чтобы перейти на Python, он унаследовал синтаксис от C, но так как нет перевода / оптимизации ДО выполнения на интерпретируемых языках, вещи не обязательно так тесно связаны (так как есть еще один шаг разбора). Однако интерпретатор может ссылаться на разные подпрограммы выполнения для трех типов выражений, используя преимущества различного машинного кода в зависимости от того, как сформировано выражение и от контекста оценки.
Для тех, кто любит более подробно ...
Каждый ЦП имеет АЛУ (арифметико-логический блок), который по своей сути является комбинаторной сетью, чьи входы и выходы «подключены» к регистрам и / или памяти в зависимости от кода операции инструкции.
Двоичные операции обычно реализуются как «модификатор регистра накопителя с вводом, взятым« где-то », где где-то может быть - внутри самого потока команд (типично для константы манифеста: ADD A 5) - внутри другого реестра (типично для вычисления выражения с временные значения: например, ADD AB) - внутри памяти, по адресу, заданному регистром (типично для выборки данных, например: ADD A (H)) - H, в этом случае работает как указатель разыменования.
С этим псевдокодом, x += 5
является
ADD (X) 5
в то время как x = x+5
это
MOVE A (X)
ADD A 5
MOVE (X) A
То есть x + 5 дает временное значение, которое назначается позже. x += 5
работает непосредственно на х.
Фактическая реализация зависит от реального набора команд процессора: если нет ADD (.) c
кода операции, первый код становится вторым: нет пути.
Если есть такой код операции, и оптимизация включена, второе выражение, после устранения обратных движений и корректировки регистра кода операции, становится первым.
x += 5
чемx = x + 5
? Или это действительно просто синтаксический сахар, как вы предлагаете?