common-lisp
평등 및 기타 비교 술어
수색…
EQ와 EQL의 차이점
EQ
는 두 값의 메모리 주소가 같은지 확인합니다. 즉 두 값이 실제로 동일 하고 동일한 개체인지 확인합니다. 그래서, 그것은 신원 검사로 간주 될 수 있으며, 단지 구조물에 적용되어야합니다 : cons, 배열, 구조체, 객체, 일반적으로 당신이 실제로 동일한 객체가 다른 경로를 통해 도달했는지, 다른 변수들.EQL
은 두 개의 구조체가 같은 객체인지 (EQ
와 같은) 또는 구조화되지 않은 동일한 값 (즉, 같은 유형 또는 문자 값의 숫자에 대해 동일한 숫자 값)인지 여부를 확인합니다. 이것은EQ
연산자를 포함하고 구조화되지 않은 값에도 사용할 수 있으므로 가장 중요하고 가장 많이 사용되는 연산자이며MEMBER
와 같은 등가 비교가 필요한 거의 모든 기본 함수는 기본적으로이 연산자를 사용 합니다.
따라서, (EQ XY)
(EQL XY)
의미하는 반면, 그 반대는 유지되지 않습니다.
몇 가지 예를 통해 두 연산자의 차이를 정리할 수 있습니다.
(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을 사용한 구조적 동일성
이 세 연산자는 구조적 동등성을 구현합니다. 즉, 서로 다른 복잡한 객체가 동일한 구성 요소와 동일한 구조를 갖는지 확인합니다.
EQUAL
은 구조화되지 않은 데이터의 경우 EQL
처럼 작동하고 conses (목록 및 트리) 및 두 가지 특수 유형의 배열, 문자열 및 비트 벡터로 구성된 구조의 경우 구조 동등성을 수행하고 동형 인 두 구조에서 true를 반환합니다. 기본 구성 요소는 상응하여 동일 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 인 모든 경우에 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
). 예를 들면 :
(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
이 예제에서 우리는 두 개의 숫자 값이 같은 숫자 유형을 가지고있는 경우 에만 동일하다는 사실을 엄격히 밝히지 않는 한, =
는 숫자 값 사이의 비교를 수행하는 데 일반적으로 사용되는 연산자라고 결론 지을 수 있습니다. 어떤 경우에는 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 문자는, CHAR-CODE
의해 취득 된 대응하는 코드와 같은 순서로 a..z
됩니다. 대문자와 소문자를 구별하지 않는 경우, 2 개의 범위 a..z
, A..Z
로부터 취해진 임의의 2 개의 문자의 상대 순서는 구현에 의존합니다 . 예 :
(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
등의 단어가 STRING입니다. 그들은 문자 수가 동일하고 correspending 자에있어서 동일한 경우 두 문자열은 동일하다 CHAR=
또는 CHAR-EQUAL
테스트 대소 문자인지 아닌지.
문자열 사이의 순서는 두 문자열의 문자에 대한 사전 순서입니다. 순서 비교가 성공하면 결과는 T
가 아니라 두 문자열이 다른 첫 번째 문자의 색인이됩니다 (NIL이 아닌 모든 객체는 Common Lisp에서 "일반화 된 부울"이므로 true와 같습니다).
중요한 것은 string에 대한 모든 비교 연산자가 start1
, end1
, start2
, end2
와 같은 네 개의 키워드 매개 변수를 허용한다는 것입니다.이 매개 변수는 비교를 하나 또는 두 개의 문자열 내부의 연속 된 문자만으로 제한하는 데 사용할 수 있습니다. 생략 된 경우 시작 인덱스는 0이고 끝 인덱스는 생략되며 문자열의 길이와 :start
index :start
가있는 문자에서 :start
하여 인덱스 :end - 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
는 STRING-EQUAL
과 같습니다.
간과하다
Common Lisp에는 값을 비교하기위한 많은 다른 술어가 있습니다. 다음 범주로 분류 할 수 있습니다.
- 일반 평등 연산자 : EQ, EQL, EQUAL, EQUALP. 모든 유형의 값에 사용할 수 있으며 항상 부울 값 T 또는 NIL을 리턴합니다.
- 숫자에 대해서는 = 및 =을, 문자에 대해서는 CHAR = CHAR = CHAR-EQUAL CHAR-NOT-EQUAL, 문자열에 대해서는 STRING = STRING-EQUAL STRING-NOT-EQUAL, conses에 대해서는 TREE-EQUAL을 입력하십시오.
- 숫자 값의 비교 연산자 : <, <=,>,> =. 그들은 모든 유형의 숫자에 적용 할 수 있으며 실제 유형과 독립적으로 숫자의 수학적 가치를 비교할 수 있습니다.
- CHAR <, CHAR-LESSP 등과 같이 대소 문자를 구분하거나 대소 문자를 구분하지 않고 문자를 비교하는 자연어 사전 순을 유지하는 구현 방식에 따라 문자를 비교하는 연산자입니다.
- 문자 비교 연산자를 사용하여 문자열을 사전 식으로 비교하는 대소 문자를 구분하거나 대소 문자를 구분하지 않는 STRING, STRING-LESSP 등과 같은 문자열에 대한 비교 연산자입니다.