R6RS:翻訳:R6RS:11.5 Equivalence predicates

R6RS:翻訳:R6RS:11.5 Equivalence predicates

11.5 同値述語

述語は常に真偽値(#t か #f)を返す手続きである。同値述語は数学的な同値関係(対称的であり、反射的であり、推移的である)の計算論的な類義語である。本節で述べる同値述語について、 eq? は最も粒度が細かく識別力の強いものであり、 equal? は最も粗いものである。 eqv? 述語は eq? よりもやや識別力に劣る。

[procedure] (eqv? obj1 obj2)

eqv? 手続きはオブジェクトに対して有用な同値関係を定義する。つまり、 obj1obj2 が普通同じオブジェクトと見做される場合に #t を返し、それ以外に #f を返す。この関係はやや解釈に曖昧さが残るが、次の部分的な eqv? の仕様はすべての実装系で満たされていなければならない。

eqv? 手続きは次のいずれかを満たす場合に #t を返す。

eqv? 手続きは次のいずれかが満たされたときに #f を返す。

: obj1obj2 が数値オブジェクトで、 eqv? 手続きが #t を返した場合でも、 obj1obj2 を引き数にして = を呼び出した場合にも #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

[procedure] (eq? obj1 obj2)

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

[procedure] (equal? obj1 obj2)

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? 手続きは常に停止しなければならない。

More ...