Когда это нужно
Создайте процедуру, чтобы повторно использовать вычисление или передать его другой процедуре. Используйте apply, если часть аргументов или все аргументы хранятся в списке.
Проверьте запуском
Сохраните всю программу ниже в procedures.lspx. Сначала установите Native CLI или запускайте каждый пример отдельно в песочнице.
(define (sum first . rest) (apply + first rest))
(sum 1 2 3 4)lispex run procedures.lspxВывод
10Команда завершается с кодом 0 и не создаёт файлов.
Разбор примера
sum требует хотя бы один аргумент. first получает 1, а rest получает новый список (2 3 4). apply объединяет явный первый аргумент с этим списком и один раз вызывает +.
Имена first и rest называются параметрами. Значения, переданные при вызове, — аргументами. Допустимое число аргументов процедуры называют её арностью. Точка перед rest означает, что этот параметр получает новый список оставшихся аргументов.
Выражение (apply + first rest) — тело процедуры, то есть код, выполняемый при вызове. В этом примере оно выполняет то же вычисление, что и (+ 1 2 3 4).
Определите имена внутри процедуры
(define (g)
(define a 1)
(define b 2)
(+ a b))
(g)Наблюдаемый результат
3Определения a и b создают имена, доступные только внутри g. Последовательные формы define в начале тела объединяются в одну группу. Процедуры, определённые в этой группе, могут вызывать друг друга.
Держите эти определения вместе в начале тела. Форма define после другого выражения уже не входит в начальную группу.
Выберите параметры и форму вызова
Используйте define, чтобы дать процедуре имя. lambda создаёт значение-процедуру без имени. В обоих случаях имена определяются структурой окружающего кода по правилам лексической области видимости. Процедура, сохраняющая доступ к этим именам, называется замыканием.
| Форма | Подходящее применение | Арность |
|---|---|---|
(define (f x) body) | именованная повторно используемая процедура | фиксированная |
(lambda (x y) body) | анонимная процедура-значение | фиксированная |
(define (f . xs) body) | именованная процедура собирает свои аргументы | ноль или больше |
(lambda (x . rest) body) | обязательная часть и остаток | не меньше фиксированной части |
(apply f a final-list) | применение аргументов, собранных как данные | проверяет f |
В обозначениях (x ...) и (x ... . rest) многоточие означает дополнительные имена параметров, а не буквальный текст программы. Это две допустимые формы списка параметров lambda. Имя без скобок, как в (lambda args ...), вызывает E130.
Параметр оставшихся аргументов получает новый правильный список, последний хвост которого равен (). Последний аргумент apply тоже должен быть правильным списком. Например, '(1 2) — правильный список, а '(1 . 2) оканчивается на 2 и является неправильным.
Проверьте аргументы до выполнения тела
Лиспекс сначала вычисляет выражение процедуры, затем выражения аргументов слева направо. Если число аргументов не подходит, E302 возникает до выполнения тела. Например, (sum) не передаёт ни одного аргумента, хотя sum требует хотя бы один.
Вызов значения, которое не является процедурой, вызывает E301. Неправильный список в последнем аргументе, как в (apply + '(1 . 2)), вызывает E312.
Используйте apply для последнего вызова
Если apply — последнее действие процедуры, он сохраняет хвостовую позицию. Результат вызванной процедуры становится результатом вызывающей без дополнительного шага возврата. Примеры приведены в разделе хвостовых вызовов.
Лиспекс использует один обработчик для прямых вызовов и apply. Он сам выполняет замыкания Лиспекса, не передавая их в apply языка реализации.
Куда дальше
Справочник процедур высшего порядка показывает, как передавать процедуры в map, filter и другие операции. Руководство по замыканиям объясняет, как процедура сохраняет доступ к именам после завершения окружающего кода.