帮我写我的LISP :) LISP环境,Ruby Hashes

时间:2011-01-12 04:44:43

标签: python ruby programming-languages lisp scheme

我在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是常规Hash:

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类。谢谢:))

2 个答案:

答案 0 :(得分:5)

如果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,如预期的那样。

答案 1 :(得分:1)

我的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基本上管理了关闭。