Binding 球拍方案中的无约束变量
我试图实现一个数据结构来保持程序的状态(变量绑定)。我用关联列表表示状态,关联列表是键值对的列表 一个示例关联列表是:Binding 球拍方案中的无约束变量,binding,scheme,environment-variables,racket,state,Binding,Scheme,Environment Variables,Racket,State,我试图实现一个数据结构来保持程序的状态(变量绑定)。我用关联列表表示状态,关联列表是键值对的列表 一个示例关联列表是:”((x.3)(y.(12))(z.a))。在本例中,'x键具有值3,'y键具有值'(12),'z键具有值'a 要操纵状态,我有两个函数: (获取绑定状态变量) 此函数用于返回符号var在状态中绑定(分配)到的值 (设置绑定状态变量val) 此函数返回一个与状态相同的新状态,但var绑定到val的情况除外 空状态 此变量对应于没有绑定的状态。它是使用define定义的。 注意
”((x.3)(y.(12))(z.a))
。在本例中,'x
键具有值3
,'y
键具有值'(12)
,'z
键具有值'a
要操纵状态,我有两个函数:
(获取绑定状态变量)
此函数用于返回符号var在状态中绑定(分配)到的值
(设置绑定状态变量val)
此函数返回一个与状态相同的新状态,但var绑定到val的情况除外
空状态
此变量对应于没有绑定的状态。它是使用define定义的。
注意:空状态不是一个函数
> (get-binding (set-binding (set-binding empty-state 'x 3) 'x 4) 'x)
输出4
这是我的密码:
(define (enclosing-environment env) (mcdr env))
(define empty-state null)
(define (get-binding env var)
(define (env-loop env)
(define (scan vars vals)
(cond [(null? vars)
(env-loop (enclosing-environment env))]
[(eq? var (mcar vars))
(mcar vals)]
[else
(scan (mcdr vars)
(mcdr vals))]))
(if (eq? env empty-state)
(error "Unbound variable:" var)
(let ([frame (first-frame env)])
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
(define (set-binding! env var val)
(define (env-loop env)
(define (scan vars vals)
(cond [(null? vars)
(env-loop (enclosing-environment env))]
[(eq? var (mcar vars))
(set-mcar! vals val)]
[else
(scan (mcdr vars)
(mcdr vals))]))
(if (eq? env empty-state)
(error "Unbound variable -- SET!:" var)
(let ([frame (first-frame env)])
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
但是它给出了一个错误,上面写着“unboundvariable--SET!:x”。我该如何预防它?
env
的值传递给set binding时,会抛出该值代码>是eq?
到null
别名为空状态
(var.val)
添加到env
之前,将env
与null
进行比较
空环境
解决方案
不要将
(eq?env empty state)
视为错误条件…至少在初始化之后。您是否意识到会获得绑定
和设置绑定
除了两行之外是相同的功能吗?我建议您将它们合并为一个功能:
(define (get/set-binding when-found when-unbound env var (val))
(define (env-loop env)
(define (scan vars vals)
(cond [(null? vars)
(env-loop (enclosing-environment env))]
[(eq? var (mcar vars))
(when-found val vals)]
[else
(scan (mcdr vars)
(mcdr vals))]))
(if (eq? env empty-state)
(when-unbound var)
(let ([frame (first-frame env)])
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
然后您可以修复错误。问题源于无法修改null
,因此不能通过修改参数引入绑定。您只能通过返回值执行此操作。您可以定义设置绑定代码>如下所示:
(define (set-binding! env var val)
(set/get-binding (λ (val vals)
(set-mcar! vals val)
env)
(λ (var)
(add-binding var val env)) env var val))
(require compatibility/mlist)
(define (add-binding var val env)
(if (null? env)
(mlist (make-frame (mlist var) (mlist val)))
(let ((first (mcar env)))
(set-frame-variables! (mcons var (frame-variables first)))
(set-frame-values! (mcons val (frame-values first)))
env)))
要定义addbinding
,我必须根据您的代码猜测env
的结构。我假设env
是一个多帧列表,frame
是这样的:
(define-struct frame (variables values))
我将进一步假设,如果env
有任何帧,您希望将新变量添加到第一帧
如果这些假设成立,则添加绑定
的定义如下:
(define (set-binding! env var val)
(set/get-binding (λ (val vals)
(set-mcar! vals val)
env)
(λ (var)
(add-binding var val env)) env var val))
(require compatibility/mlist)
(define (add-binding var val env)
(if (null? env)
(mlist (make-frame (mlist var) (mlist val)))
(let ((first (mcar env)))
(set-frame-variables! (mcons var (frame-variables first)))
(set-frame-values! (mcons val (frame-values first)))
env)))
由于此版本的设置绑定
返回一个全新的env
如果您为env
传递null
,则需要始终指定set binding的返回值代码>:
正确用法:
(set! env (set-binding! env var val))
;; If env is null, the new binding is immediately garbage-collected.
(set-binding! env var val)
不正确的用法:
(set! env (set-binding! env var val))
;; If env is null, the new binding is immediately garbage-collected.
(set-binding! env var val)
这是很多代码
如果“环境”是一个查找绑定到名称的值的函数,该怎么办?要获取该值,我们只需将环境应用于名称:
(define (env-get env name)
(env name))
然后,为了扩展环境,通过创建另一个环境,我们会查找名称,但如果找不到,则会使用基本环境:
(define (env-set env name value)
(lambda (nname)
(if (eq? nname name)
value
(env nname))))
但是,如果环境为空,只需出错即可
(define env-empty
(lambda (nname)
(error "Unbound Variable: " nname)))
例如:
> (define env-1 (env-set env-empty 'x 4))
> (define env-2 (env-set env-1 'y '(a b)))
> (env-get env-2 'x)
4
> (env-get env-2 'y)
(a b)
>
使用哈希映射比使用可变关联列表更容易做到这一点……事实上,为什么要使用可变对呢?在我看来,get binding
和set binding
都是纯粹的功能性操作。嘿,我记得Racket的一位主要开发人员提到,他不确定在Racket中包含可变对是否是一个好的举措。我们可以把重点放在这个问题上吗,伙计们,set binding
的描述与您的实现不匹配<代码>设置绑定如果绑定存在(任何位置),则代码>将对其进行变异,否则将抛出错误。这与返回“与状态相同的新状态不同,只是var绑定到val”。