Как стать автором
Обновить

Операции, функции и специальные формы в императивных языках программирования

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров1.9K

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

Если читатель владеет одним из языков семейства Лисп (Common Lisp, Scheme, Clojure и т.д.), а в особенности если читал SICP, то ему излагаемый вопрос не в новинку, и он может пропустить эту статью. Если читатель использует Хаскель или другой язык, основанный на модели ленивых вычислений, то там всё немножко по-другому, и впрямую изложение материала в данной статье к таким языкам не относится, хотя фундаментальные принципы в основе лежат те же.

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

Операции

Большинство императивных языков программирования использует синтаксис выражений, приближенный к математическим формулам, где, помимо собственно вызовов функций, используются операции (operators) со специальным синтаксисом, обычно одноместные префиксные или двуместные инфиксные, например, ~x, *p или 5+2. Без какой-либо потери общности эти операции можно было бы (а в языке Лисп так и сделано) представить в обычной функциональной форме вызова, например, not(x), target(p) или plus(5,2). Поэтому, говоря об операциях, мы фактически с тем же успехом можем вместо этого говорить о функциях или, по крайней мере, о чём-то синтаксически подобном функциям. Далее мы можем специально не выделять операции среди прочих функциональных объектов.

Функции

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

Для нас сейчас в механизме применения функций важен механизм передачи параметров.

Подавляющее большинство современных императивных языков программирования использует в конечном итоге два способа передачи параметров – по значению и по ссылке. (Этими способами природа и человеческая мысль не ограничиваются, и, например, в языке Алгол использовался выпадающий из нашего хода рассуждений способ передачи параметров по имени, но впоследствии решили, что тех же самых результатов гораздо красивее можно добиться передачей по значению некоторого лямбда-выражения).

Передача параметров как по значению, так и по ссылке заключается в том, что сначала вычисляется значение фактического параметра, а затем само это значение или ссылка на него передаётся внутрь функции для использования в качестве формального параметра. (Не все языки, поддерживающие передачу параметров по ссылке, поддерживают использование выражения для ссылки, но это для нас не важно).

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

Таким образом, способ вызова функции такой: вычисляем вызываемую функцию, вычисляем значения всех её фактических параметров, вызываем функцию с параметрами. В большинстве императивных языков точный порядок вычисления параметров и самой функции не определён.

Например:

(*func) (x+3, ++i);

Здесь мы вычисляем тройку значений выражений *func, x+3, ++i (поскольку это язык Си, то вычисления происходят не обязательно в таком же порядке) и после этого вызываем функцию, на которую указывает func, с переданными ей вычисленными значениями x+3 и ++i.

x+y

Здесь мы определяемся со значением операции +, вычисляем значение x, вычисляем значение y, после чего применяем операцию + к вычисленным значениям x и y.

Специальные формы

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

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

В большинстве императивных языков реализовываются три специальные формы: логическое И, логическое ИЛИ и условная операция.

В операции x && y значение параметра y вычисляется только в том случае, если значение параметра x является истинным.

В операции x || y значение параметра y вычисляется только в том случае, если значение параметра x является ложным.

В операции x ? y : z вычисляется только один из параметров y или z в зависимости от истинности параметра x.

Что произойдёт, если мы попробуем заменить операцию && в языке Си на написанную нами функцию And?

bool And (bool x, bool y) {
  return x&&y;
}

Такая замена семантически не эквивалентна. Например, мы имеем право написать:

if ((i >= 0) && (array[i] > 0)) ...

но не имеем право заменить такую конструкцию на:

if (And (i >= 0, array[i] > 0)) ...

В первом случае, если i < 0, то обращения к массиву не произойдёт, так как второй параметр операции && вычисляется только в случае истинности первого.

Во втором случае, если i < 0, то второй параметр функции And всё равно будет вычисляться, исходя из общего порядка вызова функции, и произойдёт выход за границы массива при этом вычислении.

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

По той же самой причине функции вроде coalesce не являются полноценной заменой условной операции, так как не представляют собой специальные формы.

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

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

Теги:
Хабы:
Всего голосов 7: ↑5 и ↓2+4
Комментарии38

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань