В компьютерном программировании оператор присваивания устанавливает и / или повторно устанавливает значение, хранящееся в ячейке (ах) памяти, обозначенной именем переменной ; другими словами, он копирует значение в переменную. В большинстве императивных языков программирования оператор присваивания (или выражение) является фундаментальной конструкцией.
Сегодня наиболее часто используемым обозначением для этой базовой операции стало (первоначально Superplan 1949–51, популяризированный Fortran 1957 и C ), за которым следует [1] (первоначально ALGOL 1958, популяризированный Паскалем ) [2], хотя есть многие другие используемые обозначения. В некоторых языках используемый символ рассматривается как оператор (что означает, что оператор присваивания в целом возвращает значение), в то время как другие определяют присвоение как оператор (что означает, что его нельзя использовать в выражении).x = expr
x := expr
Присваивания обычно позволяют переменной хранить разные значения в разное время в течение ее жизненного цикла и области действия . Однако некоторые языки (в основном строго функциональные ) не допускают такого «деструктивного» переназначения, так как это может означать изменения нелокального состояния. Цель состоит в том, чтобы обеспечить ссылочную прозрачность , то есть функции, которые не зависят от состояния некоторой переменной (переменных), но дают одинаковые результаты для заданного набора параметрических входных данных в любой момент времени. Современные программы на других языках также часто используют аналогичные стратегии, хотя и менее строгие, и только в определенных частях, чтобы уменьшить сложность, обычно в сочетании с дополнительными методологиями, такими как структурирование данных ,структурное программирование и объектная ориентация .
Семантика [ править ]
Операция присваивания - это процесс в императивном программировании, в котором различные значения связываются с конкретным именем переменной по прошествии времени. [1] Программа в такой модели работает, изменяя свое состояние, используя последовательные операторы присваивания. [2] [3] Примитивы императивных языков программирования полагаются на присваивание для выполнения итерации . [4] На самом низком уровне назначение реализуется с помощью машинных операций, таких как MOVE
или STORE
. [2] [4]
Переменные - это контейнеры для значений. Можно поместить значение в переменную, а затем заменить его новым. Операция присваивания изменяет текущее состояние исполняемой программы. [3] Следовательно, присвоение зависит от концепции переменных . В задании:
expression
Оценивается в текущем состоянии программы.- Объекту
variable
присваивается вычисленное значение, заменяющее предыдущее значение этой переменной.
Пример: Предполагая, что a
это числовая переменная, присвоение a := 2*a
означает, что содержимое переменной a
удваивается после выполнения оператора.
Пример сегмента кода C :
int x = 10 ; float y ; х = 23 ; y = 32,4f ;
В этом примере переменная x
сначала объявляется как int, а затем ей присваивается значение 10. Обратите внимание, что объявление и присваивание выполняются в одном операторе. Во второй строке y
объявляется без присваивания. В третьей строке x
переназначено значение 23. Наконец, y
присвоено значение 32,4.
Для операции присваивания необходимо, чтобы значение переменной былоexpression
четко определено (это действительное rvalue ) и чтобы variable
объект представлял изменяемую сущность (это допустимое изменяемое ( неконстантное ) lvalue ). В некоторых языках, обычно в динамических , нет необходимости объявлять переменную перед присвоением ей значения. В таких языках переменная автоматически объявляется при первом назначении, причем объем ее объявления зависит от языка.
Одно задание [ править ]
Любое присвоение, которое изменяет существующее значение (например x := x + 1
), запрещено в чисто функциональных языках. [4] В функциональном программировании не рекомендуется присваивание в пользу единственного присваивания, также называемого инициализацией . Одиночное присваивание является примером привязки имени и отличается от присваивания, описанного в этой статье, тем, что оно может быть выполнено только один раз, обычно при создании переменной; последующее переназначение не допускается.
Оценка выражения не имеет побочного эффекта, если она не меняет наблюдаемое состояние машины [5] и дает те же значения для того же ввода. [4] Императивное присваивание может вызвать побочные эффекты, уничтожая и делая старое значение недоступным при замене его новым, [6] и по этой причине в LISP и функциональном программировании называется деструктивным присваиванием , подобно деструктивному обновлению .
Одиночное присваивание - это единственная форма присваивания, доступная в чисто функциональных языках, таких как Haskell , которые не имеют переменных в смысле императивных языков программирования [4], а имеют именованные постоянные значения, возможно, составного характера с их элементами, постепенно определяемыми по запросу. . Чисто функциональные языки могут обеспечивать возможность параллельного выполнения вычислений , избегая узкого места фон Неймана последовательного выполнения одного шага во времени, поскольку значения не зависят друг от друга. [7]
Нечистые функциональные языки обеспечивают как одиночное присваивание, так и истинное присваивание (хотя истинное присваивание обычно используется с меньшей частотой, чем в императивных языках программирования). Например, в Scheme для всех переменных можно использовать как одиночное присваивание (с let
), так и истинное присваивание (с set!
), а для деструктивного обновления внутри списков, векторов, строк и т. Д. Предоставляются специализированные примитивы. В OCaml разрешено только одиночное присваивание. для переменных - через синтаксис; однако деструктивное обновление может использоваться для элементов массивов и строк с отдельным оператором, а также для полей записей и объектов, которые были явно объявлены изменяемыми (то есть могут быть изменены после их первоначального объявления) программистом.let name = value
<-
Функциональные языки программирования, которые используют одиночное присваивание, включают Clojure (для структур данных, а не переменных), Erlang (он принимает множественное присваивание, если значения равны, в отличие от Haskell), F # , Haskell , JavaScript (для констант), Lava , OCaml , Oz (для переменных потока данных, а не ячеек), Racket (для некоторых структур данных, таких как списки, а не символы), SASL , Scala (для vals), SISAL , Standard ML . Non- возвратами Пролог код можно считать явнымоднократное присваивание, явное в том смысле, что его (именованные) переменные могут находиться в явно неназначенном состоянии или быть установлены ровно один раз. В Haskell, напротив, не может быть неназначенных переменных, и каждая переменная может рассматриваться как неявно установленная на свое значение (или, скорее, на вычислительный объект, который будет генерировать свое значение по запросу ) при ее создании.
Ценность задания [ править ]
В некоторых языках программирования оператор присваивания возвращает значение, а в других - нет.
В большинстве языков программирования, ориентированных на выражения (например, C ), оператор присваивания возвращает присвоенное значение, допуская такие идиомы, как x = y = a
, в которых оператор присваивания y = a
возвращает значение a
, которое затем присваивается x
. В таком операторе, как , например , возвращаемое значение функции используется для управления циклом при присвоении того же значения переменной.while ((ch = getchar()) != EOF) {…}
В других языках программирования, например Scheme , возвращаемое значение присваивания не определено, и такие идиомы недействительны.
В Haskell , [8] не существует никакого присваивания переменной; но операции, подобные присваиванию (например, присвоение полю массива или полю изменяемой структуры данных) обычно оцениваются по типу единицы , который представлен как ()
. Этот тип имеет только одно возможное значение, поэтому не содержит информации. Обычно это тип выражения, который оценивается исключительно на предмет его побочных эффектов.
Варианты формы задания [ править ]
Некоторые шаблоны использования очень распространены и поэтому часто имеют специальный синтаксис для их поддержки. Это в первую очередь синтаксический сахар для уменьшения избыточности в исходном коде, но также помогает читателям кода понять намерения программиста и дает компилятору ключ к возможной оптимизации.
Дополненное задание [ править ]
Случай, когда присвоенное значение зависит от предыдущего, настолько распространен, что многие императивные языки, в первую очередь C и большинство его потомков, предоставляют специальные операторы, называемые расширенным присваиванием , например *=
, так что a = 2*a
вместо этого их можно записать как a *= 2
. [3] Помимо синтаксического сахара, это облегчает задачу компилятора, давая понять, что изменение переменной a
на месте возможно.
Привязанное задание [ править ]
Оператор типа w = x = y = z
называется цепным присваиванием, в котором значение z
присваивается нескольким переменным w, x,
и y
. Связанные назначения часто используются для инициализации нескольких переменных, как в
a = b = c = d = f = 0
Не все языки программирования поддерживают присвоение по цепочке. Связанные задания эквивалентны последовательности заданий, но стратегия оценки различается в зависимости от языка. Для простых связанных назначений, таких как инициализация нескольких переменных, стратегия оценки не имеет значения, но если цели (l-значения) в назначении каким-либо образом связаны, стратегия оценки влияет на результат.
В некоторых языках программирования ( например, C ) поддерживаются связанные присваивания, поскольку присваивания являются выражениями и имеют значения. В этом случае цепное присваивание может быть реализовано с помощью правоассоциативного присваивания , а присваивания происходят справа налево. Например, i = arr[i] = f()
эквивалентно arr[i] = f(); i = arr[i]
. В C ++ они также доступны для значений типов классов, объявляя соответствующий тип возвращаемого значения для оператора присваивания.
В Python операторы присваивания не являются выражениями и, следовательно, не имеют значения. Вместо этого связанные присваивания представляют собой серию операторов с несколькими целевыми объектами для одного выражения. Присваивания выполняются слева направо, так что i = arr[i] = f()
выражение оценивается f()
, затем присваивается результат крайнему левому целевому объекту i
, а затем присваивается тот же результат следующему целевому объекту arr[i]
, используя новое значение i
. [9] Это по сути эквивалентно тому, tmp = f(); i = tmp; arr[i] = tmp
что фактическая переменная для временного значения не создается.
Параллельное назначение [ править ]
Некоторые языки программирования, такие как APL , Common Lisp , [10] Go , [11] JavaScript (начиная с версии 1.7), PHP , Maple , Lua , occam 2 , [12] Perl , [13] Python , [14] REBOL , Ruby , [15] и PowerShell позволяют назначать несколько переменных параллельно, используя такой синтаксис:
а, б: = 0, 1
который одновременно присваивает 0 a
и 1 b
. Это чаще всего известно как параллельное присвоение ; она была введена в CPL в 1963 году под названием одновременным назначением , [16] и иногда называется множественное присваивание , хотя это заблуждение при использовании с «одним назначением», так как они не противоположны. Если правая часть присваивания представляет собой единственную переменную (например, массив или структуру), функция называется распаковкой [17] или деструктурирующим присваиванием : [18]
список переменных : = {0, 1}a, b: = список
Список будет распакован так, что 0 будет назначено, a
а 1 - b
. Более того,
а, б: = б, а
меняет местами значения a
и b
. В языках без параллельного присваивания это должно быть написано для использования временной переменной
var t: = aа: = бб: = т
поскольку a := b; b := a
оставляет оба значения a
и b
с исходным значением b
.
Некоторые языки, такие как Go и Python, объединяют параллельное присваивание, кортежи и автоматическую распаковку кортежей, чтобы разрешить несколько возвращаемых значений из одной функции, как в этом примере Python,
def f (): вернуть 1 , 2 a , b = f ()
в то время как другие языки, такие как C # , показанный здесь, требуют явного построения и деконструкции кортежей с помощью круглых скобок:
(а, б) = (б, а);
( Строка , INT ) F () => ( "Foo" , 1 ); var ( a , b ) = f ();
Это обеспечивает альтернативу использованию выходных параметров для возврата нескольких значений из функции. Это относится к CLU (1974), и CLU помогла популяризировать параллельное присваивание в целом.
C # дополнительно допускает обобщенное назначение деконструкции с реализацией, определяемой выражением с правой стороны, поскольку компилятор ищет соответствующий экземпляр или метод расширения Deconstruct
в выражении, которое должно иметь выходные параметры для присваиваемых переменных. [19] Например, один такой метод, который дал бы классу такое же поведение, как и возвращаемое значение f()
выше, будет
void Deconstruct ( out string a , out int b ) { a = "foo" ; b = 1 ; }
В C и C ++ оператор запятой аналогичен параллельному присваиванию, позволяя выполнять несколько присваиваний в одном операторе, a = 1, b = 2
вместо записи a, b = 1, 2
. Это в основном используется в циклах for и заменяется параллельным присваиванием в других языках, таких как Go. [20]
Однако приведенный выше код C ++ не гарантирует идеальной одновременности, поскольку правая часть следующего кода a = b, b = a+1
оценивается после левой. В таких языках, как Python, a, b = b, a+1
будет назначать две переменные одновременно, используя начальное значение a для вычисления нового b.
Назначение против равенства [ править ]
Использование знака равенства =
в качестве оператора присваивания часто подвергалось критике из-за конфликта с равенством в качестве сравнения на равенство. Это приводит как к замешательству новичков при написании кода, так и к замешательству даже у опытных программистов при чтении кода. Использование равных для дат назначения назад к Хайнц Ратишозер языка «s SUPERPLAN , разработанный с 1949 по 1951 год , и особенно популяризировал Fortran:
Печально известным примером плохой идеи был выбор знака равенства для обозначения назначения. Он восходит к Фортрану в 1957 году [a] и был слепо скопирован армиями разработчиков языков. Почему это плохая идея? Потому что это опровергает вековую традицию, когда «=» обозначает сравнение на равенство, предикат, который является либо истинным, либо ложным. Но в Фортране это означало назначение, обеспечение равенства. В этом случае операнды находятся в неравном положении: левый операнд (переменная) должен быть равен правому операнду (выражению). x = y не означает то же самое, что y = x. [21]
- Никлаус Вирт , Хорошие идеи, в Зазеркалье
Начинающие программисты иногда путают присваивание с оператором отношения равенства, поскольку «=» означает равенство в математике и используется для присваивания во многих языках. Но присваивание изменяет значение переменной, а проверка на равенство проверяет, имеют ли два выражения одно и то же значение.
В некоторых языках, таких как BASIC , один знак равенства ( "="
) используется как для оператора присваивания, так и для оператора отношения равенства, при этом контекст определяет, что имеется в виду. В других языках для двух операторов используются разные символы. Например:
- В ALGOL и Pascal оператор присваивания представляет собой двоеточие и знак равенства (
":="
), в то время как оператор равенства представляет собой одиночное выражение equals ("="
). - В языке C оператор присваивания - это единственный знак равенства (
"="
), а оператор равенства - это пара знаков равенства ("=="
). - В R оператор присваивания в основном
<-
такой же, как вx <- value
, но в определенных контекстах может использоваться единственный знак равенства.
Сходство двух символов может привести к ошибкам, если программист забудет, какая форма (" =
", " ==
", " :=
") подходит, или опечатает " =
", когда " ==
" было задумано. Это обычная проблема программирования с такими языками, как C (включая одну известную попытку проникнуть в ядро Linux) [22], где оператор присваивания также возвращает присвоенное значение (таким же образом, как функция возвращает значение) и может быть правильно вложенными внутри выражений. Если, например, намерение состояло в том, чтобы сравнить два значения в if
операторе, присвоение с большой вероятностью вернет значение, интерпретируемое как логическое значение true, и в этом случае then
предложение будет выполнено, что приведет к неожиданному поведению программы.Некоторые языковые процессоры (например,gcc ) может обнаруживать такие ситуации и предупреждать программиста о потенциальной ошибке.
Обозначение [ править ]
Двумя наиболее распространенными представлениями для копирования присваивания являются знак равенства ( =
) и двоеточие равно ( :=
). Обе формы могут семантически обозначают либо присваивания заявления или присвоение оператор (который также имеет значение), в зависимости от языка и / или использования.
variable = expression
Fortran , PL / I , C (и потомки, такие как C ++ , Java и т. Д.), Оболочка Bourne , Python , Go (назначение предварительно объявленным переменным), R , PowerShell , Nim и т. Д. variable := expression
ALGOL (и производные), Simula , CPL , BCPL , Pascal [23] (и потомки, такие как Modula ), Mary , PL / M , Ada , Smalltalk , Eiffel , [24] [25] Oberon , Dylan , [26] Seed7 , Python (выражение присваивания), [27] Go (сокращение для объявления и определения переменной), [28] Io , AMPL , ML , [29] AutoHotkey и т. Д.
Другие возможности включают стрелку влево или ключевое слово, хотя есть и другие, более редкие варианты:
variable << expression
Мэджик variable <- expression
F # , OCaml , R , S variable <<- expression
р assign("variable", expression)
р variable ← expression
APL , [30] Smalltalk , программирование на BASIC variable =: expression
J LET variable = expression
БАЗОВЫЙ let variable := expression
XQuery set variable to expression
AppleScript set variable = expression
Оболочка C Set-Variable variable (expression)
PowerShell variable : expression
Macsyma, Maxima , K variable: expression
Ребол var variable expression
язык сценариев mIRC reference-variable :- reference-expression
Симула
Назначения математических псевдокодов обычно обозначаются стрелкой влево.
Некоторые платформы помещают выражение слева и переменную справа:
MOVE expression TO variable
КОБОЛ expression → variable
TI-BASIC , Casio BASIC expression -> variable
ПОП-2 , БЕТА , Р put expression into variable
LiveCode PUT expression IN variable
ABC
Некоторые языки, ориентированные на выражения, такие как Lisp [31] [32] и Tcl, единообразно используют префиксный (или постфиксный) синтаксис для всех операторов, включая присваивание.
(setf variable expression)
Common Lisp (set! variable expression)
Схема [33] [34] [35] set variable expression
Tcl expression variable !
Четвертый
См. Также [ править ]
- Оператор присваивания в C ++
- Оператор (программирование)
- Привязка имени
- Унификация (вычисления)
- Неизменяемый объект
- Const-правильность
Заметки [ править ]
- ^ Использование
=
предшествовавших Fortran, хотя это было популяризировано Fortran.
Ссылки [ править ]
- ^ a b "Декларативная 2cs24" . www.csc.liv.ac.uk . Архивировано из оригинала 24 апреля 2006 года . Проверено 20 апреля 2018 года .
- ^ a b c «Императивное программирование» . uah.edu . Проверено 20 апреля 2018 года .
- ^ a b c Рюдигер-Маркус Флейг (2008). Программирование биоинформатики на Python: практический курс для начинающих . Wiley-VCH. С. 98–99. ISBN 978-3-527-32094-3. Проверено 25 декабря 2010 года .
- ^ a b c d e Переходя границы: исследуйте функциональное программирование с помощью Haskell. Архивировано 19 ноября 2010 г. на Wayback Machine , Брюс Тейт.
- ^ Митчелл, Джон С. (2003). Понятия в языках программирования . Издательство Кембриджского университета. п. 23. ISBN 978-0-521-78098-8. Проверено 3 января 2011 года .
- ^ "Императивные языки программирования (IPL)" (PDF) . gwu.edu . Проверено 20 апреля 2018 года .
- ^ Джон С. Митчелл (2003). Понятия в языках программирования . Издательство Кембриджского университета. С. 81–82. ISBN 978-0-521-78098-8. Проверено 3 января 2011 года .
- ^ Худак, Пол (2000). Школа выражения Haskell: изучение функционального программирования с помощью мультимедиа . Кембридж: Издательство Кембриджского университета. ISBN 0-521-64408-9.
- ^ «7. Простые утверждения - документация Python 3.6.5» . docs.python.org . Проверено 20 апреля 2018 года .
- ^ "CLHS: Macro SETF, PSETF" . Common Lisp Hyperspec . LispWorks . Проверено 23 апреля 2019 года .
- ^ Спецификация языка программирования Go: Назначения
- ^ ИНМОС Лимитед, изд. (1988). Справочное руководство Occam 2 . Нью-Джерси: Прентис-Холл. ISBN 0-13-629312-3.
- ^ Уолл, Ларри ; Кристиансен, Том; Шварц, Рэндал С. (1996). Язык программирования Perl (2-е изд.). Кембридж: О'Рейли. ISBN 1-56592-149-6.
- Перейти ↑ Lutz, Mark (2001). Язык программирования Python (2-е изд.). Севастополь: О'Рейли. ISBN 0-596-00085-5.
- ^ Томас, Дэвид; Хант, Эндрю (2001). Программирование на Ruby: Руководство программиста-прагматика . Река Аппер Сэдл: Эддисон Уэсли. ISBN 0-201-71089-7.
- ^ DW Barron et al. , "Основные особенности CPL", Компьютерный журнал 6 : 2: 140 (1963). полный текст (подписка)
- ^ «PEP 3132 - Расширенная итерационная распаковка» . legacy.python.org . Проверено 20 апреля 2018 года .
- ^ «Деструктурирующее задание» . Веб-документы MDN . Проверено 20 апреля 2018 года .
- ^ «Деконструкция кортежей и других типов» . Документы Microsoft . Microsoft . Проверено 29 августа 2019 .
- ^ Эффективный Go : для «Наконец, в Go нет оператора запятой, а ++ и - являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присваивание (хотя это исключает ++ и - ). "
- ↑ Никлаус Вирт. «Хорошие идеи в Зазеркалье». CiteSeerX 10.1.1.88.8309 . Отсутствует или пусто
|url=
( справка ) - ↑ Corbet (6 ноября 2003 г.). «Попытка взломать ядро» .
- ^ Мур, Лори (1980). Основы программирования на Паскале . Нью-Йорк: Джон Вили и сыновья. ISBN 0-470-26939-1.
- ^ Мейер, Бертран (1992). Эйфель-язык . Хемел Хемпстед: Prentice Hall International (Великобритания). ISBN 0-13-247925-7.
- ^ Винер, Ричард (1996). Объектно-ориентированное введение в информатику с использованием Eiffel . Река Аппер Сэдл, Нью-Джерси: Prentice Hall. ISBN 0-13-183872-5.
- ^ Файнберг, Нил; Кин, Соня Э .; Мэтьюз, Роберт О .; Витингтон, П. Такер (1997). Дилан Программирование . Массачусетс: Эддисон Уэсли. ISBN 0-201-47976-1.
- ^ «PEP 572 - Выражения присваивания» . python.org . 28 февраля 2018 . Дата обращения 4 марта 2020 .
- ^ «Спецификация языка программирования Go - язык программирования Go» . golang.org . Проверено 20 апреля 2018 года .
- ^ Ульман, Джеффри Д. (1998). Элементы программирования машинного обучения: издание ML97 . Энглвуд Клиффс, Нью-Джерси: Прентис Холл. ISBN 0-13-790387-1.
- ^ Айверсон, Кеннет Э. (1962). Язык программирования . Джон Вили и сыновья. ISBN 0-471-43014-5. Архивировано из оригинала на 2009-06-04 . Проверено 9 мая 2010 .
- ^ Грэм, Пол (1996). ANSI Common Lisp . Нью-Джерси: Прентис-Холл. ISBN 0-13-370875-6.
- ^ Стил, Гай Л. (1990). Common Lisp: язык . Лексингтон: Цифровая пресса. ISBN 1-55558-041-6.
- ^ Дибвиг, Р. Кент (1996). Язык программирования схемы: Схема ANSI . Нью-Джерси: Прентис-Холл. ISBN 0-13-454646-6.
- ^ Смит, Джерри Д. (1988). Введение в схему . Нью-Джерси: Прентис-Холл. ISBN 0-13-496712-7.
- ^ Абельсон, Гарольд; Сассман, Джеральд Джей; Суссман, Джули (1996). Структура и интерпретация компьютерных программ . Нью-Джерси: Макгроу-Хилл. ISBN 0-07-000484-6.