2017-01-18 15 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

ラムダ式の意味的平等をお探しですか? –

+0

@JonChesterfield私はもっと "軽量"の変種だと思います。 2つの同じ関数が2つの異なる場所で宣言されている場合、 '#t'を得る必要はありません。関数の値がコード内の同じ行と位置に宣言されていれば、 '#t'を得るだけで十分です。 – dvvrd

+0

これはおそらく解決可能です。 Iircラケットは、デバッグ情報を使って式にタグを付けます。そのため、1つのアプローチでは、それを解析することになります。それ以外には何も持っていませんが、少なくともあなたは象徴的な証明システムは必要ありません:) –

答えて

1

機能を実現するコードを制御すると、ラケット内部からのサポートがなくても実行できます。特定のラムダを示すカウンタ(または何らかの識別子)を保持すると、マクロ展開と同じアイデンティティを持つことができる構造体に異なるクロージャをラップすることができます。ここではデモンストレーションです:

#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と似ていますが、私はそれを考慮しました。マクロソリューションは外部定義ラムダでは機能しませんが、私はまだこの方法を受け入れるべきです。ご回答どうもありがとうございました! – dvvrd

+0

@dvvrd私は、私の迷惑メールディレクトリのファイルにvisual-lambda.rktという名前を付けているので、この解決策は実際にその答えに基づいていると思います.-) – Sylwester

関連する問題