【问题标题】:Help me write my LISP :) LISP environments, Ruby Hashes帮我写我的 LISP :) LISP 环境,Ruby Hashes
【发布时间】:2011-01-12 04:44:43
【问题描述】:

我正在用 Ruby 实现 LISP 的初级版本,只是为了让自己熟悉一些概念。我的实现基于 Peter Norvig 的 Lispy (http://norvig.com/lispy.html)。

我在这里缺少一些东西,我会很感激一些帮助......

他将 Python 的 dict 子类化如下:

class Env(dict):
    "An environment: a dict of {'var':val} pairs, with an outer Env."
    def __init__(self, parms=(), args=(), outer=None):
        self.update(zip(parms,args))
        self.outer = outer
    def find(self, var):
        "Find the innermost Env where var appears."
        return self if var in self else self.outer.find(var)

然后他继续解释他为什么这样做,而不仅仅是使用字典。然而,由于某种原因,他的解释不断地从我的眼睛里闪过,又从我的脑后传出来。

为什么不使用dict,然后在eval函数内部,当需要创建新的“子环境”时,只需获取现有的dict并更新需要更新的键/值对,然后传递新的字典进入下一个评估?

Python 解释器不会跟踪以前的“外部”环境吗?并且递归的性质不会确保将值从“内部”拉到“外部”吗?

我正在使用 Ruby,并尝试以这种方式实现。但是有些东西不起作用,可能是因为这个,也可能不是。这是我的 eval 函数, env 是一个常规哈希:

def eval(x, env = $global_env)
  ........ 
  elsif x[0] == "lambda" then
    ->(*args) { eval(x[2], env.merge(Hash[*x[1].zip(args).flatten(1)])) }
  ........ 
end

当然重要的是“lambda”行。

如果存在功能上的差异,那么我在这里所做的与 Norvig 对他的 Env 类所做的有什么重要的不同?有人可以向我描述一下两者会偏离的情况吗?

如果没有区别,那么也许有人可以告诉我为什么 Norvig 使用 Env 类。谢谢:)

【问题讨论】:

  • 理解Scheme最好的方法就是实现Scheme!

标签: python ruby programming-languages lisp scheme


【解决方案1】:

如果 Lisp 中的变量绑定是不可变的,那么复制环境就相当于链接它们。但请考虑以下情况:

(define main
  (lambda ()
    (define i 0)
    (define increment-i
      (lambda ()
        (set! i (+ i 1))))
    (increment-i)
    i))

(main)

如果increment-i的环境完全独立于main的环境(因为是副本),i的突变在main中将不可见,以上代码将返回0 .另一方面,如果环境已链接,则返回值将是 1,正如预期的那样。

【讨论】:

  • 啊,我相信就是这样。谢谢!
【解决方案2】:

我的 Python 不是那么好,我的 Lisp 也很生疏,但我会猜测发生了什么:即使在声称没有指针的语言中,你也无法摆脱指针。

您所说的eval 不会复制dict,它只会复制引用,您最终会得到两个引用(AKA 变量) (即指向)相同的底层对象。发生的事情基本上是这种情况的一种变体:

a = [ 'a', 'b', 'c' ]
b = a
a[1] = 'x'

puts a
# => ["a", "x", "c"]
puts b    
# => ["a", "x", "c"]

他的Env 类允许在eval 内部修改环境而不需要修改外部环境,而find 方法允许您无需了解就可以访问外部环境;此外,修改最终会出现在内部环境中,而这些更改将掩盖外部环境中的相应值; get 操作会访问本地环境和外部环境,set 操作只会修改内部环境。

我猜你可以将Env 称为对象级别(而不是类级别)外观。

我不确定您的 ruby​​ 实现有什么问题,看起来您正在制作环境哈希的修改副本。你能澄清一下“虽然有些东西不起作用”吗?

澄清:Env 是一个符号表。 find 中的包装/重定向内容允许您访问外部符号表,同时保护它免受添加新符号的影响,新符号只会添加到内部符号表中。 Env 本质上是管理关闭。

【讨论】:

  • 感谢您的回复,合乎逻辑。但是,我不认为在这种情况下会出现这种情况,因为 Ruby 中的 AFAIK “合并”创建了一个新的哈希,而旧的哈希则保持不变。如果我错了,有人在这里纠正我:)
  • Ruby 的 Hash#merge 确实返回了一个新的 Hash (ruby-doc.org/core/classes/Hash.html#M000759),所以我将把这部分从我的答案中删除。也许你可以比“虽然有些东西不起作用”更具体一点。
  • 好吧,这不值得讨论...我实际上并不是在问如何修复 interp 的部分。那是行不通的——我主要是好奇 Norvig 为什么要制作那个 Env 类。
  • 最后检查我的“澄清”。
  • 你会需要访问外部吗?由于 eval 函数传递了一个新的散列,“以前的”eval 函数仍然会有旧散列的副本,不是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
相关资源
最近更新 更多