Арифметика: оператор is

Урок объясняет, почему в Prolog нельзя написать X = 2 + 3 и ждать пятёрку, и как правильно считать через is.

is — встроенный оператор, который вычисляет арифметическое выражение справа и унифицирует полученное число с тем, что слева.

Новички, пришедшие из обычных языков, почти всегда спотыкаются здесь. В Python или JavaScript знак = означает «присвой», а 2 + 3 где угодно даёт 5. В Prolog оба этих ожидания ложны, и пока вы это не прочувствуете, арифметика будет вести себя «загадочно».

Почему = ничего не вычисляет

Оператор = в Prolog — это унификация, попытка сделать два терма одинаковыми. Он не «присваивает» и не «считает». Когда вы пишете X = 2 + 3, Prolog видит слева переменную, а справа составной терм +(2, 3) — структуру с функтором + и двумя аргументами. Унификация просто связывает X с этим деревом.

?- X = 2 + 3.
X = 2+3.

?- X = 2 + 3, X == 5.
false.

Вывод:

X = 2+3.

Видите: X стал не числом 5, а термом 2+3. Сравнение X == 5 провалилось, потому что структура +(2,3) и число 5 — это разные термы. Запись 2+3 в выводе — лишь красивая форма печати дерева +(2,3).

Что делает is

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

?- X is 2 + 3.
X = 5.

?- X is (4 + 6) * 2 - 1.
X = 19.

?- X is 17 mod 5.
X = 2.

Вывод:

X = 5.

Чаще всего is используют внутри правил, чтобы получить новое значение из старых:

square(X, Y) :- Y is X * X.

?- square(7, R).
R = 49.

Вывод:

R = 49.

Правый аргумент обязан быть означен

Главное ограничение: к моменту вычисления is все переменные справа должны быть связаны конкретными числами. is умеет считать выражение, но не умеет решать уравнения. Запрос X is Y + 1 при несвязанном Y упадёт с ошибкой instantiation error.

?- Y is 5 + 1, X is Y * 2.
Y = 6,
X = 12.

?- X is Y + 1.
ERROR: Arguments are not sufficiently instantiated

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

Сравнения по значению

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

ОператорСмыслПример (истина)
=:=равно по значению2 + 2 =:= 4
=\=не равно по значению3 =\= 5
<меньше2 < 3
>больше5 > 1
=<меньше или равно4 =< 4
>=больше или равно7 >= 7

Запомните разницу между =:= и =. Выражение 1 + 1 =:= 2 истинно (значения равны), а 1 + 1 = 2 ложно (термы +(1,1) и 2 разные структурно). Сравнения, как и is, требуют, чтобы переменные внутри были означены.

Ловушка «меньше или равно»

Оператор «меньше или равно» в Prolog пишется =<, то есть знак равенства идёт перед знаком меньше. Привычного по другим языкам <= в Prolog нет — он будет разобран как другой терм и не сработает как сравнение. А вот «больше или равно» пишется ожидаемо: >=.

grade(Score, pass) :- Score >= 60.
grade(Score, fail) :- Score < 60.

?- grade(72, R).
R = pass.

Вывод:

R = pass.

Как работает под капотом

В Prolog любой код — это термы. Запись 2 + 3 парсер превращает в дерево +(2, 3), потому что + объявлен как инфиксный оператор с определённым приоритетом. Сам по себе этот терм — просто данные, как список или пара. Никто его не «считает», пока вы явно не попросите.

     is/2
   /      \
  X        +(2,3)
            /  \
           2    3

Оператор is — это предикат, который при доказательстве цели запускает арифметический вычислитель на правом аргументе: рекурсивно обходит дерево, складывает, умножает и так далее, получает число и унифицирует его с левым аргументом. Если в дереве встретится несвязанная переменная, считать нечего — отсюда instantiation error. Сравнения =:=, < и прочие работают так же: вычисляют оба дерева и сравнивают итоговые числа.

Частые ошибки

  • Ждать вычисления от =. X = 2 + 3 даёт терм, а не 5. Для счёта всегда is.
  • Несвязанная переменная справа от is. X is Y + 1 при свободном Y — ошибка. Сначала свяжите Y.
  • Путать = и =:=. Для сравнения чисел по значению — =:=, а = сравнивает структуру термов.
  • Писать <= вместо =<. «Меньше или равно» — это =<, знак равенства идёт первым.
  • Менять переменную через is. X is X + 1 почти всегда провал: X уже связан, и новое значение с ним не унифицируется. Заводите новую переменную.

Итог

  • = — унификация (делает термы одинаковыми), а не присваивание и не вычисление.
  • is вычисляет правую часть как арифметику и унифицирует число с левой.
  • Правый аргумент is должен быть полностью означен, иначе instantiation error.
  • Сравнения =:=, =\=, <, >, =<, >= вычисляют обе стороны и сравнивают значения.
  • «Меньше или равно» — это =<, а не <=.
Проверьте себя
1. Чему унифицируется X в запросе ?- X = 2 + 3.?
AЧислу 5
BТерму 2+3 (структуре +(2,3))
CОшибке instantiation error
DАтому '2+3'
2. Почему запрос ?- X is Y + 1. при несвязанном Y завершается ошибкой?
Ais не умеет работать с переменными вообще
BПравый аргумент is должен быть полностью означен числами
CY нужно объявить как dynamic
DНужно писать =:= вместо is
3. Как в Prolog пишется сравнение «меньше или равно»?
A<=
B=<
C>=
D=:=