如何创建一个解释器,而不是序列化所有数据,包括lambda函数?

问题描述 投票:0回答:1

在lisp / scheme中,函数WRITE和READ可以输出语言中的任何数据并将其读回,除了lambdas / closures。

我希望能够做的一个例子是:

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

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

((read) 7)

得到结果9

这将如何实施?是否有任何教科书或文件可以解释使用此功能制作翻译的详细信息?谢谢

lambda lisp interpreter
1个回答
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+可能可以忽略(但可能只是)。我还需要收藏这些东西吗?因为如果我不尝试从我保存的文件中取回该定义,如果我在这些事情没有定义的情况下这样做,那将会非常失败。

因此,将一般对象(特别是函数)存储到外部存储器并正确地执行它是一个巨大的问题。特别是我认为它甚至不是一个定义明确的问题。例如,在许多实现中,像+cons这样的东西可能实际上并不是指您所假设的,并且像函数应用程序这样的隐式操作可能不会按照您的想法执行:是否应该使用您正在保存的对象进行保存?

正确地解决这些问题并且通常是非常困难的,并且实际上问题并不充分,甚至没有明确定义的解决方案。

具体问题

但是,这并不意味着在您根本不关心部分或全部这些问题的特定情况下,问题无法解决。

例如,下面是Racket中的一些代码,它们涉及一些简单的案例。

笔记:

  • 这段代码是不安全的:它调用load,这相当于调用eval并具有所有危险;
  • define/stashing的定义可能会错过define语法中的一些重要案例;
  • define/stashing除了在顶层之外不能正常工作(因为根本没有处理词汇环境);
  • 没有尝试处理依赖关系或任何难题;
  • 上面的一个特例是递归函数除非经过仔细编写否则不起作用;
  • 我没有检查这是否是便携式Scheme但可能没有。

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

(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/stashing)然后stashunstash

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

不会,虽然如果你将unstash变成你定义的相同的REPL,它似乎会起作用。

© www.soinside.com 2019 - 2024. All rights reserved.