common-lisp
等価および他の比較述部
サーチ…
EQとEQLの違い
EQ
は、2つの値が同じメモリアドレスを持っているかどうかをチェックします。つまり、2つの値が実際に同じで同一のオブジェクトであるかどうかをチェックします。したがって、それはアイデンティティテストとみなすことができ、コンス、配列、構造体、オブジェクトなどの構造体にのみ適用する必要があります。実際には、同じオブジェクトが異なるパスで「到達」しているか、異なる変数。EQL
は、2つの構造体が同じオブジェクト(EQ
)であるか 、同じ構造化されていない値(同じ型の数値または文字値の同じ数値)であるかどうかをチェックします。それはEQ
演算子を含み、非構造化値に対しても使用できるので、最も重要で最も一般的に使用される演算子であり、MEMBER
などの等価比較を必要とするほとんどすべてのプリミティブ関数は、 デフォルトでこの演算子を使用します 。
したがって、 (EQ XY)
暗示している(EQL XY)
ことは常に真実であり、逆行者は成立しません。
いくつかの例では、2つの演算子の違いを明確にすることができます。
(eq 'a 'a)
T ;; => since two s-expressions (QUOTE A) are “internalized” as the same symbol by the reader.
(eq (list 'a) (list 'a))
NIL ;; => here two lists are generated as different objects in memory
(let* ((l1 (list 'a))
(l2 l1))
(eq l1 l2))
T ;; => here there is only one list which is accessed through two different variables
(eq 1 1)
?? ;; it depends on the implementation: it could be either T or NIL if integers are “boxed”
(eq #\a #\a)
?? ;; it depends on the implementation, like for numbers
(eq 2d0 2d0)
?? ;; => dependes on the implementation, but usually is NIL, since numbers in double
;; precision are treated as structures in many implementations
(let ((a1 2d0)
(a2 2d0))
(eq a1 a2))
?? ;; => also in this case the results depends on the implementation
EQL
同じ例を試してみましょう:
(eql 'a 'a)
T ;; => equal because they are the same value, as for EQ
(eql (list 'a) (list 'a))
NIL ;; => different because they different objects in memory, as for EQ
(let* ((l1 (list 'a))
(l2 l1))
(eql l1 l2))
T ;; => as above
(eql 1 1)
T ;; they are the same number, even if integers are “boxed”
(eql #\a #\a)
T ;; they are the same character
(eql 2d0 2d0)
T ;; => they are the same number, even if numbers in double precision are treated as
;; structures in many implementations
(let ((a1 2d0)
(a2 2d0))
(eql a1 a2))
T ;; => as before
(eql 2 2.0)
NIL;; => since the two values are of a different numeric type
これらの例から、 EQL
演算子を使用して、すべての値、構造化されていない値、および実際に多くの専門家が一般的にEQ
の使用に反対することをアドバイスする理由について、
EQUAL、EQUALP、TREE-EQUALによる構造的平等
これらの3つの演算子は構造的な等価性を実装しています。つまり、異なる複雑なオブジェクトが等価な構成で同等の構造を持っているかどうかを確認します。
EQUAL
は、構造化されていないデータの場合はEQL
ように振る舞い、conses(リストとツリー)で構築された構造体と2つの特別な型の配列、文字列とビットベクトルの場合、 構造的な等価性を実行します。基本コンポーネントは、相応による等しいEQUAL
。例えば:
(equal (list 1 (cons 2 3)) (list 1 (cons 2 (+ 2 1))))
T ;; => since the two arguments are both equal to (1 (2 . 3))
(equal "ABC" "ABC")
T ;; => equality on strings
(equal "Abc" "ABC")
NIL ;; => case sensitive equality on strings
(equal '(1 . "ABC") '(1 . "ABC"))
T ;; => equal since it uses EQL on 1 and 1, and EQUAL on "ABC" and "ABC"
(let* ((a (make-array 3 :initial-contents '(1 2 3)))
(b (make-array 3 :initial-contents '(1 2 3)))
(c a))
(values (equal a b)
(equal a c)))
NIL ;; => the structural equivalence is not used for general arrays
T ;; => a and c are alias for the same object, so it is like EQL
EQUALP
は、 EQUAL
が真であるすべてのケースでtrueを返しますが、任意の種類とディメンション、構造体、ハッシュテーブル(クラスインスタンスではありません)の配列に対しても構造的に等価です。さらに、文字列に対して大文字と小文字を区別しない同値を使用します。
(equalp "Abc" "ABC")
T ;; => case insensitive equality on strings
(equalp (make-array 3 :initial-contents '(1 2 3))
(make-array 3 :initial-contents (list 1 2 (+ 2 1))))
T ;; => the structural equivalence is used also for any kind of arrays
(let ((hash1 (make-hash-table))
(hash2 (make-hash-table)))
(setf (gethash 'key hash1) 42)
(setf (gethash 'key hash2) 42)
(print (equalp hash1 hash2))
(setf (gethash 'another-key hash1) 84)
(equalp hash1 hash2))
T ;; => after the first two insertions, hash1 and hash2 have the same keys and values
NIL ;; => after the third insertion, hash1 and hash2 have different keys and values
(progn (defstruct s) (equalp (make-s) (make-s)))
T ;; => the two values are structurally equal
(progn (defclass c () ()) (equalp (make-instance 'c) (make-instance 'c)))
NIL ;; => two structurally equivalent class instances returns NIL, it's up to the user to
;; define an equality method for classes
最後に、 TREE-EQUAL
はcons
を介して構築された構造体に適用でき、 EQUAL
ような同形であるかどうかをチェックしますが、リーフを比較するためにどの関数を使用するかを選択することができます(つまり、これは他のデータ型でもEQL
ません(デフォルトでは、atomで使用されるテストはEQL
)。例えば:
(let ((l1 '(1 . ("A" . 2)))
(l2 '(1 . ("A" . 2))))
(tree-equal l1 l2 :test #'eql))
NIL ;; => since (eql "A" "A") gives NIL
(let ((l1 '(1 . ("A" . 2)))
(l2 '(1 . ("A" . 2))))
(tree-equal l1 l2 :test #'equal))
T ;; since (equal "A" "A") gives T
数値に関する比較演算子
数値は、異なるタイプの数値の物理的表現の違いを無視して、 =
とその他の数値比較演算子( /=
、 <
、 <=
、 >
、 >=
)と比較し、対応する数学的値。例えば:
(= 42 42)
T ;; => both number have the sme numeric type and the same value
(= 1 1.0 1d0)
T ;; => all the tree values represent the number 1, while for instance (eql 1 1d0) => NIL
;; since it returns true only if the operands have the same numeric type
(= 0.0 -0.0)
T ;; => again, the value is the same, while (eql 0.0 -0.0) => NIL
(= 3.0 #c(3.0 0.0))
T ;; => a complex number with 0 imaginary part is equal to a real number
(= 0.33333333 11184811/33554432)
T ;; => since a float number is passed to RATIONAL before comparing it to another number
;; => and (RATIONAL 0.33333333) => 11184811/33554432 in 32-bit IEEE floats architectures
(= 0.33333333 0.33333334)
T ;; => since the result of RATIONAL on both numbers is equal in 32-bit IEEE floats architectures
(= 0.33333333d0 0.33333334d0)
NIL ;; => since the RATIONAL of the two numbers in double precision is different
これらの例から、 =
は、2つの数値が同じ数値型を持つ場合にのみ等しくなることを厳密にしない限り、数値を比較するために通常使用される演算子であると結論付けることができます。この場合はEQL
を使用する必要があります。
文字と文字列の比較演算子
Common Lispには、2つの文字を比較する12の型固有の演算子があり、そのうち6つは大文字と小文字の区別があり、他は大文字小文字の区別がありません。彼らの名前は、その意味を覚えやすくする簡単なパターンを持っています:
大文字と小文字を区別 | 大文字小文字を区別しません |
---|---|
CHAR = | 二等式 |
CHAR / = | CHAR-NOT-EQUAL |
CHAR < | CHAR-LESSP |
CHAR <= | CHAR-NOT-GREATERP |
CHAR> | CHAR-GREATERP |
CHAR> = | CHAR-NOT-LESSP |
大文字と小文字を区別しない比較の場合、2つの範囲a..z
、 A..Z
から取られた任意の2文字間の相対的な順序は、実装に依存していますが、同じケースの2文字はCHAR-CODE
によって得られる対応するコードと同じ順序です。例:
(char= #\a #\a)
T ;; => the operands are the same character
(char= #\a #\A)
NIL ;; => case sensitive equality
(CHAR-EQUAL #\a #\A)
T ;; => case insensitive equality
(char> #\b #\a)
T ;; => since in all encodings (CHAR-CODE #\b) is always greater than (CHAR-CODE #\a)
(char-greaterp #\b \#A)
T ;; => since for case insensitive the ordering is such that A=a, B=b, and so on,
;; and furthermore either 9<A or Z<0.
(char> #\b #\A)
?? ;; => the result is implementation dependent
文字列の場合、特定の演算子はCHARではなくSTRING=
、 STRING-EQUAL
などです。テストで大文字と小文字が区別されている場合は、 CHAR=
またはCHAR-EQUAL
によって同じ文字数を持ち、対応する文字が等しい場合、2つの文字列は等しくなります。
文字列間の順序付けは、2つの文字列の文字の並び順になります。順序の比較が成功すると、結果はT
ではなく、2つの文字列が異なる最初の文字のインデックスになります(NIL以外のオブジェクトはすべてCommon Lispの "一般化されたブール値"なのでtrueと同じです)。
重要なことは、文字列のすべての比較演算子が、 end1
、 start2
、 end2
、 start1
4つのキーワードパラメータを受け入れることです。 start1
、一方または両方の文字列内の連続した文字列への比較を制限するために使用できます。省略された場合の開始インデックスは0、終了インデックスは省略され、文字列の長さと等しくなり、比較はindex :start
の文字で始まる部分文字列に対して実行され、index :end - 1
含む文字で終了します。
最後に、文字列が1文字であっても、文字と比較することはできません。
例:
(string= "foo" "foo")
T ;; => both strings have the same lenght and the characters are `CHAR=` in order
(string= "Foo" "foo")
NIL ;; => case sensitive comparison
(string-equal "Foo" "foo")
T ;; => case insensitive comparison
(string= "foobar" "barfoo" :end1 3 :start2 3)
T ;; => the comparison is perform on substrings
(string< "fooarr" "foobar")
3 ;; => the first string is lexicographically less than the second one and
;; the first character different in the two string has index 3
(string< "foo" "foobar")
3 ;; => the first string is a prefix of the second and the result is its length
特別な場合として、文字列比較演算子もまた、シンボルに適用することができ、比較が上に形成されているSYMBOL-NAME
シンボルの。例えば:
(string= 'a "A")
T ;; since (SYMBOL-NAME 'a) is "A"
(string-equal '|a| 'a)
T ;; since the the symbol names are "a" and "A" respectively
最後の注意として、文字に関するEQL
はCHAR=
と等価です。文字列のEQUAL
はSTRING=
と等価EQUALP
、文字列のEQUALP
はSTRING-EQUAL
と等価です。
俯瞰
Common Lispには、値を比較するためのさまざまな述語があります。それらは次のカテゴリに分類できます。
- 一般的な等価演算子:EQ、EQL、EQUAL、EQUALPこれらは任意の型の値に使用でき、常にブール値TまたはNILを返します。
- 文字にはCHAR = CHAR = CHAR-EQUAL CHAR-NOT-EQUAL、文字列にはSTRING = STRING = STRING-EQUAL STRING-NOT-EQUAL、コンスにはTREE-EQUALをタイプします。
- 数値の比較演算子:<、<=、>、> =。それらは任意のタイプの数値に適用でき、実際のタイプとは独立して数値の数学的な値を比較することができます。
- CHAR <、CHAR-LESSPなどの文字の比較演算子で、大文字と小文字を区別して大文字と小文字を区別しない方法で文字を比較します。
- STRING <、STRING-LESSPなどの文字列の比較演算子は、文字比較演算子を使用して、文字列を辞書的に大文字小文字を区別して比較するか、大文字小文字を区別しない方法で比較します。