如何使一个解释器能够序列化包括lambda函数在内的所有数据?

时间:2019-02-12 10:50:40

标签: lambda lisp interpreter

在lisp / scheme中,除了lambda / closures外,WRITE和READ函数可以用该语言输出任何数据,然后将其读回。

我想做的一个例子是:

(define f (lambda (x) (lambda (y) (+ x y))))
(write (f 2))

应该输出一些可以读回并像这样使用的东西

((read) 7)

获得结果9

这将如何实施?是否有任何教科书或文档说明使用此功能进行口译的详细信息?谢谢

1 个答案:

答案 0 :(得分:0)

实际上Lisp系统不能读写任意对象:只有足够简单的对象。以Common Lisp为例,给出

(defclass foo ()
  ((x :reader foo-x :initarg :x)))

然后:

> (make-instance 'foo :x 2)
#<foo 40200A9813>

那是无法阅读的东西。

一般问题

要使Lisp / Scheme能够将常规对象写入(在下面“隐藏”)到文件中,并从文件中读取(在“ unstash”以下)它们,至少需要足够的自省才能知道哪些部分的对象。为了能够可靠地存储和取消存储对象,您还需要能够知道对象所依赖的对象。对于任何函数或闭包都不存在任何可移植的方式,而且依赖问题很严重。

作为一个例子,考虑一下(现在在Scheme中):

(define y 3)

(define (bar x)
  (+ x y))

(define (foo x)
  (cons (bar x) x))

如果我说(stash foo "/tmp/foo")应该怎么办?

  • 作为最低限度的要求,系统必须能够充分理解foo,以便以某种形式存储它,以便在加载时可以对其进行重构。
  • 但是foo有依赖关系:它依赖于barbar依次依赖于y,也依赖于cons和{{1 }}可能可以忽略(但仅可能)。我还需要藏起来那些东西吗?因为如果我不尝试从存放的文件中撤回定义,如果我在没有定义的上下文中执行该操作,将导致严重失败。

因此,将通用对象(尤其是功能)存储到外部存储中并正确执行是一个巨大的问题。特别是,我认为这甚至不是一个定义明确的问题。例如,在许多实现中,诸如++之类的东西实际上可能并不代表您所假设的内容,而诸如函数应用程序之类的隐式操作可能不符合您的想法:应该 that 与要保存的对象一起保存吗?

正确地 一般地解决这些问题非常困难,实际上,这些问题的处境还不够好,甚至没有明确的解决方案。

特定问题

但是,这并不意味着该问题在某些情况下是无法解决的,因为您根本不关心其中的某些或全部问题。

这是Racket中的一些代码,例如一些简单的案例。

注释:

  • 此代码不安全:它调用cons等同于调用load并具有所有危险;
  • eval的定义可能错过了define/stashing语法中的一些重要情况;
  • define只能在顶层使用(因为根本没有处理词法环境),否则将无法正常工作;
  • 没有尝试处理依赖关系或任何困难的问题;
  • 上述情况的一个特殊情况是,递归函数除非仔细编写,否则将不起作用;
  • 我没有检查这是否是可移植的Scheme,但可能不是。

总的来说,这只是一个骇人听闻的演示,它很容易处理一些在实践中可能很有趣的特殊情况。

define/stashing

要使用此功能,您需要使用(define definition-map (make-hasheqv)) (define (save-definition! thing source-form) (hash-set! definition-map thing source-form) thing) (define (get-definition thing) (hash-ref definition-map thing)) (define (clear-definitions!) (hash-clear! definition-map)) (define-syntax define/stashing ;; This is probably missing cases (syntax-rules () [(_ (name) form ...) (define name (save-definition! (lambda () form ...) '(lambda () form ...)))] [(_ (name arg ...) form ...) (define name (save-definition! (lambda (arg ...) form ...) '(lambda (arg ...) form ...)))] [(_ (name arg ... . rest) form ...) (define name (save-definition! (lambda (arg ... . rest) form ...) '(lambda (arg ... . rest) form ...)))] [(_ name value) (define name (save-definition! value 'value))])) (define (stash thing file) (call-with-output-file file (λ (o) (write (get-definition thing) o) thing) #:mode 'text #:exists 'truncate/replace)) (define (unstash file) (load file)) 而不是define/stashing(模块可以使define 成为 define),然后define/stashingstash

unstash

如果要隐藏递归函数,则需要使用本地定义编写它们:

> (define/stashing (cons-1 x) (cons x 1))
> (stash cons-1 "/tmp/x")
#<procedure>
> (cons-1 2)
'(2 . 1)
> ((unstash "/tmp/x") 2)
'(2 . 1)

将起作用,而

(define/stashing (fact n)
  (let floop ([m 1] [t 1])
    (if (= m n)
        (* t m)
        (floop (+ m 1) (* t m)))))

不会,但是如果您将其(define/stashing (fact n) (if (= n 1) n (* n (fact (- n 1))))) 插入定义的相同REPL中,它会出现