在lisp / scheme中,函数WRITE和READ可以输出语言中的任何数据并将其读回,除了lambdas / closures。
我希望能够做的一个例子是:
(define f (lambda (x) (lambda (y) (+ x y))))
(write (f 2))
应该输出一些可以读回来并像这样使用的东西
((read) 7)
得到结果9
。
这将如何实施?是否有任何教科书或文件可以解释使用此功能制作翻译的详细信息?谢谢
事实上,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
有依赖性:它依赖于bar
和bar
反过来依赖于y
,以及依赖于cons
和+
可能可以忽略(但可能只是)。我还需要收藏这些东西吗?因为如果我不尝试从我保存的文件中取回该定义,如果我在这些事情没有定义的情况下这样做,那将会非常失败。因此,将一般对象(特别是函数)存储到外部存储器并正确地执行它是一个巨大的问题。特别是我认为它甚至不是一个定义明确的问题。例如,在许多实现中,像+
或cons
这样的东西可能实际上并不是指您所假设的,并且像函数应用程序这样的隐式操作可能不会按照您的想法执行:是否应该使用您正在保存的对象进行保存?
正确地解决这些问题并且通常是非常困难的,并且实际上问题并不充分,甚至没有明确定义的解决方案。
但是,这并不意味着在您根本不关心部分或全部这些问题的特定情况下,问题无法解决。
例如,下面是Racket中的一些代码,它们涉及一些简单的案例。
笔记:
load
,这相当于调用eval
并具有所有危险;define/stashing
的定义可能会错过define
语法中的一些重要案例;define/stashing
除了在顶层之外不能正常工作(因为根本没有处理词汇环境);总的来说,这只是一个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
)然后stash
和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)))))
不会,虽然如果你将unstash
变成你定义的相同的REPL,它似乎会起作用。