2017-01-18 34 views
1

我有點異國情調。我需要比較功能,而不是「起源」而不是「實例」。在這裏我實際上意味着:「智能」比較球拍中的功能

(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? 

在這兩種情況下,我們獲得的功能,兩個不同的「實例」(即使有捕獲不同的值),但在源代碼中的相同位置這兩個聲明。當然,獲得這些功能主體的實際文本將解決這個問題,但這裏的其他答案告訴我們,在Racket中這是不可能的。有一些技巧可以幫助我嗎?

編輯: 這不是關於函數的理論等價性的問題。這完全是技術問題,而非編譯代碼中Racket的函數表示。因此可以用以下方式重新表達:我可以從「用戶」代碼中得到一些例程的行號嗎?我想這應該是可行的,因爲Racket調試器以某種方式獲得它。

+0

你在尋找lambda表達式的語義相等嗎? –

+0

@JonChesterfield我認爲更「輕量級」的變體。如果在兩個不同的地方聲明兩個相同的函數,則不需要'#t'。如果函數值在代碼中的同一行和位置中聲明,那麼獲得'#t'就足夠了。 – dvvrd

+0

這可能是可以解決的。 Iirc 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) 

免責聲明:我是一個多敲詐勒索所以這或許已經完成更優雅奸人,我不知道的性能損失,這將給予。

+0

這個想法看起來類似於http://stackoverflow.com/a/20362858,我認爲它。雖然宏觀解決方案不適用於外部定義的lambda,但似乎我仍然應該接受這種方式。非常感謝您的回答! – dvvrd

+0

@dvvrd我認爲解決方案實際上是基於這個答案,因爲我將它基於我的垃圾目錄中的一個文件,名爲visual-lambda.rkt :-) – Sylwester