Functional programming 在lambda表达式中查找自由变量

Functional programming 在lambda表达式中查找自由变量,functional-programming,lisp,scheme,Functional Programming,Lisp,Scheme,有人知道我如何计算lambda表达式中的自由变量吗?自由变量是不属于lambda参数的变量 我目前的方法(这让我一事无成)是简单地使用car和cdr来遍历表达式。我的主要问题是弄清楚一个值是一个变量还是一个scheme原语。有没有一种方法可以测试某个东西的计算结果是否符合scheme的内置函数之一?例如: (is-scheme-primitive? 'and) ;Value: #t 我正在使用MIT scheme。对于任意的MIT scheme程序,没有任何方法可以做到这一点。一个问题是,您描

有人知道我如何计算lambda表达式中的自由变量吗?自由变量是不属于lambda参数的变量

我目前的方法(这让我一事无成)是简单地使用car和cdr来遍历表达式。我的主要问题是弄清楚一个值是一个变量还是一个scheme原语。有没有一种方法可以测试某个东西的计算结果是否符合scheme的内置函数之一?例如:

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

我正在使用MIT scheme。

对于任意的MIT scheme程序,没有任何方法可以做到这一点。一个问题是,您描述的函数无法工作。例如,这不使用“scheme基元”

(let ((and 7)) (+ and 1))
但它肯定使用了符号
”和

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

要使其工作,您需要限制接受为输入的程序集。最好的选择是将其限制在“完全扩展”的程序中。换句话说,您希望确保
自由变量
函数的输入中没有宏的任何用途

为此,您可以使用许多方案系统提供的
展开
功能。不幸的是,从外观上看,MIT Scheme并没有提供此功能。如果您能够使用不同的系统,则提供该功能以及在宏中正常工作的功能

Racket实际上还提供了一个您所要求的实现,正如我所描述的,它需要完全扩展的程序作为输入(例如
expand
local expand
的输出)。你也可以看到它

有关完整扩展源代码所涉及问题的详细讨论,请参阅Flatt、Culpepper、Darais和Findler的文章。

[EDIT 4]免责声明;或者,一年后回顾: 这实际上是解决这个问题的一个非常糟糕的方法。它是一种非常快速和肮脏的方法,可以实现OP的基本目标,但不能抵抗任何“现实生活”用例。请参阅此答案评论中的讨论,以及其他答案,以了解原因

[/编辑] 此解决方案可能不太理想,但它适用于您希望在mit scheme的REPL环境中提供的任何lambda表单(请参见编辑)。我使用的过程的文档可以在
get vars
中找到,它获取一个带引号的
lambda
并返回一个对列表。每对的第一个元素是符号,第二个是由
环境引用类型返回的值

(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:更改代码以保护再次调用
环境引用类型
时使用符号以外的内容调用


编辑3:正如Sam在评论中指出的,这将不会看到lambda下的let中绑定的符号有任何值。。不确定是否有一个简单的解决方法。因此,我关于采用任何
lambda
的说法是错误的,应该读得更像“任何不包含新绑定形式的简单
lambda
”。。。哦,好吧。

可能不适用于任意大的程序,但对于顶级环境中的简单lambda,我相信我已经找到了工作解决方案的基础。您的解决方案不适用于任何带绑定的函数(如
let
lambda
)在他们里面。不是
let
lambda
打破了它。。这是因为我没有防止使用符号以外的东西调用
环境引用类型。您是正确的,尽管它不会将let中的绑定视为具有任何值…这对该程序不起作用:
(lambda(x)(let((y 1))(+x y))
@Sam您是正确的。在调用整数上的
环境引用类型时,它被破坏了,但即使在防止这种情况发生后,它也不会在
lambda
@oobivat中看到任何绑定。这个HW问题的一个规定是,在lambda下不允许他们这样做,所以这个解决方案可以很好地工作。谢谢你给我指明了正确的方向。将研究您的解决方案并尝试实现自己。这也有许多其他问题:考虑<代码>(lambda()(引用x))< /代码>。代码>展平
不是处理Scheme程序的好方法。@oobivat,如果不连接到宏扩展器本身,就无法实现
展开
。毕竟,我们甚至不知道MIT的核心方案是什么——它可能使用
lambda
实现
let
,或者它可能将
let
作为一个原语。