В компьютерном программировании операторы — это конструкции, определенные в языках программирования , которые ведут себя как функции , но отличаются синтаксически или семантически .
Общие простые примеры включают арифметику (например, сложение с +
), сравнение (например, « больше чем » с >
) и логические операции (например AND
, также написанные &&
на некоторых языках). Более сложные примеры включают присваивание (обычно =
или :=
), доступ к полю в записи или объекте (обычно .
) и оператор разрешения области (часто ::
или .
). Языки обычно определяют набор встроенных операторов и в некоторых случаях позволяют пользователям добавлять новые значения к существующим операторам или даже определять совершенно новые операторы.
Синтаксически операторы обычно противопоставляются функциям . В большинстве языков функции можно рассматривать как специальную форму префиксного оператора с фиксированным уровнем приоритета и ассоциативностью, часто с обязательными круглыми скобками , например Func(a)
(или (Func a)
в Lisp ). Большинство языков поддерживают функции, определяемые программистом, но не могут претендовать на поддержку операторов, определяемых программистом, если только они не имеют более чем префиксной записи и более одного уровня приоритета. Семантически операторы можно рассматривать как особую форму функции с различными обозначениями вызова и ограниченным количеством параметров (обычно 1 или 2).
Положение оператора относительно его операндов может быть префиксным , инфиксным или постфиксным , а синтаксис выражения, включающего оператор, зависит от его арности (количества операндов ), приоритета и (если применимо) ассоциативности . Большинство языков программирования поддерживают бинарные операторы и несколько унарных операторов , а некоторые поддерживают больше операндов, например, оператор ?: в C, который является троичным. Существуют префиксные унарные операторы, такие как унарный минус -x
, и постфиксные унарные операторы, такие как постинкремент x++
; а бинарные операции являются инфиксными, например x + y
или x = y
. Инфиксные операции более высокой арности требуют дополнительных символов, таких как тернарный оператор ?: в C, записываемый как a ? b : c
– действительно, поскольку это единственный распространенный пример, его часто называют тернарным оператором. Однако префиксные и постфиксные операции могут поддерживать любую желаемую арность, например 1 2 3 4 +
.
Иногда [1] [2] части языка могут быть описаны как операторы « matchfix » или « circumfix » [3] [4] либо для упрощения описания языка, либо для его реализации. Оператор циркумфикса состоит из двух или более частей, охватывающих его операнды. Операторы Circumfix имеют наивысший приоритет: их содержимое оценивается, а полученное значение используется в окружающем выражении. Наиболее знакомым оператором циркумфикса являются упомянутые выше круглые скобки, которые используются для указания того, какие части выражения должны оцениваться раньше других. Другим примером из физики является обозначение внутреннего произведения нотации Дирака . Операторы циркумфикса особенно полезны для обозначения операций, которые включают в себя множество или различное количество операндов.
В спецификации языка будет указан синтаксис поддерживаемых им операторов, в то время как языки, такие как Пролог , которые поддерживают операторы, определяемые программистом, требуют, чтобы синтаксис был определен программистом.
Семантика операторов особенно зависит от значения, стратегии оценки и режима передачи аргументов (например, логического короткого замыкания). Проще говоря, выражение, включающее оператор, каким-то образом вычисляется, и результирующее значение может быть просто значением (r-значение) или объектом, допускающим присваивание (l-значение).
В простых случаях это идентично обычным вызовам функций; например, сложение x + y
обычно эквивалентно вызову функции add(x, y)
, а сравнение «меньше» x < y
— lt(x, y)
, что означает, что аргументы оцениваются обычным способом, затем вычисляется некоторая функция, и результат возвращается в виде значения. Однако семантика может существенно отличаться. Например, при присвоении a = b
цель a
не оценивается, а вместо этого используется ее местоположениеb
(адрес) для хранения значения , что соответствует семантике вызова по ссылке . Кроме того, присвоение может быть оператором (без значения) или выражением (значением), причем само значение может быть либо r-значением (просто значение), либо l-значением (которому можно присвоить). В качестве другого примера — оператор разрешения области :: и оператор доступа к элементу. (как в Foo::Bar
or a.b
) работают не со значениями, а с именами , по сути, семантикой вызова по имени , и их значением является имя.
Использование l-значений в качестве операндов операторов особенно заметно в унарных операторах увеличения и уменьшения . В C, например, следующий оператор является допустимым и четко определенным и зависит от того факта, что индексация массива возвращает l-значение:
х = ++ а [ я ];
Важным применением является ситуация, когда левоассоциативный бинарный оператор изменяет свой левый аргумент (или создает побочный эффект ), а затем вычисляет этот аргумент как l-значение. [a] Это позволяет использовать последовательность операторов, влияющих на исходный аргумент, обеспечивая плавный интерфейс , аналогичный каскадированию методов . Типичным примером является <<
оператор в библиотеке C++ iostream
, который обеспечивает плавный вывод, например:
cout << "Привет" << " " << "мир!" << конец ;
Язык может содержать фиксированное количество встроенных операторов (например , +, -, *, <, <=, !, = и т. д. в C и C++ , PHP ) или может допускать создание операторов, определяемых программистом. (например , Prolog , [5] Seed7 , [6] F# , OCaml , Haskell ). Некоторые языки программирования ограничивают символы операторов специальными символами, такими как + или := , в то время как другие допускают также имена, такие как (например, Pascal ).div
Большинство языков имеют встроенный набор операторов, но не допускают операторов, определяемых пользователем, поскольку это существенно усложняет синтаксический анализ. [b] Многие языки позволяют использовать операторы только для встроенных типов, но другие позволяют использовать существующие операторы для пользовательских типов; это известно как перегрузка операторов . Однако некоторые языки позволяют определять новые операторы либо во время компиляции, либо во время выполнения. Это может включать метапрограммирование (определение операторов на отдельном языке) или внутри самого языка. Определение новых операторов, особенно определение во время выполнения, часто делает невозможным правильный статический анализ программ, поскольку синтаксис языка может быть полным по Тьюрингу, поэтому даже построение синтаксического дерева может потребовать решения проблемы остановки, что невозможно. Это происходит , например, с Perl и некоторыми диалектами Lisp .
Типичными примерами синтаксических различий являются математические арифметические операции , например «>» для « больше чем », с именами, часто выходящими за пределы набора идентификаторов языка для функций, и вызываемые с синтаксисом, отличным от синтаксиса языка для вызова функций. Как функция, «больше чем» обычно будет называться идентификатором, например gt
или, greater_than
и вызываться как функция, например gt(x, y)
. Вместо этого операция использует специальный символ >
(который обозначается отдельно во время лексического анализа ) и инфиксную нотацию, например x > y
.
Обычными примерами, которые отличаются семантически (режимом передачи аргументов), являются логические операции, которые часто содержат короткую оценку : например, короткая конъюнктура (X И Y), которая оценивает более поздние аргументы только в том случае, если более ранние не являются ложными, в языке с строгие функции вызова по значению. Вместо этого это ведет себя аналогично if/then/else.
Менее распространенные операторы включают:
e, f
*p
и оператор адреса:&x
number = spell_out_numbers ? "forty-two" : 42
x ?: y
x ?? y
x <=> y
+=
: _ _ FMAed Multi-Add (FMA) и некоторые высокопроизводительные программные библиотеки поддерживают такие функции, как cis x = cos x + i sin x, для повышения скорости обработки или уменьшения размера кода.-=
*=
/=
%=
<<=
>>=
&=
^=
|=
Компилятор может реализовывать операторы и функции с помощью вызовов подпрограмм или встроенного кода . Некоторые встроенные операторы, поддерживаемые языком, имеют прямое отображение на небольшое количество инструкций, обычно встречающихся в центральных процессорах , хотя другие ( например , '+', используемый для выражения конкатенации строк ), могут иметь сложную реализацию.
В некоторых языках программирования оператор может быть специальным полиморфным , то есть иметь определения для более чем одного типа данных (например, в Java , где +
оператор используется как для сложения чисел, так и для объединения строк). Такой оператор называется перегруженным . В языках, которые поддерживают перегрузку операторов программистом (например, C++ ), но имеют ограниченный набор операторов, перегрузка операторов часто используется для определения индивидуального использования операторов.
В примере операторы: (больше) и (меньше).IF ORDER_DATE > "12/31/2011" AND ORDER_DATE < "01/01/2013" THEN CONTINUE ELSE STOP
>
AND
<
Некоторые языки также допускают неявное преобразование или приведение операндов оператора к подходящим типам данных для выполнения операции. Например, в Perl правила приведения приводят к 12 + "3.14"
получению результата 15.14
. Текст "3.14"
преобразуется в число 3,14, прежде чем можно будет выполнить сложение. Кроме того, 12
является целым числом и 3.14
является числом с плавающей запятой или числом с фиксированной запятой (числом, в котором есть десятичный знак), поэтому целое число затем преобразуется в число с плавающей запятой или число с фиксированной запятой соответственно.
JavaScript следует противоположным правилам: найдя то же самое выражение, что и выше, он преобразует целое число 12
в строку "12"
, а затем объединяет два операнда в форму "123.14"
.
При наличии приведения в языке программист должен знать конкретные правила, касающиеся типов операндов и типа результата операции, чтобы избежать тонких ошибок программирования.
В следующей таблице показаны функции оператора на нескольких языках программирования:
@
требует лексирования и токенизации этого символа, а структура фразы (синтаксическое дерево) зависит от арности и приоритета этого оператора.