在方案 R7RS 中,有 aload
和include
形式。
包含描述为:
语义:include 和 include-ci 都采用一个或多个以字符串文字表示的文件名,应用特定于实现的算法来查找相应的文件,按照指定的顺序读取文件的内容,就像重复应用 read 一样,并有效地重新- 将 include 或 include-ci 表达式与包含从文件中读取的内容的 begin 表达式放在一起。两者之间的区别在于 include-ci 读取每个文件,就像它以 #!fold-case 指令开头一样,而 include 则不然。注意:鼓励实现在包含包含文件的目录中搜索文件,并为用户提供一种指定其他目录进行搜索的方法。
负载描述为:
依赖于实现的操作用于将文件名转换为包含Scheme源代码的现有文件的名称。加载过程从文件中读取表达式和定义,并在环境说明符指定的环境中按顺序计算它们。如果省略环境说明符,则假定为(交互环境)。未指定是否打印表达式的结果。加载过程不会影响当前输入端口和当前输出端口返回的值。它返回一个未指定的值。基本原理:为了可移植性,加载必须对源文件进行操作。它对其他类型文件的操作必然因实现而异。
这两种形式的理由是什么?我认为这是历史性的。这两种形式之间有什么重要的语义差异吗?我发现load
可以选择包含环境说明符,但include
没有。并且include-ci
没有直接等效的使用load
. 但比较load
和include
单独比较,有什么区别,重要吗?
小智 5
我认为关键的区别在于include
is语法(或者用传统的 Lisp 术语来说,它是一个宏)而load
是一个函数。在传统的 Lisp 术语中(在我无法给出的方案术语中会有更正式的定义),这意味着include
它在宏扩展时工作,而load
在求值时工作。对于具有文件编译器的实现来说,这些时间可能非常不同:宏扩展时间发生在文件编译期间,而评估仅在加载已编译文件时才发生。
因此,如果我们考虑两个文件,f1.scm
其中包含
(define foo 1)
(include "f2.scm")
Run Code Online (Sandbox Code Playgroud)
并f2.scm
含有
(define bar 2)
Run Code Online (Sandbox Code Playgroud)
那么如果您加载或编译 f1.scm
它,则与加载或编译包含以下内容的文件完全相同fe.scm
:
(define foo 1)
(begin
(define bar 2))
Run Code Online (Sandbox Code Playgroud)
这又与包含相同fe.scm
:
(define foo 1)
(define bar 2)
Run Code Online (Sandbox Code Playgroud)
特别是,文件的包含发生在宏扩展时,即编译器运行时发生的情况:编译器生成的目标文件(fasl 文件)将包含 和 的编译定义foo
,bar
并且不会以任何方式依赖f2.scm
于其编译的定义同等现有。
现在考虑f3.scm
包含:
(define foo 1)
(load "f2")
Run Code Online (Sandbox Code Playgroud)
(请注意,我假设(load "f2")
(与 相反(load "f2.scm")
)如果可以找到已编译的文件,则加载它,如果找不到,则加载源文件:我认为这是依赖于实现的)。
加载此文件的源代码将执行与加载相同的操作f1.scm
:它将导致foo
和bar
被定义。但编译此文件不会:它会生成一个已编译的文件,稍后加载该文件时将尝试加载f2.scm
. 如果该文件存在,则在加载时,将加载该文件,并且效果将与情况相同include
。如果加载时它不存在,就会发生不好的事情。编译f1.scm
不会导致 中的定义f2.scm
被编译。
根据您的背景,可能值得将其与 C 系列语言进行比较。所做include
的就是#include
所做的:它在读取源文件时拼接它们,而在 C 中(如在许多Scheme/Lisp 系统中),这种情况在编译文件时发生。所做的就是在运行时load
加载代码,在 C 语言中,您需要通过调用动态链接器或其他东西来完成此操作。