何时在lisp解释器

时间:2016-05-21 18:48:43

标签: memory-management lisp interpreter

我从头开始编写一个简单的lisp解释器。我有一个全局环境,在评估文件中的所有表单时绑定了顶级变量。当评估文件中的所有表单时,将释放顶级env和其中的所有键值数据结构。

当求值程序遇到lambda表单时,它会创建一个包含3个内容的PROC对象:应用过程时要绑定在本地框架中的参数列表,函数,以及指向其创建环境的指针。例如:

(lambda (x) x)

会产生内部的东西,如:

PROC- args: x, 
      body: x, 
      env: pointer to top level env

当应用PROC时,将为该帧创建一个新环境,并在那里暂存本地绑定,以允许使用适当的绑定来评估正文。此框架环境包含指向其闭包的指针,以允许在THAT内部进行变量查找。在这种情况下,那将是全球环境。在评估PROC主体之后,我可以释放与其关联的所有单元格,包括其框架环境,并在没有内存泄漏的情况下退出。

我的问题是更高阶的功能。考虑一下:

(define conser 
    (lambda (x)
        (lambda (y) (cons x y))))

一个函数,它接受一个参数并产生另一个函数,该函数将该参数包含在您传递给它的内容中。所以,

(define aconser (conser '(1)))

会产生一个函数,它将'(1)传递给任何传递给它的东西。例如:

(aconser '(2)) ; ((1) 2) 

我的问题是aconser必须保留指向其创建环境的指针,即conser通过调用(conser '(1))生成的指针。当aconser应用PROC时,其框架必须指向定义conser时存在的aconser框架,因此我无法释放框架申请后conser。我不知道如何/最好的方法来释放与lambda帧相关的内存,并且还支持这种持久的高阶函数。

我可以想到一些解决方案:

  • 某种类型的ARC

  • 将封闭环境复制到评估PROC的框架中

这似乎是隐含的内容here。因此,我不是将PROC对象中的指针保存到其闭包中,而是...复制闭包环境并直接在单元格中存储指向 的指针?这不只是将罐头更深一层并导致同样的问题吗?

  • 在高阶函数体内的读取时间递归地替换标签

我担心我可能会遗漏一些非常简单的内容,而且我很好奇这个过程在lisp和其他语言的其他实现中是如何支持的。我没有太多的运气寻找答案,因为这个问题非常具体,甚至对于这个实施(我承认我只是脱离了我的帽子作为学习项目)而且我能够找到的大部分内容只是解释了细节从正在实施的语言的角度来看,而不是从语言正在实施的语言中解决。

Here is a link到我的来源中的相关行,如果它有用,我很乐意详细说明这个问题是否不够详细,无法彻底描述问题。谢谢!

1 个答案:

答案 0 :(得分:1)

通常在天真的解释器中处理它的方法是使用垃圾收集器(GC)并在GC堆中分配激活帧。因此,您永远不会明确释放这些帧,您可以在适用时让GC释放它们。

在更复杂的实现中,您可以使用稍微不同的方法:

  • 创建闭包时,不要存储指向当前环境的指针。相反,复制闭包使用的那些变量的值(它被称为lambda的自由变量)。 并更改封闭体的主体以使用这些副本,而不是在环境中查找这些变量。它被称为闭包转换
  • 现在,您可以将环境视为普通堆栈,并在退出范围后立即释放激活帧。
  • 您仍然需要GC来决定何时可以释放闭包
  • 这又需要进行"赋值转换&#34 ;:复制变量值意味着如果这些变量被修改则会改变语义。因此,要恢复原始语义,您需要查找那些被复制到闭包中的变量"以及"修改",并将它们变成"参考单元" (例如 cons 单元格,您将值保留在car中),以便副本不再复制该值,而只是复制对实际位置的引用价值保持在哪里。 [旁注:这样的实现显然意味着避免setq并使用更具功能性的风格可能最终会更有效率。 ]

更复杂的实现还具有以下优点:它可以提供安全的空间语义:闭包只会保留它实际引用的数据,这与封闭结束的天真方法相反引用整个周围的环境,因此可以阻止GC收集实际未被引用的数据,但恰好在闭包捕获时恰好位于环境中。