Racket文档表明,Racket具有单独的表单:require
,load
,include
和import
。许多其他语言只包含其中一种,通常用于同义词(尽管显然存在语言特定差异,例如C中的#include
和Java中的import
。
由于Racket拥有所有这四种,因此每种产品之间有什么区别?我应该使用哪种产品?此外,如果每个都有特定用途,我何时应该使用替代类型?另外,this question似乎表明require
(与provide
配对)是首选,为什么?
答案 0 :(得分:4)
你是对的,你想要的默认值几乎总是require
(与provide
配对)。这些表格与Racket的modules
密切相关,可以让您更轻松地确定哪些变量应该放在哪些文件中。例如,以下文件定义了三个变量,但只导出2。
#lang racket ; a.rkt
(provide b c)
(define a 1)
(define b 2)
(define c 3)
根据the Racket style guide,提供理想情况下应该是#lang
之后文件中的第一个表单,以便您可以轻松地告诉模块提供的内容。在某些情况下,这是不可能的,但在您开始制作自己想要公开发布的Racket库之前,您可能不会遇到这些问题。就我个人而言,我仍然会在require
之前放置一个文件provide
,但我确实有时会感到沮丧。
在repl或其他模块中,您现在可以要求此文件并查看它提供的变量。
Welcome to Racket v6.12.
> (require "a.rkt")
> c
3
> b
2
> a
; a: undefined;
; cannot reference undefined identifier
; [,bt for context]
有ways to get around this,但这可以作为模块传达其显式导出内容的方式。
这是一个更加动态的需求变体。通常,您不应该使用它,而是在需要动态加载模块时使用dynamic-require
。在这种情况下,load
实际上是require
在幕后使用的原语。如果你明确地想要模仿顶级(很明显,你几乎从不这样做),那么加载是一个很好的选择。虽然在极少数情况下,我仍会指导您使用racket/load
语言。这与每个表单直接输入到repl中的方式完全相互作用。
#lang racket/load
(define x 5)
(displayln x) ; => prints 5
(define x 6)
(displayln x) ; => prints 6
包含类似于C中的#include
。您应该使用它的情况更少。 include
表单获取给定路径的s表达式语法,并将其直接放在include
表单所在的文件中。首先,这可以作为一个很好的解决方案,允许您将单个模块拆分为多个文件,或者有一个模块'你想要放入多个文件。然而,有更好的方法可以在不使用include
的情况下完成这两件事,也不会带来令人困惑的副作用。 1 有一点要记住,如果您仍然坚持使用import
,您导入的文件可能不应该有#lang
行,除非您明确要嵌入子模块。 (在这种情况下,除了require
之外,您还需要有include
表单。
最后,import
实际上并不是Racket的核心部分,而是unit system的一部分。单元在某些方面类似于模块,但允许循环依赖(单元A可以取决于单元B,而单元B取决于单元A)。近年来,由于他们的语法开销,他们已经失宠了。
与其他表单import
(以及export
)不同,请使用signatures,并依赖外部linker来决定哪些实际单位应该链接在一起。单位本身是一个复杂的主题,值得他们自己提出如何创建和链接它们的问题。
TLDR;使用require
和provide
。它们是最受支持且最容易理解的。其他形式确实有其用途,但应考虑到“高级用途”和“高级用途”。仅
1 这些副作用与您对C中#include
的期望相同。例如,顺序很重要,并且表达式也以非常不可预测的方式混合。 子>