Разница между нагрузкой и включением в схему R7RS

В схеме R7RS есть и load а также include форма.

Включить описывается как:

Семантика: и include, и include-ci берут одно или несколько имен файлов, выраженных в виде строковых литералов, применяют алгоритм, специфичный для реализации, для поиска соответствующих файлов, считывают содержимое файлов в указанном порядке, как будто повторяющимися приложениями чтения, и эффективно перезаписывают - поместите выражение include или include-ci в выражение начала, содержащее то, что было прочитано из файлов. Разница между ними заключается в том, что include-ci читает каждый файл, как если бы он начинался с директивы #! Fold-case, а include - нет. Примечание. Реализациям рекомендуется выполнять поиск файлов в каталоге, который содержит включаемый файл, и предоставлять пользователям возможность указать другие каталоги для поиска.

Нагрузка описывается как:

Зависящая от реализации операция используется для преобразования имени файла в имя существующего файла, содержащего исходный код Схемы. Процедура загрузки считывает выражения и определения из файла и последовательно оценивает их в среде, определенной спецификатором среды. Если спецификатор среды опущен, предполагается взаимодействие с окружением. Не указано, печатаются ли результаты выражений. Процедура загрузки не влияет на значения, возвращаемые current-input-port и current-output-port. Возвращает неопределенное значение. Обоснование: для переносимости загрузка должна работать с исходными файлами. Его работа с другими типами файлов обязательно зависит от реализации.

Каково обоснование двух форм? Я предполагаю, что это историческое. Есть ли какая-то семантическая разница между этими двумя формами? я вижу это load может дополнительно включать спецификатор среды и include не имеет этого А также include-ci не имеет прямого эквивалента использования load, Но сравнивая load а также include один, в чем разница и это важно?

2 ответа

Я думаю, что критическая разница в том, что include это синтаксис (или в традиционных терминах Лисп, это макрос), в то время как load это функция. В традиционных терминах Лисп (в терминах Схемы это будет гораздо более формальным определением, которое я не компетентен дать), это означает, что include делает свою работу во время макро-расширения, в то время как load делает свою работу во время оценки. Эти времена могут сильно отличаться для реализации, в которой есть файловый компилятор: время расширения макроса происходит во время компиляции файлов, тогда как оценка происходит только намного позже, когда загружаются скомпилированные файлы.

Итак, если мы рассмотрим два файла, f1.scm содержащий

(define foo 1)
(include "f2.scm")

а также f2.scm содержащий

(define bar 2)

тогда если вы загрузите или скомпилируете f1.scm это точно так же, как если бы вы загрузили или скомпилировали файл fe.scm который содержал:

(define foo 1)
(begin
  (define bar 2))

который в свою очередь такой же, как если бы fe.scm содержала:

(define foo 1)
(define bar 2)

В частности, это включение файлов происходит во время расширения макроса, которое происходит при запуске компилятора: объектный файл (файл fasl), создаваемый компилятором, будет включать скомпилированные определения foo а также bar, и никак не будет зависеть от f2.scm или его скомпилированный эквивалент существующий.

Теперь рассмотрим f3.scm содержащий:

(define foo 1)
(load "f2")

(обратите внимание, я предположил, что (load "f2") (в отличие от (load "f2.scm")) загружает скомпилированный файл, если он может его найти, и исходный файл, если он не может: я думаю, что это зависит от реализации).

Загрузка источника этого файла будет аналогична загрузке f1.scm: это вызовет foo а также bar быть определенным. Но компиляция этого файла не произойдет: он создаст скомпилированный файл, который при последующей загрузке попытается загрузить либо исходную, либо скомпилированную версии. f2.scm, Если этот файл существует, во время загрузки он будет загружен, и эффект будет таким же, как include дело. Если он не существует во время загрузки, произойдут плохие вещи. составление f1.scm не вызовет определения в f2.scm быть скомпилированным.


В зависимости от вашего опыта, возможно, стоит сравнить это, скажем, с языками семейства Си. Какие include делает то, что #include делает: это склеивает в исходные файлы, когда они читаются, и в C (как во многих системах Scheme/Lisp) это происходит, когда файлы компилируются. Какие load Он загружает код во время выполнения, что в C вам нужно сделать, вызвав динамический компоновщик или что-то в этом роде.

Исторически, реализации Lisp не предлагали модульные системы.

Большие программы использовали загрузку, чтобы выполнить набор инструкций, функция загрузки запускает сценарий REPL, считывая S-выражения из файла, одно за другим, и передавая их в eval.

С другой стороны, include используется для вставки кода, прочитанного из файла, в ваш код. Он не оценивает код.

... заменить выражение include или include-ci выражением начала, содержащим то, что было прочитано из файлов

Добавленный 'begin' подготавливает код, считанный из файла, для последовательной оценки.

Источники: Вопрос цитаты, Ракетные документы

Другие вопросы по тегам