13

有谁知道我如何找出 lambda 表达式中的自由变量?自由变量是不属于 lambda 参数的变量。

我目前的方法(这让我无处可去)是简单地使用 car 和 cdr 来完成表达式。我的主要问题是确定一个值是变量还是方案原语之一。有没有办法测试某些东西是否评估为方案的内置函数之一?例如:

(is-scheme-primitive? 'and)
;Value: #t

我正在使用 MIT 方案。

4

2 回答 2

6

对于任意 MIT Scheme 程序,没有任何方法可以做到这一点。一个问题是您描述的功能无法正常工作。例如,这不使用“方案原语” and

(let ((and 7)) (+ and 1))

但它肯定使用符号'and

另一个问题是很多东西,比如and,都是用宏实现的特殊形式。您需要知道程序中的所有宏都扩展成什么,才能弄清楚程序中使用了哪些变量。

要完成这项工作,您需要限制您接受作为输入的程序集。最好的选择是将其限制为“完全扩展”的程序。换句话说,您要确保free-variables函数的输入中没有任何宏的使用。

为此,您可以使用expand许多 Scheme 系统提供的功能。不幸的是,从在线文档来看,MIT Scheme 似乎没有提供此功能。如果您能够使用不同的系统,Racket会提供该expand功能以及local-expand在宏内部正常工作的功能。

Racket 实际上还提供了您要求的free-variables功能的实现,正如我所描述的,它需要完全扩展的程序作为输入(例如expandor的输出local-expand)。您也可以查看源代码

有关完全扩展源代码所涉及问题的详细讨论,请参阅Flatt、Culpepper、Darais 和 Findler即将发表的这篇论文。

于 2012-04-29T12:47:54.320 回答
3

[编辑 4] 免责声明;或者,一年后回顾:

这实际上是解决这个问题的一个非常糟糕的方法。它作为一种非常快速和肮脏的方法可以实现 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不包含新绑定形式的简单”......哦,好吧。

于 2012-04-29T17:29:39.240 回答