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”。