【问题标题】:"Smart" comparison of functions in RacketRacket中功能的“智能”比较
【发布时间】:2017-06-03 11:55:12
【问题描述】:

我遇到了一些异国情调。我需要比较函数,而是通过它们的“起源”而不是“实例”。这就是我的真正意思:

(define-values (a b c d) (values #f #f #f #f))

(define (f x)
  (let ([g (λ (y) (printf "Please tell ~a this is ~a\n" x y))]
        [h (curry printf "Don't tell ~a this is ~a\n" x)])
    (if a
        (set! b g)
        (set! a g))
    (if c
        (set! d h)
        (set! c h))))

(f "me")
(f " me")

(a "possible")
(d "impossible")

(equal? a b) ; <====  Is it possible to compare these guys
(equal? c d) ; <====  to get #t in both cases?

在这两种情况下,我们都会得到两个不同的函数“实例”(即使捕获了不同的值),但都在源代码的同一位置声明。当然,获取这些函数主体的实际文本将解决问题,但 SO 上的其他答案表明这在 Racket 中是不可能的。有什么技巧可以帮到我吗?

编辑: 这不是关于函数理论上等价的问题。这完全是技术问题,而不是关于 Racket 在编译代码中的函数表示。所以它可以被重新表述,例如,通过以下方式:我可以从“用户”代码中获取一些例程的行号吗?我想这应该是可行的,因为 Racket 调试器以某种方式获得它。

【问题讨论】:

  • 您是否在寻找 lambda 表达式的语义相等性?
  • @JonChesterfield 我认为更“轻量级”的变体。如果在两个不同的地方声明了两个相同的函数,则没有必要获取#t。如果函数值在代码中的同一行和同一位置声明,则获得#t 就足够了。
  • 这可能是可以解决的。 Iirc 球拍用调试信息标记表达式,因此一种方法是解析它。除此之外我什么都没有,但至少你不需要符号证明系统:)
  • @JonChesterfield,是的,这完全是技术问题:) 顺便说一句,感谢您提供调试信息的提示!也许我可以深入研究一下。
  • “其他答案表明这在 Racket 中是不可能的。”这不仅在 Racket 中是不可能的,而且在 任何 turing 完整的编程语言中可证明是不可能的。无论如何,您尝试做的事情可能通过宏来实现,但这似乎是个坏主意。这对我来说是an XY problem 的强烈气味。

标签: function compare racket


【解决方案1】:

如果您控制生成功能的代码,即使没有球拍内部的支持也可以完成。如果您保留一个表示特定 lambda 的计数器(或某个标识符),它可以将不同的闭包包装在一个结构中,该结构可以具有与宏扩展相同的标识。这是一个演示:

#lang racket

;; makes a procedure object that can have other data connected to it
(struct proc (id obj) 
  #:property prop:procedure
  (struct-field-index obj)
  #:methods gen:custom-write
  [(define (write-proc x port mode)
     (display (format "#<procedure-id-~a>" (proc-id x)) port))])

;; compares the ids of two proc objects if they are proc objects
(define (proc-equal? a b)
  (and (proc? a)
       (proc? b)
       (= (proc-id a) (proc-id b))))

;; extends equal?, candidate to provide
(define (equal*? a b)
  (or (proc-equal? a b)
      (equal? a b)))

;; the state we keep
(begin-for-syntax
  (define unique-proc-id-per-code 0))

;; a macro that changes (lambda* ...) to 
;; (proc expansion-id (lambda ...)) 
(define-syntax (lambda* stx)
  (let ((proc-id unique-proc-id-per-code))
    (set! unique-proc-id-per-code (add1 unique-proc-id-per-code))
    #`(proc #,(datum->syntax stx proc-id) (lambda #,@(datum->syntax stx (cdr (syntax-e stx)))))))


;; test with making a counter
(define counter-from
  (lambda* (from)
    (lambda* ()
      (begin0
        from
        (set! from (add1 from))))))

;; evaluatin the outer shows it has id 0
counter-from ; ==> #<procedure-id-0>

;; make two counters that both use the inner lambda
(define from10 (counter-from 10))
(define from20 (counter-from 20))

;; both have the same expansion id
from10 ; ==> #<procedure-id-1>
from20 ; ==> #<procedure-id-1>

;; they are not equal?
(equal? from10 from20)      ; ==> #f (different object instances of proc)
;; but they are procedure-equal?
(proc-equal? from10 from20) ; ==> #t (same id, thus came from same macroexpansion)

免责声明:我更像是一个阴谋家而不是敲诈勒索者,所以这也许可以更优雅地完成,我不知道这会带来什么性能损失。

【讨论】:

  • 这个想法和stackoverflow.com/a/20362858很像,我看中了。虽然宏解决方案不适用于外部定义的 lambda,但似乎我仍然应该接受这种方式。非常感谢您的回答!
  • @dvvrd 我认为解决方案实际上是基于该答案,因为我基于我的垃圾目录中名为 visual-lambda.rkt 的文件 :-)
猜你喜欢
  • 2013-05-18
  • 1970-01-01
  • 2014-03-08
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
  • 2012-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多