述語は常に真偽値(#t か #f)を返す手続きである。同値述語は数学的な同値関係(対称的であり、反射的であり、推移的である)の計算論的な類義語である。本節で述べる同値述語について、 eq? は最も粒度が細かく識別力の強いものであり、 equal? は最も粗いものである。 eqv? 述語は eq? よりもやや識別力に劣る。
eqv? 手続きはオブジェクトに対して有用な同値関係を定義する。つまり、 obj1 と obj2 が普通同じオブジェクトと見做される場合に #t を返し、それ以外に #f を返す。この関係はやや解釈に曖昧さが残るが、次の部分的な eqv? の仕様はすべての実装系で満たされていなければならない。
eqv? 手続きは次のいずれかを満たす場合に #t を返す。
eqv? 手続きは次のいずれかが満たされたときに #f を返す。
注: obj1 と obj2 が数値オブジェクトで、 eqv? 手続きが #t を返した場合でも、 obj1 と obj2 を引き数にして = を呼び出した場合にも #t が返るとはかぎらない。
(eqv? ’a ’a) ⇒ #t (eqv? ’a ’b) ⇒ #f (eqv? 2 2) ⇒ #t (eqv? ’() ’()) ⇒ #t (eqv? 100000000 100000000) ⇒ #t (eqv? (cons 1 2) (cons 1 2)) ⇒ #f (eqv? (lambda () 1) (lambda () 2)) ⇒ #f (eqv? #f ’nil) ⇒ #f
次に示す例は上の規則で完全には eqv? 振る舞いが規定されない場合である。このような場合について言えるのは、 eqv? の戻り値は真偽値でなければならないということだけである。
(let ((p (lambda (x) x))) (eqv? p p)) ⇒ unspecified (eqv? "" "") ⇒ unspecified (eqv? ’#() ’#()) ⇒ unspecified (eqv? (lambda (x) x) (lambda (x) x)) ⇒ unspecified (eqv? (lambda (x) x) (lambda (y) y)) ⇒ unspecified (eqv? +nan.0 +nan.0) ⇒ unspecified
次の例では局所的な状態をもった手続きに eqv? を使った例を示す。 gen-counter の呼び出し結果は、それぞれに内部的なカウンタがあり、毎回異なる手続きを返さなければならない。 gen-loser 手続きは呼び出されると常に同じように振る舞う手続きを返す。しかしながら、 eqv? はこの同値性を検知できないかもしれない。
(define gen-counter (lambda () (let ((n 0)) (lambda () (set! n (+ n 1)) n)))) (let ((g (gen-counter))) (eqv? g g)) ⇒ unspecified (eqv? (gen-counter) (gen-counter)) ⇒ #f (define gen-loser (lambda () (let ((n 0)) (lambda () (set! n (+ n 1)) 27)))) (let ((g (gen-loser))) (eqv? g g)) ⇒ unspecified (eqv? (gen-loser) (gen-loser)) ⇒ unspecified (letrec ((f (lambda () (if (eqv? f g) ’both ’f))) (g (lambda () (if (eqv? f g) ’both ’g)))) (eqv? f g)) ⇒ unspecified (letrec ((f (lambda () (if (eqv? f g) ’f ’both))) (g (lambda () (if (eqv? f g) ’g ’both)))) (eqv? f g)) ⇒ #f
実装系は適切な場合に定数間の構造の共有してもかまわない。さらに、R6RS:翻訳:R6RS:11.4 Expressions の 11.4.1 節で触れたように、定数は任意の時に、実装系によって同時に異なる場所に複製されることもある。したがって、定数に対する eqv? の値は実装系依存になる時がある。
(eqv? ’(a) ’(a)) ⇒ unspecified (eqv? "a" "a") ⇒ unspecified (eqv? ’(b) (cdr ’(a b))) ⇒ unspecified (let ((x ’(a))) (eqv? x x)) ⇒ #t
eq? 述語は eqv? と類似であるが、いくつかの場合で eqv? よりも細やかな区別ができる。
eq? と eqv? はシンボル、真偽値、空リスト、対、手続き、空でない文字列、バイトベクタ、ベクタ、構造体には同一の振る舞いをすることが保証されている。数値オブジェクトと文字に対する eq? の振る舞いは実装系依存であるが、常に #t か #f を返し、 eqv? が #t を返すときにだけ #t を返す。 eq? 述語は空ベクタ、空バイトベクタ、空文字列に対しても異なる振る舞いをすることがある。
(eq? ’a ’a) ⇒ #t (eq? ’(a) ’(a)) ⇒ unspecified (eq? (list ’a) (list ’a)) ⇒ #f (eq? "a" "a") ⇒ unspecified (eq? "" "") ⇒ unspecified (eq? ’() ’()) ⇒ #t (eq? 2 2) ⇒ unspecified (eq? #\A #\A) ⇒ unspecified (eq? car car) ⇒ #t (let ((n (+ 2 3))) (eq? n n)) ⇒ unspecified (let ((x ’(a))) (eq? x x)) ⇒ #t (let ((x ’#())) (eq? x x)) ⇒ unspecified (let ((p (lambda (x) x))) (eq? p p)) ⇒ unspecified
equal? 述語はその引き数の正則木(regular tree)への(場合によっては無限の)展開形が、順序付き木として等しい場合、かつその場合にかぎり #t を返す。
equal? 述語では対とベクタを出力辺の端点として扱い、文字列の比較に string=? を使い、バイトベクタの比較に bytevector=? を使い(R6RS:翻訳:Standard Libraries:2 Bytevectors 参照)、その他の端点の比較には eqv? を使う。
(equal? ’a ’a) ⇒ #t (equal? ’(a) ’(a)) ⇒ #t (equal? ’(a (b) c) ’(a (b) c)) ⇒ #t (equal? "abc" "abc") ⇒ #t (equal? 2 2) ⇒ #t (equal? (make-vector 5 ’a) (make-vector 5 ’a)) ⇒ #t (equal? ’#vu8(1 2 3 4 5) (u8-list->bytevector ’(1 2 3 4 5)) ⇒ #t (equal? (lambda (x) x) (lambda (y) y)) ⇒ unspecified (let* ((x (list ’a)) (y (list ’a)) (z (list x y))) (list (equal? z (list y x)) (equal? z (list x x)))) ⇒ (#t #t)
注: 引き数に循環がある場合でも、 equal? 手続きは常に停止しなければならない。