【问题标题】:Unbound variable in Racket-SchemeRacket-Scheme 中的未绑定变量
【发布时间】:2015-04-22 00:32:23
【问题描述】:

我正在尝试实现一个数据结构来保持程序的状态(变量绑定)。我用关联列表表示状态,关联列表是键值对列表。

一个示例关联列表是:'((x . 3) (y . (1 2)) (z . a))。在此示例中,键 'x 的值是 3'y 的值是 '(1 2)'z 的值是 'a

为了操纵状态,我有两个函数:

  1. (get-binding state var)

    此函数返回值符号 var 绑定(分配)到状态。

  2. (set-binding state var val)

    此函数返回一个与 state 相同的新状态,不同之处在于 var 绑定到 val。

  3. empty-state

    此变量对应于没有绑定的状态。它是使用define定义的。 注意:empty-state 不是函数。

例子:

> (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))

但它给出了一个错误,显示“未绑定变量 -- SET!: x”。如何预防?

【问题讨论】:

  • 使用哈希映射而不是可变关联列表会容易得多。
  • ...事实上,你为什么要使用可变对呢?在我看来 get-bindingset-binding 是纯粹的函数操作。
  • 嘿,我记得 Racket 的主要开发人员之一提到他不确定在 Racket 中包含可变对毕竟是一个好举措。
  • 我们能不能把重点放在这里,伙计们?
  • set-binding 的描述与您的实现不匹配。 set-binding! 正在改变一个绑定,如果一个存在(任何地方),否则抛出一个错误。这与返回“与状态相同的新状态,但 var 绑定到 val 除外”不同。

标签: binding scheme environment-variables racket state


【解决方案1】:

你知道get-binding!set-binding 除了两行之外是同一个函数吗?我建议你将它们组合成一个函数:

(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,因此无法通过修改参数来引入绑定。你只能通过返回值来做到这一点。你可以这样定义set-binding!

 (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))

要定义add-binding,我必须根据您的代码对env 的结构进行一些猜测。我将假设env 是一个帧列表,而frame 是这样的:

(define-struct frame (variables values))

我将进一步假设如果env 有任何帧,您希望将新变量添加到第一帧。

如果这些假设成立,那么add-binding 将被定义为:

(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)))

由于此版本的set-binding! 返回一个全新的env,如果您将null 传递给env,则需要始终分配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)

【讨论】:

    【解决方案2】:

    那是很多代码...

    如果“环境”是一个查找绑定到名称的值的函数怎么办?要获得值,我们需要做的就是将环境应用于名称:

    (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)
    > 
    

    【讨论】:

      【解决方案3】:

      场所

      1. 错误在代码中定义。

      2. 当传递给set-binding!env 的值是eq?null 别名为empty-state 时,会抛出此错误。

      3. 在将(var . val) 添加到env 之前,将envnull 进行比较。

      猜想

      初始环境是空的...即null 或别名为empty-environment

      解决方案

      不要将(eq? env empty-state) 视为错误条件...至少在初始化之前。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-11
        • 1970-01-01
        • 1970-01-01
        • 2012-10-24
        • 2018-11-10
        相关资源
        最近更新 更多