Разница между нагрузкой и включением в схему 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' подготавливает код, считанный из файла, для последовательной оценки.
Источники: Вопрос цитаты, Ракетные документы