【问题标题】:How to test if two functions are the same?如何测试两个函数是否相同?
【发布时间】:2016-02-26 07:18:50
【问题描述】:

我在网上找到了一个代码sn-p somewhere

(letrec
  ([id (lambda (v) v)]
   [ctx0 (lambda (v) `(k ,v))]
         .....
         .....
         (if (memq ctx (list ctx0 id)) <---- condition always return false
         .....

其中 ctx 也是一个函数:

但是我永远无法让测试语句返回 true。

然后我有以下测试:

(define ctx0 (lambda (v) `(k ,v)))
(define ctx1 (lambda (v) `(k ,v)))
(eq? ctx0 ctx1)
=> #f
(eqv? ctx0 ctx1)
=> #f
(equal? ctx0 ctx1)
=> #f

这让我怀疑两个函数总是不同的,因为它们有不同的内存位置。

但如果函数可以与其他函数进行比较,我如何测试两个函数是否相同?如果他们有不同的变量名怎么办?例如:

(lambda (x) (+ x 1))(lambda (y) (+ y 1))

附:我使用 DrRacket 来测试代码。

【问题讨论】:

    标签: functional-programming scheme racket


    【解决方案1】:

    你不能。函数被视为不透明的值:它们仅按身份进行比较,仅此而已。 这是设计使然。


    但是为什么?语言不能实现有意义的方法来比较有时可能有用的函数吗?好吧,不是真的,但有时如果不详细说明,很难理解为什么。让我们从您的问题中考虑您的示例 - 这两个函数似乎等效:

    (define ctx0 (lambda (v) `(k ,v)))
    (define ctx1 (lambda (v) `(k ,v)))
    

    确实如此。但是比较这些函数是否相等会完成什么呢?毕竟,我们可以很容易地实现另一个功能:

    (define ctx2 (lambda (w) `(k ,w)))
    

    就所有意图和目的而言,此函数与前两个函数相同,但它会通过简单的相等性检查!

    为了判断两个值是否相等,我们必须定义一些定义相等的算法。鉴于我迄今为止提供的示例,这样的算法似乎很明显:当(且仅当)它们是 α-equivalent 时,两个函数应该被认为是相等的。有了这个,我们现在可以有意义地检查两个函数是否相等!

    ...对吗?

    (define ctx3 (lambda (v) (list 'k v)))
    

    嗯,哦。这个函数做同样的事情,但它的实现方式并不完全相同,所以它没有通过我们的相等性检查。当然,我们可以解决这个问题。 Quasiquotation 和使用 list 构造函数几乎相同,因此我们可以将它们定义为在大多数情况下是等价的。

    (define ctx4 (lambda (v) (reverse (list v 'k))))
    

    啊!这在操作上也是等效的,但它仍然无法通过我们的等效算法。我们怎么可能做到这一点?


    事实证明我们不能,真的。函数是抽象单元——就其本质而言,我们不应该知道它们是如何实现的,只需要知道它们做了什么。这意味着函数相等实际上只能根据操作等价来正确定义;也就是说,实现并不重要,重要的是行为。

    这在任何非平凡语言中都是一个不可判定的问题。不可能确定任何两个函数在操作上是否等效,因为如果可以,我们可以解决halting problem

    理论上,编程语言可以提供一种尽力而为的算法来确定函数等价性,可能使用 α 等价性或其他某种度量。不幸的是,这真的没有用——取决于函数的实现而不是它的行为来确定程序的语义,这违反了函数抽象的基本法则,因此任何依赖于这样一个系统的程序都将是一个反模式。

    函数相等是一个非常诱人的问题,当简单的情况看起来很容易时想要解决,但大多数语言都采用了正确的方法,甚至没有尝试过。这并不是说这不是一个有用的想法:如果可能的话,它会非常有用!但既然不是,您将不得不使用不同的工具来完成这项工作。

    【讨论】:

    • 我只是尝试了作者在他的代码中给出的几个例子,“(memq ctx(list ctx0 id))”将在我测试“(cps'(lambda(x)(如果 (如果 t (如果 x (fa) b) c) ew)))"。但由于 memq 使用“eq?”比较两个元素....我很困惑
    • @Jackddddd 请注意,通过引用该 lambda 表达式,您实际上并没有返回 lambda,而是返回了符号列表。您链接的代码包括一个小型递归算法,该算法将 s 表达式(全部只是引用!)转换为它们的 CPS 等效项。但是,这只是半相关的:该代码正在按身份比较函数,这很好用。正如人们所期望的那样,(let ([x (lambda (v) v)]) (eq? x x)) 的计算结果为 #t
    • 我将补充一点,在引用透明的语言中(即语言不区分引用和值),你甚至没有函数的引用相等,因为所以会与语言的语义不兼容。
    • 尝试断言两个函数在在统计容差范围内是相同的是否有意义?也就是说,断言(= (f x) (g x)) 表示x 的数字N,然后计算fg 必须以95% 的置信度相等...
    • @logc 许多(甚至可能是大多数)函数都有一个无限域,或者至少是一个非常大的域,因此统计方法在计算上是不可行的。
    【解决方案2】:

    语义上,两个函数fg 相等,如果它们同意每个输入,即如果对于所有x,我们有(= (f x) (g x))。当然,没有办法针对x每个可能值进行测试。

    如果您只想合理地确信(lambda (x) (+ x 1))(lambda (y) (+ y 1)) 是相同的,那么您可以尝试断言

    (map (lambda (x) (+ x 1)) [(-5) (-4) (-3) (-2) (-1) 0 1 2 3 4 5])
    

    (map (lambda (y) (+ y 1)) [(-5) (-4) (-3) (-2) (-1) 0 1 2 3 4 5])
    

    在您的单元测试中是相同的。

    【讨论】:

      猜你喜欢
      • 2010-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-20
      • 2012-02-21
      相关资源
      最近更新 更多