Вы можете предсказать результат программы, проследив порядок изменения значений и вызовов процедур. Лиспекс сначала вычисляет оператор вызова, затем операнды слева направо.
Проследите изменение значения
Вставьте эту программу целиком в песочницу.
(define x 0)
(list (begin (set! x 1) x) (begin (set! x (+ x 1)) x))Результат
(1 2)Первый begin меняет x на 1 и возвращает 1. Второй читает новое значение, прибавляет единицу, сохраняет 2 и возвращает его. Процедура list получает сначала 1, затем 2.
Оператор вызова — это выражение, которое даёт процедуру. Здесь это list. Операнды дают значения аргументов, здесь это два выражения begin.
Учитывайте вид выражения
| Выражение | Порядок вычисления | Что происходит с результатами |
|---|---|---|
| вызов процедуры | оператор, затем операнды слева направо | после проверки числа аргументов вызывается процедура |
begin или тело процедуры | выражения слева направо | ранние результаты отбрасываются, последний возвращается |
if | условие, затем выбранная ветвь | возвращается результат этой ветви |
and | операнды слева направо | остановка на #f либо возврат последнего результата |
or | операнды слева направо | остановка на первом результате, отличном от #f |
let | инициализаторы слева направо, с именами из внешней области | новые имена связываются со значениями для тела |
let* | инициализатор, затем связывание имени | следующие инициализаторы видят предыдущие новые имена |
letrec | сначала память для всех имён, затем вычисление и запись начальных значений слева направо | все имена видны при инициализации |
Инициализатор задаёт начальное значение имени. Область видимости определяет, где доступно имя. Место хранения одного связанного значения называется ячейкой. Чтение ячейки letrec до присвоения ей значения даёт E321.
Ошибка останавливает текущее вычисление. Указанный порядок относится к выражениям, до которых программа успела дойти. Выражения перед последним в begin или теле могут возвращать любое число значений, поскольку их результаты отбрасываются.
Посмотрите, что будет пропущено
Запустите этот пример как отдельную программу.
(list (if #t 7 (/ 1 0))
(and #f (/ 1 0))
(or 0 (/ 1 0)))Результат
(7 #f 0)Ни одно деление не выполняется. if выбирает 7, and останавливается на #f, а or — на 0. Ложным считается только #f, поэтому 0 считается истинным.
Число аргументов проверяется после вычисления
Ошибка числа аргументов не мешает сначала вычислить оператор и операнды. После установки Native CLI сохраните этот полный пример в arity-effects.lspx.
((begin (display "operator ") (lambda (x) x))
(begin (display "left ") 1)
(begin (display "right") 2))lispex run arity-effects.lspxСтандартный вывод содержит этот текст без перевода строки в конце:
operator left rightПроцедура ожидает один аргумент, но получает два. Затем команда пишет в стандартный поток ошибок:
E302 arity-effects.lspx:1:1 arity mismatch: expected 1 argument(s), got 2Код завершения — 1, новые файлы не создаются. Тело процедуры не выполняется.
Различия форм let описаны в главе «Связывания и область видимости». Правила выбора ветви приведены в главе «Условия».