如果在球拍语言中定义了变量,你如何能有不同的行为?
答案 0 :(得分:9)
有几种方法可以做到这一点。但我怀疑这些都不是你想要的,所以我只提供指向函数的指针(并解释每个函数的问题):
namespace-variable-value
是一个从某个命名空间中检索顶级变量值的函数。这仅适用于REPL交互和REPL代码,因为模块中定义的代码无论如何都不会使用这些东西。换句话说,您可以使用此函数(以及相应的namespace-set-variable-value!
)来获取值(如果有)并设置它们,但这些值的唯一用途是在模块中本身不存在的代码中。换句话说,使用这个工具就像保留一个将符号映射到值的哈希表一样好,只是在REPL上稍微方便一点,因为你只需键入名称......
更有可能的是,这些事情都是在宏中完成的。第一种方法是使用特殊的#%top
宏。对于未知绑定的模块中的所有名称,将自动插入此宏。这个宏所做的通常是抛出一个错误,但是你可以在你的代码中重新定义它(或者用你自己的语言重新定义它),用这些未知的名字做其他事情。
稍微复杂一点的方法是使用identifier-binding
函数 - 再次使用宏,而不是在运行时 - 并使用它来获取信息关于给宏的一些名称,并根据该名称决定扩展到什么名称。
最后两个选项是更有用的选项,但它们不是新手级别的宏,这就是我怀疑你提出错误问题的原因。为了澄清,您可以使用它们来编写一种defined?
特殊形式,以检查是否定义了某个名称,但该问题是基于其余代码由宏回答的问题,因此问这个问题并不是很有用。如果你想要这样的东西可以在你使用这种谓词的其他动态语言中启用那种代码,那么最好的方法就是重新定义#%top
来进行某种查找(哈希表或者全局命名空间)而不是抛出编译错误 - 但同样,明确使用哈希表之间的区别主要是装饰性的(同样,这不是新手的事情)。
答案 1 :(得分:6)
首先,阅读Eli的回答。然后,根据Eli的回答,您可以通过这种方式实现defined?
宏:
#lang racket
; The macro
(define-syntax (defined? stx)
(syntax-case stx ()
[(_ id)
(with-syntax ([v (identifier-binding #'id)])
#''v)]))
; Tests
(define x 3)
(if (defined? x) 'defined 'not-defined) ; -> defined
(let ([y 4])
(if (defined? y) 'defined 'not-defined)) ; -> defined
(if (defined? z) 'defined 'not-defined) ; -> not-defined
它适用于这个基本情况,但它有一个问题:如果z
未定义,if
认为已定义并使用其值的分支将引发编译时错误,因为正常if
在运行时(动态)检查其条件值:
; This doesn't work because z in `(list z)' is undefined:
(if (defined? z) (list z) 'not-defined)
所以你可能想要的是一个if-defined
宏,它在编译时(而不是在运行时)告诉if
要采取的分支:
#lang racket
; The macro
(define-syntax (if-defined stx)
(syntax-case stx ()
[(_ id iftrue iffalse)
(let ([where (identifier-binding #'id)])
(if where #'iftrue #'iffalse))]))
; Tests
(if-defined z (list z) 'not-defined) ; -> not-defined
(if-defined t (void) (define t 5))
t ; -> 5
(define x 3)
(if-defined x (void) (define x 6))
x ; -> 3