述語は常に真偽値(#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? 手続きは常に停止しなければならない。