Присваивание (информатика)

редактировать

В компьютерном программировании оператор присваивания устанавливает и / или повторно -задает значение , хранящееся в ячейках памяти, обозначенных переменной именем ; другими словами, он копирует значение в переменную. В большинстве императивных языков программирования оператор присваивания (или выражение) является фундаментальной конструкцией.

Сегодня наиболее часто используемым обозначением для этой базовой операции стало x = expr(первоначально Superplan 1949–51, популяризировано Fortran 1957 и C ), за которым следует x: = expr(первоначально АЛГОЛ 1958, популяризированный Паскалем ), хотя есть много другие используемые обозначения. В некоторых языках используемый символ рассматривается как оператор (что означает, что оператор присваивания в целом возвращает значение), в то время как другие определяют присвоение как оператор (что означает, что его нельзя использовать в выражении).

Назначения обычно позволяют переменной сохранять разные значения в разное время в течение ее жизненного цикла и области действия. Однако некоторые языки (в основном строго функциональный ) не допускают такого «деструктивного» переназначения, так как это может означать изменения нелокального состояния. Цель состоит в том, чтобы обеспечить ссылочную прозрачность, то есть функции, которые не зависят от состояния некоторой переменной (переменных), но производят одинаковые результаты для заданного набора параметрических входных данных в любой момент времени. Современные программы на других языках также часто используют аналогичные стратегии, хотя и менее строгие, и только в определенных частях, чтобы уменьшить сложность, обычно в сочетании с дополнительными методологиями, такими как структурирование данных, структурное программирование и ориентация объекта.

Содержание

  • 1 Семантика
  • 2 Одно присваивание
  • 3 Значение присваивания
  • 4 Вариантные формы присваивания
    • 4.1 Расширенное присваивание
    • 4.2 Связанное присваивание
    • 4.3 Параллельное присваивание
  • 5 Присваивание или равенство
  • 6 Обозначение
  • 7 См. также
  • 8 Примечания
  • 9 Ссылки

Семантика

Операция присваивания - это процесс в императивном программировании, в котором различные значения связаны с конкретным именем переменной по прошествии времени. Программа в такой модели работает, изменяя свое состояние, используя последовательные операторы присваивания. Примитивы императивных языков программирования полагаются на присваивание для выполнения итерации. На самом низком уровне назначение осуществляется с помощью машинных операций, таких как MOVEили STORE.

Переменные являются контейнерами для значений. Можно поместить значение в переменную, а затем заменить его новым. Операция присваивания изменяет текущее состояние исполняемой программы. Следовательно, присвоение зависит от концепции переменных. В присвоении:

  • выражение оценивается в текущем состоянии программы.
  • Переменной назначается вычисленное значение, заменяющее предыдущее значение эту переменную.

Пример: Предполагая, что aявляется числовой переменной, присвоение a: = 2 * aозначает, что содержимое переменной aудваивается после выполнения оператора.

Пример сегмента кода C :

int x = 10; float y; х = 23; y = 32,4f;

В этом примере переменная xсначала объявляется как int, а затем ей присваивается значение 10. Обратите внимание, что объявление и присваивание выполняются в одном операторе. Во второй строке yобъявлен без присваивания. В третьей строке xповторно присваивается значение 23. Наконец, yприсваивается значение 32,4.

Для операции присваивания необходимо, чтобы значение выражениябыло четко определено (это действительное rvalue ) и чтобы переменная представляет изменяемую сущность (это допустимое изменяемое (не const ) lvalue ). В некоторых языках, обычно динамических, нет необходимости объявлять переменную до присвоения ей значения. В таких языках переменная автоматически объявляется при первом назначении, а объем ее объявления зависит от языка.

Одиночное присвоение

Любое присвоение, изменяющее существующее значение (например, x: = x + 1), запрещено в чисто функциональных языках. В функциональном программировании назначение не рекомендуется в пользу однократного назначения, также называемого инициализацией. Одиночное присвоение является примером привязки имени и отличается от присвоения, описанного в этой статье, тем, что оно может быть выполнено только один раз, обычно при создании переменной; последующее переназначение не допускается.

Оценка выражения не имеет побочного эффекта , если она не изменяет наблюдаемое состояние машины и дает те же значения для того же ввода. Императивное присвоение может привести к побочным эффектам при разрушении и создании недоступности старого значения при замене его новым, поэтому в LISP и функциональное программирование оно называется деструктивным присвоением, аналогично деструктивному обновлению.

Одиночное присваивание - единственная форма присваивания, доступная в чисто функциональных языках, таких как Haskell, которые не имеют переменных в смысле императивных языков программирования, а имеют именованные константы ценности, возможно, комплексного характера с их элементами, постепенно определяемыми по запросу. Чисто функциональные языки могут обеспечить возможность параллельного выполнения вычислений , избегая узкого места фон Неймана последовательного выполнения одного шага во времени, поскольку значения не зависят друг от друга.

Нечеткие функциональные языки обеспечивают как одиночное присваивание, так и истинное присваивание (хотя истинное присваивание обычно используется с меньшей частотой, чем в императивных языках программирования). Например, в Scheme для всех переменных можно использовать как одиночное присвоение (с let), так и истинное присвоение (с set!), а для деструктивного обновления внутри списков предусмотрены специализированные примитивы., векторы, строки и т. д. В OCaml для переменных разрешено только одиночное присваивание с помощью синтаксиса let name = value; однако деструктивное обновление может использоваться для элементов массивов и строк с отдельным оператором <-, а также для полей записей и объектов, которые были явно объявлены изменяемыми (что означает возможность изменения после их первоначального объявления) программистом.

Функциональные языки программирования, использующие одиночное присваивание, включают Clojure (для структур данных, а не переменных), Erlang (допускает множественное присваивание, если значения равны, в отличие от в Haskell), F#, Haskell, Lava, OCaml, Oz (для переменных потока данных, а не ячеек), Racket (для некоторых структур данных, например списков, а не символы), SASL, Scala (для vals), SISAL, Standard ML. Код без с возвратом Prolog можно рассматривать как явное однократное присваивание, явное в том смысле, что его (именованные) переменные могут находиться в явно неназначенном состоянии или устанавливаться ровно один раз. В Haskell, напротив, не может быть неназначенных переменных, и каждая переменная может рассматриваться как неявно установленная на свое значение (или, скорее, на вычислительный объект, который будет генерировать свое значение по запросу) при ее создании.

Значение присваивания

В некоторых языках программирования оператор присваивания возвращает значение, а в других - нет.

В большинстве языков программирования, ориентированных на выражения (например, C ), оператор присваивания возвращает присвоенное значение, допуская такие идиомы, как x = y = a, в котором оператор присваивания y = aвозвращает значение a, которое затем присваивается x. В таком операторе, как while ((ch = getchar ())! = EOF) {…}, возвращаемое значение функции используется для управления циклом при присвоении того же значения переменной.

В других языках программирования, например Схема, возвращаемое значение присваивания не определено, и такие идиомы недопустимы.

В Haskell нет назначения переменных; но операции, аналогичные присваиванию (например, присвоение полю массива или полю изменяемой структуры данных) обычно оцениваются по типу блока , который представлен как (). Этот тип имеет только одно возможное значение, поэтому не содержит информации. Обычно это тип выражения, который оценивается исключительно на предмет его побочных эффектов.

Вариантные формы присвоения

Определенные шаблоны использования очень распространены и поэтому часто имеют специальный синтаксис для их поддержки. Это в первую очередь синтаксический сахар для уменьшения избыточности в исходном коде, но также помогает читателям кода понять намерения программиста и дает компилятору ключ к возможной оптимизации.

Расширенное присвоение

Случай, когда присвоенное значение зависит от предыдущего, настолько распространен, что многие императивные языки, в первую очередь C и большинство его потомков, предоставляют специальные операторы, называемые расширенным присваиванием, например * =, поэтому a = 2 * aвместо этого можно записать как a * = 2. Помимо синтаксического сахара, это упрощает задачу компилятора, давая понять, что возможна модификация на месте переменной 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. По сути, это эквивалент tmp = f (); я = tmp; arr [i] = tmp, хотя фактическая переменная для временного значения не создается.

Параллельное присвоение

Некоторые языки программирования, такие как APL, Common Lisp,Go,JavaScript (начиная с версии 1.7), PHP, Maple, Lua, occam 2, Perl, Python, REBOL, Ruby и PowerShell позволяют назначать несколько переменных параллельно с синтаксисом вида:

a, b: = 0, 1

который одновременно присваивает 0 aи 1 b. Это чаще всего известно как параллельное присвоение ; он был введен в CPL в 1963 году под названием одновременное присвоение и иногда называется множественное присвоение, хотя это сбивает с толку при использовании с «одиночным назначением», поскольку это не противоположности. Если правая часть присвоения представляет собой единственную переменную (например, массив или структуру), функция называется распаковкой или деструктуризацией присвоения :

var list: = {0, 1} a, b: = list

Список будет распакован так, что 0 будет присвоен a, а 1 - b. Кроме того,

a, b: = b, a

меняет местами значения aи b. В языках без параллельного присвоения это должно быть написано для использования временной переменной

var t: = a a: = b b: = t

, поскольку a: = b; b: = aоставляет и a, и bс исходным значением b.

. Некоторые языки, такие как Go и Python, сочетают параллельное присваивание, кортежи и автоматическое распаковка кортежа, чтобы разрешить несколько возвращаемых значений из одной функции, как в этом примере Python,

def f (): return 1, 2 a, b = f ()

в то время как другие языки, такие как поскольку C #, показанный здесь, требует явного построения и деконструкции кортежа в круглых скобках:

(a, b) = (b, a);
(строка, число) f () =>("foo", 1); var (a, b) = f ();

Это обеспечивает альтернативу использованию выходных параметров для возврата нескольких значений из функции. Это относится к CLU (1974), и CLU помогло популяризировать параллельное присваивание в целом.

C # дополнительно допускает обобщенное назначение деконструкции с реализацией, определяемой выражением в правой части, поскольку компилятор ищет соответствующий экземпляр или extension Деконструировать методдля выражения, которое должно иметь выходные параметры для присваиваемых переменных. Например, один из таких методов, который дал бы классу , который проявляется в том же поведении, что и возвращаемое значение f ()выше, будет

void Deconstruct (out string a, out int b) {a = "foo"; b = 1; }

В C и C ++ оператор запятой аналогичен параллельному присваиванию, позволяя выполнять несколько присваиваний в пределах одного оператора, записывая a = 1, b = 2вместо а, b = 1, 2. Это в основном используется в для циклов и заменяется параллельным присваиванием в других языках, таких как Go. Однако приведенный выше код C ++ не гарантирует идеальной одновременности, поскольку правая часть следующего кода a = b, b = a + 1оценивается после левой стороны. В таких языках, как Python, a, b = b, a + 1будет назначать две переменные одновременно, используя начальное значение a для вычисления нового b.

Присваивание против равенства

Использование знака равенства =в качестве оператора присваивания часто подвергалось критике из-за конфликта с равенством в качестве сравнения для равенства. Это приводит как к замешательству новичков в написании кода, так и к замешательству даже у опытных программистов при чтении кода. Использование равенства для присваивания восходит к языку Хайнца Рутисхаузера Суперплан, разработанному с 1949 по 1951 год и особенно популяризированному Фортраном:

Печально известный пример плохого Идея заключалась в выборе знака равенства для обозначения присвоения. Он восходит к Фортрану в 1957 году и был слепо скопирован армиями разработчиков языков. Почему это плохая идея? Потому что это опровергает вековую традицию, когда «=» обозначает сравнение на равенство, предикат, который является либо истинным, либо ложным. Но в Фортране это означало назначение, обеспечение равенства. В этом случае операнды находятся в неравном положении: левый операнд (переменная) должен быть равен правому операнду (выражению). x = y не означает то же самое, что y = x.

Никлаус Вирт, Хорошие идеи, в Зазеркалье

Начинающие программисты иногда путают назначение с оператором отношения для равенства, поскольку "=" означает равенство в математике и используется для присваивания во многих языках. Но присваивание изменяет значение переменной, а проверка на равенство проверяет, имеют ли два выражения одно и то же значение.

В некоторых языках, таких как BASIC, один знак равенства ("=") используется как для оператора присваивания, так и для оператора отношения равенства с контекстом определение того, что имеется в виду. В других языках для двух операторов используются разные символы. Например:

  • В ALGOL и Pascal оператор присваивания представляет собой двоеточие и знак равенства (": ="), а оператор равенства - одинарное равенство ("=").
  • В C оператор присваивания представляет собой одиночный знак равенства ("="), а оператор равенства представляет собой пару равных знаки ("==").
  • В R оператор присваивания в основном <-, как в x <- value, но в определенных контекстах может использоваться единственный знак равенства.

Сходство двух символов может привести к ошибкам, если программист забудет, в какой форме ("=", "==", ": =") уместно, или ошибочно набирает "=", когда предполагалось "==". Это общая проблема программирования с такими языками, как C (включая одну известную попытку бэкдора в ядре Linux), где оператор присваивания также возвращает присвоенное значение (так же, как функция возвращает значение), и может быть правильно вложен в выражения. Если целью было сравнение двух значений в оператор if, например, присвоение с большой вероятностью вернет значение, интерпретируемое как логическое значение true, и в этом случае будет выполнено предложение then, что приведет к неожиданному поведению программы. Некоторые языковые процессоры (например, gcc ) могут обнаруживать такие ситуации и предупреждать программиста о потенциальной ошибке.

Обозначение

Двумя наиболее распространенными представлениями для копирующего присвоения являются знак равенства (=) и двоеточие равно (: =). Обе формы могут семантически обозначать либо оператор присваивания, либо оператор присваивания (который также имеет значение), в зависимости от языка и / или использования.

переменная = выражениеFortran, PL / I, C (и потомки, такие как C ++, Java, и т. д.), оболочка Борна, Python, Go (присвоение предварительно объявленным переменным), R, PowerShell и т. д.
переменная: = выражениеАЛГОЛ (и производные), Simula, CPL, BCPL, Pascal (и потомки, такие как Modula ), Мэри, PL / M, Ада, Smalltalk, Эйфель, Оберон, Дилан, Seed7, Python (выражение присваивания), Go (сокращение для объявления и определения переменной), Io, AMPL, ML,AutoHotkey и т. Д.

Другие возможности включают стрелку влево или ключевое слово, хотя есть и другие, более редкие варианты:

переменная << expressionMagik
переменная <- expressionF#, OCaml, R, S
переменная <<- expressionR
assign ("переменная", выражение)R
переменная ← выражениеAPL, Smalltalk, BASIC Programming
переменная =: выражениеJ
переменная LET = выражениеBASIC
let variable: = expressionXQuery
установить переменную в выражениеAppleScript
установить переменную = выражениеоболочка C
Set-Variable переменная (выражение)PowerShell
переменная: выражениеMacsyma, Maxima, Rebol, K
var переменная выражениеязык сценариев mIRC
ссылочная-переменная: - ссылочное-выражениеSimula

математический присвоения псевдокода обычно обозначаются стрелкой влево.

Некоторые платформы помещают выражение слева и переменную справа:

MOVE выражение TO переменнаяCOBOL
выражение → переменнаяTI-BASIC, Casio BASIC
выражение ->переменнаяPOP-2, BETA, R
поместить выражение в переменнуюLiveCode

Некоторые языки, ориентированные на выражения, такие как Lisp и Tcl единообразно используют префиксный (или постфиксный) синтаксис для всех операторов, включая присваивание.

(setf variable expression)Common Lisp
(set! Variable expression)Схема
установить выражение переменнойTcl
выражение переменной!Forth

См. также

Примечания

Ссылки

Последняя правка сделана 2021-06-12 01:03:21
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте