В компьютерном программировании оператор присваивания устанавливает и / или повторно устанавливает значение, хранящееся в ячейке (ах) памяти, обозначенной именем переменной ; другими словами, он копирует значение в переменную. В большинстве императивных языков программирования оператор присваивания (или выражение) является фундаментальной конструкцией.
Сегодня наиболее часто используемым обозначением для этой базовой операции стало (первоначально 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- возвратами Пролог код можно считать явным одним присваивание, явным в том смысле , что его (названные) переменные могут быть явно Unassigned состояние, или быть установлен ровно один раз. В 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-правильность
Заметки
- ^ Использование
=
Фортрана, предшествовавшего ему, хотя он был популяризирован Фортраном.
Рекомендации
- ^ a b "Декларативная 2cs24" . www.csc.liv.ac.uk . Архивировано из оригинала 24 апреля 2006 года . Проверено 20 апреля 2018 года .
- ^ а б в «Императивное программирование» . uah.edu . Проверено 20 апреля 2018 года .
- ^ а б в Рюдигер-Маркус Флейг (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.
- ^ Лутц, Марк (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=
( справка ) - ^ Корбет (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). Элементы программирования ML: издание 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.