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

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

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

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

Семантика

Операция присваивания - это процесс в императивное программирование в котором различные значения связаны с определенным именем переменной с течением времени.[1] Программа в такой модели работает, изменяя свое состояние, используя последовательные операторы присваивания.[2][3] Примитивы императивных языков программирования полагаются на назначение для выполнения итерация.[4] На самом низком уровне присвоение осуществляется с помощью машинные операции Такие как ДВИГАТЬСЯ или же ХРАНИТЬ.[2][4]

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

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

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

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

int Икс = 10; плавать у;Икс = 23;у = 32,4f;

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

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

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

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

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

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

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

Функциональные языки программирования, использующие одно присвоение, включают Clojure (для структур данных, а не vars), Erlang (он принимает множественное присваивание, если значения равны, в отличие от Haskell), F #, Haskell, JavaScript (для констант), Лава, OCaml, Унция (для переменных потока данных, а не ячеек), Ракетка (для некоторых структур данных, таких как списки, а не символы), SASL, Scala (для валсов), СИЗАЛ, Стандартный ML. Не-возврат Пролог код можно рассматривать явный однократное присвоение, явное в том смысле, что его (именованные) переменные могут находиться в явно неназначенном состоянии или быть установлены ровно один раз. В Haskell, напротив, не может быть неназначенных переменных, и каждая переменная может рассматриваться как неявно установленная на свое значение (или, скорее, на вычислительный объект, который будет производить свое значение. по запросу, по требованию) при его создании.

Ценность задания

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

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

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

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

Вариантные формы задания

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

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

Случай, когда присвоенное значение зависит от предыдущего, настолько распространен, что многие императивные языки, особенно C и большинство его потомков предоставляют специальные операторы, называемые расширенное задание, подобно *=, так а = 2 * а вместо этого можно записать как а * = 2.[3] Помимо синтаксического сахара, это облегчает задачу компилятора, давая понять, что изменение переменной на месте а возможно.

Привязанное задание

Заявление вроде ш = х = у = г называется прикованное задание в котором значение z присваивается нескольким переменным ш, х, и у. Связанные назначения часто используются для инициализации нескольких переменных, как в

а = б = с = г = е = 0

Не все языки программирования поддерживают присвоение по цепочке. Связанные задания эквивалентны последовательности заданий, но стратегия оценки различается в зависимости от языка. Для простых связанных назначений, таких как инициализация нескольких переменных, стратегия оценки не имеет значения, но если цели (l-значения) в назначении каким-либо образом связаны, стратегия оценки влияет на результат.

В некоторых языках программирования (C например), цепные присвоения поддерживаются, поскольку присвоения являются выражениями и имеют значения. В этом случае назначение цепочки может быть реализовано с помощью правоассоциативное присвоение, а присваивания идут справа налево. Например, i = arr [i] = f () эквивалентно arr [я] = f (); i = arr [i]. В C ++ они также доступны для значений типов классов, объявляя соответствующий тип возвращаемого значения для оператора присваивания.

В Python, операторы присваивания не являются выражениями и, следовательно, не имеют значения. Вместо этого связанные присваивания представляют собой серию операторов с несколькими целевыми объектами для одного выражения. Назначения выполняются слева направо, так что i = arr [i] = f () оценивает выражение f (), затем присваивает результат самой левой цели, я, а затем присваивает тот же результат следующей цели, обр [я], используя новое значение я.[9] По сути, это эквивалентно tmp = f (); я = tmp; arr [i] = tmp хотя фактическая переменная для временного значения не создается.

Параллельное задание

Некоторые языки программирования, например APL, Common Lisp,[10] Идти,[11] JavaScript (начиная с версии 1.7), PHP, Клен, Lua, Оккам 2,[12] Perl,[13] Python,[14] REBOL, Рубин,[15] и PowerShell позволяют назначать несколько переменных параллельно, используя такой синтаксис:

а, б: = 0, 1

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

вар list: = {0, 1} a, b: = список

Список будет распакован так, что 0 будет присвоен а и 1 к б. Более того,

а, б: = б, а

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

вар t: = aa: = bb: = t

поскольку а: = b; б: = а оставляет оба а и б с исходной стоимостью б.

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

def ж():    возвращаться 1, 2а, б = ж()

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

(а, б) = (б, а);
(нить, int) ж() => ("фу", 1);вар (а, б) = ж();

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

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

пустота Деконструировать(из нить а, из int б) { а = "фу"; б = 1; }

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

Назначение против равенства

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

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

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

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

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

  • В АЛГОЛ и Паскаль, оператор присваивания - это двоеточие и знак равенства (":="), в то время как оператор равенства представляет собой одиночное равно ("=").
  • В C, оператор присваивания представляет собой единственный знак равенства ("="), а оператор равенства представляет собой пару знаков равенства ("==").
  • В р, оператор присваивания в основном <-, как в x <- значение, но в определенных контекстах может использоваться единственный знак равенства.

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

Обозначение

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

Переменная = выражениеФортран, PL / I, C (и потомки Такие как C ++, Ява, так далее.), Оболочка Борна, Python, Идти (присвоение заранее объявленным переменным), р, PowerShell, Ним, так далее.
Переменная := выражениеАЛГОЛ (и производные), Симула, CPL, BCPL, Паскаль[23] (и потомки, такие как Modula ), Мэри, PL / M, Ада, Болтовня, Эйфель,[24][25] Оберон, Дилан,[26] Семя7, Python (выражение присваивания),[27] Идти (сокращение для объявления и определения переменной),[28] Ио, AMPL, ML,[29] AutoHotkey и Т. Д.

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

Переменная << выражениеМэджик
Переменная <- выражениеF #, OCaml, р, S
Переменная <<- выражениер
назначать("Переменная", выражение)р
ПеременнаявыражениеAPL,[30] Болтовня, ОСНОВНОЕ программирование
Переменная =: выражениеJ
ПОЗВОЛЯТЬ Переменная = выражениеБАЗОВЫЙ
позволять Переменная := выражениеXQuery
набор Переменная к выражениеAppleScript
набор Переменная = выражениеОболочка C
Установить переменную Переменная (выражение)PowerShell
Переменная : выражениеMacsyma, Maxima, Ребол, K
вар Переменная выражениеязык сценариев mIRC
ссылочная переменная :- ссылка-выражениеСимула

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

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

ДВИГАТЬСЯ выражение К ПеременнаяКОБОЛ
выражениеПеременнаяTI-BASIC, Casio БАЗОВЫЙ
выражение -> ПеременнаяПОП-2, БЕТА, р
положить выражение в ПеременнаяLiveCode

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

(setf Переменная выражение)Common Lisp
(набор! Переменная выражение)Схема[33][34][35]
набор Переменная выражениеTcl
выражение Переменная !Четвертый

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

Примечания

  1. ^ Использование = появился раньше Фортрана, хотя его популяризировал Фортран.

Рекомендации

  1. ^ а б "2cs24 Декларативный". www.csc.liv.ac.uk. Архивировано из оригинал 24 апреля 2006 г.. Получено 20 апреля 2018.
  2. ^ а б c «Императивное программирование». uah.edu. Получено 20 апреля 2018.
  3. ^ а б c Рюдигер-Маркус Флейг (2008). Программирование биоинформатики на Python: практический курс для начинающих. Wiley-VCH. С. 98–99. ISBN  978-3-527-32094-3. Получено 25 декабря 2010.
  4. ^ а б c d е Переходя границы: изучите функциональное программирование с помощью Haskell В архиве 19 ноября 2010 г. Wayback Machine, Брюс Тейт
  5. ^ Митчелл, Джон С. (2003). Понятия в языках программирования. Издательство Кембриджского университета. п. 23. ISBN  978-0-521-78098-8. Получено 3 января 2011.
  6. ^ «Императивные языки программирования (IPL)» (PDF). gwu.edu. Получено 20 апреля 2018.
  7. ^ Джон С. Митчелл (2003). Понятия в языках программирования. Издательство Кембриджского университета. С. 81–82. ISBN  978-0-521-78098-8. Получено 3 января 2011.
  8. ^ Худак, Пол (2000). Школа выражения Haskell: изучение функционального программирования с помощью мультимедиа. Кембридж: Издательство Кембриджского университета. ISBN  0-521-64408-9.
  9. ^ «7. Простые инструкции - документация Python 3.6.5». docs.python.org. Получено 20 апреля 2018.
  10. ^ «CLHS: Macro SETF, PSETF». Common Lisp Hyperspec. LispWorks. Получено 23 апреля 2019.
  11. ^ Спецификация языка программирования Go: Задания
  12. ^ ИНМОС Лимитед, изд. (1988). Справочное руководство Occam 2. Нью-Джерси: Прентис-Холл. ISBN  0-13-629312-3.
  13. ^ Уолл, Ларри; Кристиансен, Том; Шварц, Рэндал К. (1996). Язык программирования Perl (2-е изд.). Кембридж: О'Рейли. ISBN  1-56592-149-6.
  14. ^ Лутц, Марк (2001). Язык программирования Python (2-е изд.). Севастополь: О'Рейли. ISBN  0-596-00085-5.
  15. ^ Томас, Дэвид; Хант, Эндрю (2001). Программирование на Ruby: Прагматичное руководство программиста. Река Верхний Сэдл: Эддисон Уэсли. ISBN  0-201-71089-7.
  16. ^ Д.В. Barron и другие., «Основные возможности CPL», Компьютерный журнал 6:2:140 (1963). полный текст (подписка)
  17. ^ «PEP 3132 - Расширенная итерационная распаковка». legacy.python.org. Получено 20 апреля 2018.
  18. ^ «Деструктурирующее задание». Веб-документы MDN. Получено 20 апреля 2018.
  19. ^ «Деконструкция кортежей и других типов». Документы Microsoft. Microsoft. Получено 29 августа 2019.
  20. ^ Эффективный Go: за, "Наконец, в Go нет оператора запятой, а ++ и - являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присваивание (хотя это исключает ++ и -)."
  21. ^ Никлаус Вирт. «Хорошие идеи в Зазеркалье». CiteSeerX  10.1.1.88.8309. Отсутствует или пусто | url = (помощь)
  22. ^ Корбет (6 ноября 2003 г.). «Попытка взломать ядро».
  23. ^ Мур, Лори (1980). Основы программирования на Паскале. Нью-Йорк: Джон Вили и сыновья. ISBN  0-470-26939-1.
  24. ^ Мейер, Бертран (1992). Эйфель язык. Хемел Хемпстед: Prentice Hall International (Великобритания). ISBN  0-13-247925-7.
  25. ^ Винер, Ричард (1996). Объектно-ориентированное введение в информатику с использованием Eiffel. Река Аппер Сэдл, Нью-Джерси: Prentice Hall. ISBN  0-13-183872-5.
  26. ^ Файнберг, Нил; Кин, Соня Э .; Мэтьюз, Роберт О .; Витингтон, П. Такер (1997). Дилан Программирование. Массачусетс: Эддисон Уэсли. ISBN  0-201-47976-1.
  27. ^ «PEP 572 - Выражения присваивания». python.org. 28 февраля 2018 г.. Получено 4 марта 2020.
  28. ^ "Спецификация языка программирования Go - язык программирования Go". golang.org. Получено 20 апреля 2018.
  29. ^ Ульман, Джеффри Д. (1998). Элементы программирования машинного обучения: издание ML97. Энглвуд Клиффс, Нью-Джерси: Прентис Холл. ISBN  0-13-790387-1.
  30. ^ Айверсон, Кеннет Э. (1962). Язык программирования. Джон Уайли и сыновья. ISBN  0-471-43014-5. Архивировано из оригинал на 2009-06-04. Получено 2010-05-09.
  31. ^ Грэм, Пол (1996). ANSI Common Lisp. Нью-Джерси: Прентис-Холл. ISBN  0-13-370875-6.
  32. ^ Стил, Гай Л. (1990). Common Lisp: язык. Лексингтон: Цифровая пресса. ISBN  1-55558-041-6.
  33. ^ Дибвиг, Р. Кент (1996). Язык программирования схемы: Схема ANSI. Нью-Джерси: Прентис-Холл. ISBN  0-13-454646-6.
  34. ^ Смит, Джерри Д. (1988). Введение в схему. Нью-Джерси: Прентис-Холл. ISBN  0-13-496712-7.
  35. ^ Абельсон, Гарольд; Сассман, Джеральд Джей; Суссман, Джули (1996). Структура и интерпретация компьютерных программ. Нью-Джерси: Макгроу-Хилл. ISBN  0-07-000484-6.