stringtranslate.com

Задание (информатика)

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

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

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

Семантика

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

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

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

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

интервал х = 10 ; плавать у ; х = 23 ; у = 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 (для значений), SISAL , Standard ML . Код Пролога без возврата можно рассматривать как явное однократное присвоение, явное в том смысле, что его (именованные) переменные могут находиться в явно неназначенном состоянии или быть установлены ровно один раз. В Haskell, напротив, не может быть неназначенных переменных, и каждую переменную можно рассматривать как неявно установленную при ее создании в свое значение (или, скорее, в вычислительный объект, который будет выдавать свое значение по требованию ).

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

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

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

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

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

Варианты форм поручения

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

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

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

Связанное назначение

Оператор Like 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}а, б := список

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

а, б := б, а

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

вар т := аа := бб := т

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

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

def  f ():  вернуть  1 ,  2 a ,  b  =  f ()

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

// Допустимый синтаксис C# или Rust ( a , b ) = ( b , a );    
// Возврат кортежа C# ( string , int ) f () => ( "foo" , 1 ); вар ( а , б ) = ж ();         
// Возвращаем кортеж Rust let f = || ( «фу» , 1 ); пусть ( a , b ) = f ();         

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

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

void Deconstruct ( out string a , out int b ) { a = "foo" ; б = 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.

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

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

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

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

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

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

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

Обозначения

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

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

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

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

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

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

Примечания

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

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

  1. ^ ab "2cs24 Декларативный". www.csc.liv.ac.uk. ​Архивировано из оригинала 24 апреля 2006 года . Проверено 20 апреля 2018 г.
  2. ^ abc «Императивное программирование». уа.еду . Архивировано из оригинала 4 марта 2016 года . Проверено 20 апреля 2018 г.
  3. ^ abc Рюдигер-Маркус Флейг (2008). Программирование биоинформатики на Python: практический курс для начинающих. Вайли-ВЧ. стр. 98–99. ISBN 978-3-527-32094-3. Проверено 25 декабря 2010 г.
  4. ^ abcde Пересекая границы: изучайте функциональное программирование с помощью Haskell. Архивировано 19 ноября 2010 г., Брюс Тейт, в Wayback Machine .
  5. ^ Митчелл, Джон К. (2003). Концепции в языках программирования. Издательство Кембриджского университета. п. 23. ISBN 978-0-521-78098-8. Проверено 3 января 2011 г.
  6. ^ «Императивные языки программирования (IPL)» (PDF) . gwu.edu . Архивировано из оригинала (PDF) 16 июля 2011 г. Проверено 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: Макрос SETF, PSETF» . Общая гиперспецификация Lisp . Лиспворкс . Проверено 23 апреля 2019 г.
  11. ^ Спецификация языка программирования Go: Задания
  12. ^ INMOS Limited, изд. (1988). Справочное руководство Оккам 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. ^ Д.В. Бэррон и др. , «Основные особенности CPL», Computer Journal 6 :2:140 (1963). полный текст (подписка)
  17. ^ «PEP 3132 — Расширенная итеративная распаковка» . Legacy.python.org . Проверено 20 апреля 2018 г.
  18. ^ «Задание по деструктуризации». Веб-документы MDN . Проверено 20 апреля 2018 г.
  19. ^ «Деконструкция кортежей и других типов» . Документы Майкрософт . Майкрософт . Проверено 29 августа 2019 г.
  20. ^ Эффективный Go: for: «Наконец, в Go нет оператора-запятой, а ++ и -- являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присваивание (хотя это исключает ++ и --). )"
  21. ^ Никлаус Вирт. «Хорошие идеи в Зазеркалье». CiteSeerX 10.1.1.88.8309 . 
  22. ^ «Язык программирования C++. Основы». ntu.edu.sg. ​01.06.2013 . Проверено 21 июня 2024 г.
  23. ^ Корбет (6 ноября 2003 г.). «Попытка взлома ядра». lwn.net . Проверено 21 июня 2024 г.
  24. ^ «Параметры статического анализатора (с использованием коллекции компиляторов GNU (GCC))» . gcc.gnu.org . Проверено 21 июня 2024 г.
  25. ^ Дейтел, Пол; Дейтел, Харви (25 октября 2022 г.). «Управляющие операторы C++, часть 2». Домашние задания . Проверено 21 июня 2024 г.
  26. ^ Мур, Лори (1980). Основы программирования на языке Паскаль . Нью-Йорк: Джон Уайли и сыновья. ISBN 0-470-26939-1.
  27. ^ Мейер, Бертран (1992). Эйфель Язык . Хемел Хемпстед: Prentice Hall International (Великобритания). ISBN 0-13-247925-7.
  28. ^ Винер, Ричард (1996). Объектно-ориентированное введение в информатику с использованием Eiffel . Река Аппер-Сэддл, Нью-Джерси: Прентис-Холл. ISBN 0-13-183872-5.
  29. ^ Фейнберг, Нил; Кин, Соня Э.; Мэтьюз, Роберт О.; Витингтон, П. Такер (1997). Программирование Дилана . Массачусетс: Эддисон Уэсли. ISBN 0-201-47976-1.
  30. ^ «PEP 572 – Выражения присваивания» . python.org . 28 февраля 2018 года . Проверено 4 марта 2020 г.
  31. ^ «Спецификация языка программирования Go — Язык программирования Go» . golang.org . Проверено 20 апреля 2018 г.
  32. ^ Уллман, Джеффри Д. (1998). Элементы программирования машинного обучения: издание ML97 . Энглвуд Клиффс, Нью-Джерси: Прентис Холл. ISBN 0-13-790387-1.
  33. ^ Айверсон, Кеннет Э. (1962). Язык программирования. Джон Уайли и сыновья. ISBN 0-471-43014-5. Архивировано из оригинала 4 июня 2009 г. Проверено 9 мая 2010 г.
  34. ^ Грэм, Пол (1996). ANSI Common Lisp. Нью-Джерси: Прентис Холл. ISBN 0-13-370875-6.
  35. ^ Стил, Гай Л. (1990). Common Lisp: язык . Лексингтон: Цифровая пресса. ISBN 1-55558-041-6.
  36. ^ Дибвиг, Р. Кент (1996). Язык программирования Scheme: ANSI Scheme . Нью-Джерси: Прентис Холл. ISBN 0-13-454646-6.
  37. ^ Смит, Джерри Д. (1988). Знакомство со схемой . Нью-Джерси: Прентис Холл. ISBN 0-13-496712-7.
  38. ^ Абельсон, Гарольд; Сассман, Джеральд Джей; Сассман, Джули (1996). Структура и интерпретация компьютерных программ . Нью-Джерси: МакГроу-Хилл. ISBN 0-07-000484-6.