使'define'在Racket中计算其第一个参数
在LISP的某些方言中,使'define'在Racket中计算其第一个参数,racket,Racket,在LISP的某些方言中,SET和SETQ之间存在区别,第一个参数计算其第一个参数,因此需要使用(SET(QUOTE…)语法 由于在Racket中,定义中不需要引用,define的行为类似于SETQ。 是否有类似于SET的球拍功能?如果没有,怎么写 我尝试了(define(SET a b)(define(eval a)b)b),但当使用(提供集)将其提供给其他语言时,它似乎不起作用。下面是我对这个问题的快速尝试: ;; lib.rkt #lang racket/base (provide (re
SET
和SETQ
之间存在区别,第一个参数计算其第一个参数,因此需要使用(SET(QUOTE…)
语法
由于在Racket中,定义中不需要引用,define
的行为类似于SETQ
。
是否有类似于SET
的球拍功能?如果没有,怎么写
我尝试了
(define(SET a b)(define(eval a)b)b)
,但当使用(提供集)
将其提供给其他语言时,它似乎不起作用。下面是我对这个问题的快速尝试:
;; lib.rkt
#lang racket/base
(provide (rename-out [@set set]
[@#%top #%top]
[@set! set!]
[@define define]))
(require syntax/parse/define)
(define env (make-hash))
(define (set x v stx)
(unless (hash-has-key? env x)
(raise-syntax-error #f "undefined id" stx))
(hash-set! env x v))
(define-simple-macro (@#%top . x)
(hash-ref
env
'x
(λ () (raise-syntax-error #f "unbound id" (quote-syntax x)))))
(define (@set x v)
(set x v x))
(define-simple-macro (@set! x:id v)
(set 'x v (quote-syntax x)))
(define-simple-macro (@define x:id v)
(begin
(when (hash-has-key? env 'x)
(raise-syntax-error #f "id already defined" (quote-syntax x)))
(hash-set! env 'x v)))
请注意,这不同于原来的球拍在几个方面。例如:
set代码>现在无法使用集-变压器
define
不能用于定义函数define
不能用于隐藏标识符还请注意,您只能通过
定义设置标识符。如果您想设置通过lambda
、let
等定义的标识符,您也需要重新定义这些结构。我会做得更简单、更详细
由于所有参数都经过计算,集合
或者说define%
可以定义为一个函数
(define (define% x y)
(eval `(define ,x ,y)))
使用lambda
使用旧式表单时,甚至可以使用define%
定义函数
(define 'ab (lambda (x y) (+ x y)))
(ab 3 5) ;; 7
它甚至在范围方面表现得很正确
(define (foo x)
(define% 'bar (lambda (x) (+ 1 x)))
(bar (bar (bar x))))
foo
;; #<procedure:foo>
bar
; bar: undefined;
; cannot reference undefined identifier
; [,bt for context]
(foo 3)
6
;; after first call however, bar is available in global environment
;; as pointed out by @AlexKnauf
bar
;; #<procedure:bar>
(定义(foo x)
(定义%巴(λ(x)(+1 x)))
(巴(巴(巴x)))
福
;; #
酒吧
; 酒吧:未定义;
; 无法引用未定义的标识符
; [,上下文为bt]
(富3)
6.
;; 然而,在第一次调用之后,bar在全局环境中可用
;; 正如@AlexKnauf所指出的
酒吧
;; #
因此,存在一些范围界定问题
(let ((x 0))
(define% 'counter (lambda () (set! x (+ x 1)) x)))
counter
;; #<procedure>
(counter)
;; 1
(counter)
;; 2
(counter)
;; 3
(let((x 0))
(定义%'计数器(lambda()(set!x(+x1))x)))
柜台
;; #
(柜台)
;; 1.
(柜台)
;; 2.
(柜台)
;; 3.
Racket中重要的一点是变量绑定是在编译时确定的,而不是在运行时确定的,因此define
变量不能在运行时“计算”。但是,可以在编译时使用宏对其求值。你能澄清一下你想要它做什么吗?我想要它是因为我正在使用Racket来构建一种教育语言,我需要一个SET
函数如果我理解正确,在你的实现中,必须在SET和SET之前定义一个变量!可以使用命令吗?是的,就是这样设置的代码>在球拍中工作。如果您不喜欢,那么请删除检查(除非(hash有key?env x)
)
。我认为您应该警告eval
的一些危险,这可能是初学者的一个陷阱,他们还不知道何时使用它,何时使用它不是一个好情况不-使用eval
是安全的。当然,如果可以避免的话,就不应该使用eval
。但在这种特殊情况下,eval
是“好的”,我称之为“evalovermacrocall”。我在这里和Vsevolod Dyomkin讨论过。在我看来,eval
的正确使用是一项资产。我认为在这种特殊情况下也是不安全的:答案中的第三个代码块在两种情况下中断:(a)当foo
的定义放入一个模块时(仅在repl或更大的eval中有效),以及(b)在repl中,调用foo
后它会中断:调用(foo 10)
后,条在其作用域之外定义为true。。。在(foo 1)
的第一个cll之后,条在外部可见。。。
(let ((x 0))
(define% 'counter (lambda () (set! x (+ x 1)) x)))
counter
;; #<procedure>
(counter)
;; 1
(counter)
;; 2
(counter)
;; 3