有谁知道我如何找出 lambda 表达式中的自由变量?自由变量是不属于 lambda 参数的变量。
我目前的方法(这让我无处可去)是简单地使用 car 和 cdr 来完成表达式。我的主要问题是确定一个值是变量还是方案原语之一。有没有办法测试某些东西是否评估为方案的内置函数之一?例如:
(is-scheme-primitive? 'and)
;Value: #t
我正在使用 MIT 方案。
有谁知道我如何找出 lambda 表达式中的自由变量?自由变量是不属于 lambda 参数的变量。
我目前的方法(这让我无处可去)是简单地使用 car 和 cdr 来完成表达式。我的主要问题是确定一个值是变量还是方案原语之一。有没有办法测试某些东西是否评估为方案的内置函数之一?例如:
(is-scheme-primitive? 'and)
;Value: #t
我正在使用 MIT 方案。
对于任意 MIT Scheme 程序,没有任何方法可以做到这一点。一个问题是您描述的功能无法正常工作。例如,这不使用“方案原语” and
:
(let ((and 7)) (+ and 1))
但它肯定使用符号'and
。
另一个问题是很多东西,比如and
,都是用宏实现的特殊形式。您需要知道程序中的所有宏都扩展成什么,才能弄清楚程序中使用了哪些变量。
要完成这项工作,您需要限制您接受作为输入的程序集。最好的选择是将其限制为“完全扩展”的程序。换句话说,您要确保free-variables
函数的输入中没有任何宏的使用。
为此,您可以使用expand
许多 Scheme 系统提供的功能。不幸的是,从在线文档来看,MIT Scheme 似乎没有提供此功能。如果您能够使用不同的系统,Racket会提供该expand
功能以及local-expand
在宏内部正常工作的功能。
Racket 实际上还提供了您要求的free-variables
功能的实现,正如我所描述的,它需要完全扩展的程序作为输入(例如expand
or的输出local-expand
)。您也可以查看源代码。
有关完全扩展源代码所涉及问题的详细讨论,请参阅Flatt、Culpepper、Darais 和 Findler即将发表的这篇论文。
这实际上是解决这个问题的一个非常糟糕的方法。它作为一种非常快速和肮脏的方法可以实现 OP 的基本目标,但不能经受任何“现实生活”用例。请参阅有关此答案的评论中的讨论以及其他答案以了解原因。
此解决方案可能不太理想,但它适用于您想在 mit-scheme 的 REPL 环境中提供的任何 lambda 形式(请参阅编辑)。我使用的程序的文档可以在mit.edu 文档站点上找到。 get-vars
接受一个引用lambda
并返回一个对列表。每对的第一个元素是符号,第二个元素是返回的值environment-reference-type
。
(define (flatten lst)
(cond ((null? lst) ())
((pair? (car lst)) (append (flatten (car lst)) (flatten (cdr lst))))
(else
(cons (car lst) (flatten (cdr lst))))))
(define (get-free-vars proc-form)
(let ((env (ge (eval proc-form user-initial-environment))))
(let loop ((pf (flatten proc-form))
(out ()))
(cond ((null? pf) out)
((symbol? (car pf))
(loop (cdr pf) (cons (cons (car pf) (environment-reference-type env (car pf))) out)))
(else
(loop (cdr pf) out))))))
编辑:示例用法:
(define a 100)
(get-vars '(lambda (x) (* x a g)))
=> ((g . unbound) (a . normal) (x . unbound) (* . normal) (x . unbound) (lambda . macro))
编辑 2:更改代码以防止environment-reference-type
使用符号以外的东西调用调用。
编辑 3:正如 Sam 在评论中指出的那样,这不会看到在 lambda 下的 let 中绑定的符号具有任何值.. 不确定是否有一个简单的解决方法。所以,我关于采取 any的说法lambda
是错误的,应该读起来更像“任何lambda
不包含新绑定形式的简单”......哦,好吧。