Арифметика: оператор 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. - Сравнения
=:=,=\=,<,>,=<,>=вычисляют обе стороны и сравнивают значения. - «Меньше или равно» — это
=<, а не<=.