【发布时间】:2013-12-03 07:40:35
【问题描述】:
如果变量是用球拍语言定义的,或者没有用球拍语言定义,你怎么能有不同的行为?
【问题讨论】:
-
你到底是什么意思?您是否尝试在某处使用未定义的变量?我无法回答您的问题。
如果变量是用球拍语言定义的,或者没有用球拍语言定义,你怎么能有不同的行为?
【问题讨论】:
有几种方法可以做到这一点。但我怀疑这些都不是你想要的,所以我只会提供指向函数的指针(并解释每个函数的问题):
namespace-variable-value 是一个从某个命名空间检索顶级变量值的函数。不过,这仅对 REPL 交互和 REPL 代码有用,因为在模块中定义的代码无论如何都不会使用这些东西。换句话说,您可以使用这个函数(以及相应的namespace-set-variable-value!)来获取值(如果有的话)并设置它们,但是这些值的唯一用途是在模块本身不存在的代码中。换句话说,使用这个工具就像保留一个将符号映射到值的哈希表一样好,只是它在 REPL 中稍微方便一些,因为你只需键入名称......
更有可能的是,这类事情是在宏中完成的。第一种方法是使用特殊的#%top 宏。此宏会自动插入到模块中未知绑定的所有名称中。这个宏通常会抛出一个错误,但您可以在代码中重新定义它(或编写您自己的语言来重新定义它),用这些未知名称执行其他操作。
一个稍微复杂一点的方法是使用identifier-binding 函数——同样,在一个宏中,而不是在运行时——并使用它来获取一些信息为宏指定的名称,并根据该名称决定要扩展的内容。
最后两个选项更有用,但它们不是新手级别的宏,这就是为什么我怀疑你问错了问题。澄清一下,您可以使用它们编写一种defined? 特殊形式来检查是否定义了某个名称,但该问题将由宏根据其余代码回答,所以不是问它真的很有用。如果您想要这样的东西可以在使用此类谓词的其他动态语言中启用那种代码,那么解决此问题的最佳方法是重新定义 #%top 以进行某种查找(哈希表或全局命名空间) 而不是抛出编译错误——但同样,这与显式使用哈希表之间的区别主要是装饰性的(同样,这不是新手)。
【讨论】:
exn:fail:contract:variable? 异常。
首先,阅读 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
【讨论】: