如果我想在运行时选择一个模块,我可以使用[ dynamic-require
'1。如果我想需要安装的软件包,例如:
(dynamic-require 'racket/match 'match)
这将(如果我安装了racket/match
),需要racket/match
并评估该库中的match
。
但是,当我想需要一个本地的非安装模块时,我会遇到麻烦。假设我有一些称为eat.rkt
的模块,它提供了一个函数:bite
:
#lang racket ;; eat.rkt
(provide bite)
(define (bite) "Nom nom")
现在说,我们要制作一个需要eat.rkt
的lunch.rkt
模块,并调用该功能。此外,假设我将它们放在相同的目录中:
#lang racket ;; lunch.rkt
(require "eat.rkt")
(bite) ; => Nom Nom
这很好,因为我使用了静态要求,但是当我想做dynamic-require
:
#lang racket ;; lunch2.rkt
(define bite (dynamic-require "eat.rkt" 'bite)
(bite) ; => Nom Nom
虽然这看起来不错,但事实证明,基于模块的路径而是current-directory
所需的模块,而是在CC_11上。因此,如果我在目录中运行该程序,则可以定义模块,这很好,但是如果我在另一个目录中,一切都会破裂:
$ racket lunch2.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch2.rkt
; default-load-handler: cannot open module file
显然,如果我知道它在哪里,我可以将current-directory
更改为该模块的目录。但是,如果我不知道这个模块的路径,有什么方法可以得到吗?或者,更直接地,相对于要求的模块路径,dynamic-require
模块是否有可能?
define-runtime-path
表单定义了一个路径,该路径将在运行时可用,并且独立于current-directory
。使用它来定义要动态需要的模块的路径,例如:
#lang racket
(require racket/runtime-path)
(define-runtime-path eat "eat.rkt")
(dynamic-require eat 'bite)
dynamic-require
相对于当前模块路径(即保存模块的路径)的最简单方法是获取该模块路径并将其附加。
您可以使用#%variable-reference
和variable-reference->module-path-index
进行此操作。(您可能还需要在其他情况下使用variable-reference->resolved-module-path
,但我们不会在这里这样做。)编写这两个功能使我们对所定义的模块有一个module-path-index?
。(或通常,#%variable-reference
来自。)
所以,我们可以一个如下的变量:
(define here (variable-reference->module-path-index (#%variable-reference)))
现在,剩下的就是将此here
路径与我们想要的模块相对路径组成。如果您愿意,我们正在寻找build-path
的模块路径类比。
事实证明,我们正在寻找的函数IS:module-path-index-join
,它采用基本路径和相对路径并将它们添加在一起。结果看起来像:
(module-path-index-join "eat.rkt" here)
(是的,它是您对build-path
的期望的倒退,但基本路径在此功能中排名第二。)
结果模块,lunch3.rkt
看起来像:
#lang racket
(define here (variable-reference->module-path-index (#%variable-reference)))
(define bite (dynamic-require (module-path-index-join "eat.rkt" here) 'bite))
现在lunch3.rkt
将需要eat.rkt
相对于其定义的位置,而不是基于current-directory
:
$ racket lunch3.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch3.rkt
"Nom Nom"
感谢Matthew Flatt帮助此答案。