4

在方案 R7RS 中既有 aload又有include形式。

包括描述为:

语义:include 和 include-ci 都采用一个或多个文件名表示为字符串文字,应用特定于实现的算法来查找相应的文件,按照指定的顺序读取文件的内容,就像通过重复应用 read 一样,有效地重新- 将 include 或 include-ci 表达式与 begin 表达式一起放置,该表达式包含从文件中读取的内容。两者的区别在于 include-ci 读取每个文件,就好像它以 #!fold-case 指令开头一样,而 include 不是。注意:鼓励实现在包含包含文件的目录中搜索文件,并为用户提供一种方法来指定要搜索的其他目录。

负载描述为:

依赖于实现的操作用于将文件名转换为包含方案源代码的现有文件的名称。加载过程从文件中读取表达式和定义,并在 environment-specifier 指定的环境中按顺序评估它们。如果省略 environment-specifier,则假定为 (interaction-environment)。未指定是否打印表达式的结果。加载过程不影响 current-input-port 和 current-output-port 返回的值。它返回一个未指定的值。理由:为了可移植性,加载必须对源文件进行操作。它对其他类型文件的操作必然因实现而异。

这两种形式的基本原理是什么?我认为这是历史性的。两种形式之间是否有任何导入语义差异?我看到load可以选择包含环境说明符,include但没有。并且include-ci没有直接等价的 using load。但是单独比较loadinclude有什么区别,重要吗?

4

2 回答 2

4

我认为关键的区别在于include语法或者在传统的 Lisp 术语中,它是一个宏),而它load是一个函数。在传统的 Lisp 术语中(在 Scheme 术语中会有更正式的定义,我无法给出),这意味着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不会导致 in 中的定义f2.scm被编译。


根据您的背景,可能值得将其与 C 系列语言进行比较。做什么include就是做什么#include:它在读取源文件时拼接在源文件中,而在 C(如在许多 Scheme/Lisp 系统中)中,这发生在编译文件时。什么是在运行时load加载代码,在 C 中,您需要通过调用动态链接器或其他东西来完成。

于 2018-01-27T14:16:28.740 回答
2

从历史上看,Lisp 实现不提供模块系统。

大型程序使用加载来运行一组指令,加载函数通过从文件中逐个读取 S 表达式并将它们传递给 eval 来运行REPL 脚本。

另一方面,Include用于将从文件中读取的代码内联到您的代码中。它不评估代码。

...将 include 或 include-ci 表达式替换为包含从文件中读取的内容的 begin 表达式

添加的“开始”准备从文件中读取的代码以进行顺序评估。

资料来源:问题引用,球拍文档

于 2018-01-26T14:47:16.303 回答