Задание (информатика) - Assignment (computer science)

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

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

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

Семантика

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

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

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

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

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

int x = 10; 
float y;
x = 23;
y = 32.4f;

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

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

Однократное присвоение

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

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

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

Нечистые функциональные языки обеспечивают как одиночное присваивание, так и истинное присваивание (хотя истинное присваивание обычно используется с меньшей частотой, чем в императивных языках программирования). Например, в 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 нет присваивания переменных; но операции, подобные присваиванию (например, присвоение полю массива или полю изменяемой структуры данных) обычно оцениваются по типу единицы , который представлен как (). Этот тип имеет только одно возможное значение, поэтому не содержит информации. Обычно это тип выражения, который оценивается исключительно на предмет его побочных эффектов.

Варианты формы уступки

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

Дополненное задание

Случай, когда присвоенное значение зависит от предыдущего, настолько распространен, что многие императивные языки, в первую очередь 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(); i = 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);
(string, int) f() => ("foo", 1);
var (a, b) = f();

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

C # дополнительно допускает обобщенное назначение деконструкции с реализацией, определяемой выражением с правой стороны, поскольку компилятор ищет соответствующий экземпляр или метод расширения Deconstruct в выражении, которое должно иметь выходные параметры для присваиваемых переменных. Например, один такой метод, который дал бы классу такое же поведение, как и возвращаемое значение 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. Однако приведенный выше код C ++ не гарантирует идеальной одновременности, поскольку правая часть следующего кода a = b, b = a+1оценивается после левой. В таких языках, как Python, a, b = b, a+1будет назначать две переменные одновременно, используя начальное значение a для вычисления нового b.

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

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

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

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

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

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

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

Сходство двух символов может привести к ошибкам, если программист забудет, какая форма (" =", " ==", " :=") подходит, или опечатает " =", когда " ==" было задумано. Это обычная проблема программирования с такими языками, как C (включая одну известную попытку бэкдора ядра Linux), где оператор присваивания также возвращает присвоенное значение (так же, как функция возвращает значение), и может быть правильно вложен внутренние выражения. Если, например, намерение состояло в том, чтобы сравнить два значения в 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 (и потомки, такие как Modula ), Mary , PL / M , Ada , Smalltalk , Eiffel , Oberon , Dylan , Seed7 , Python (выражение присваивания), Go (сокращение для объявление и определение переменной), Io , AMPL , ML , AutoHotkey и т. д.

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

variable << expression Мэджик
variable <- expression F # , OCaml , R , S
variable <<- expression р
assign("variable", expression) р
variableexpression APL , Smalltalk , базовое программирование
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 КОБОЛ
expressionvariable TI-BASIC , Casio BASIC
expression -> variable ПОП-2 , БЕТА , Р
put expression into variable LiveCode
PUT expression IN variable ABC

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

(setf variable expression) Common Lisp
(set! variable expression) Схема
set variable expression Tcl
expression variable ! Четвертый

Смотрите также

Примечания

использованная литература