트랙
/
Common Lisp
Common Lisp
/
연습 문제
/
비교의 열쇠
비교의 열쇠

비교의 열쇠

학습 연습 문제

소개

동등성

Common Lisp에는 서로 다른 동등성 판별 함수가 아주 많아요. ==나 ===처럼 하나 또는 둘만 있는 다른 프로그래밍 언어와는 다른 점이에요. Common Lisp의 판별 함수 중에는 특정 타입에만 쓰이는 것도 있고, 어떤 타입에도 두루 쓰이는 범용인 것도 있는데, 여기서는 뒤쪽의 범용 판별 함수를 다뤄요.

범용 동등성 판별 함수는 네 가지이고, 무엇을 "동등하다"고 보는지에 따라 얼마나 엄격한지가 달라요. 가장 엄격한 것부터 가장 느슨한 것 순으로 eq, eql, equal, equalp예요.

몇 가지 세부 사항은 빼고, 간단히 정의하면 다음과 같아요.

  • eq: 두 객체가 완전히 동일하다는 것을 동등함으로 정의해요. 예를 들어:
(eq 'a 'a)                 ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
  • eql: 같은 타입과 값을 가진 두 수, 또는 서로 같은 두 문자를 동등함으로 정의하고, 그 밖의 것은 eq면 동등하다고 봐요. 예를 들어:
(eql 1 1)     ; => T
(eql #\a #\a) ; => T
(eql 1 1.0)   ; => NIL
(eql #\a #\A) ; => NIL
  • equal: 두 리스트는 각 원소가 equal이면 equal이고, 두 문자열은 각 원소가 eql이면 equal이고, 두 배열은 eq면 equal이고, 그 밖의 모든 것은 eql이면 equal이라고 정의해요. 예를 들어:
(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2))         ; => NIL
(equal "foo" "foo")           ; => T
  • equalp: 문자열과 문자는 대소문자를 구분하지 않고 비교하고, 수는 타입 변환을 어느 정도 거쳐서 비교해요. 리스트와 배열은 모든 원소가 equalp면 equalp이고, 구조체는 같은 타입이고 모든 슬롯이 equalp면 equalp이고, 해시 테이블은 키와 값이 equalp면 equalp예요. 예를 들어:
(equalp "foo" "FoO")                          ; => T
(equalp 3 3.0)                                ; => T
(equalp (make-a-structure :slot1 1 :slot2 2) 
        (make-a-structure :slot1 1 :slot2 2)) ; => T

지침

Lisp 외계인 레슬리는 미로 로봇을 프로그래밍하고 있어요(어린 Lisp 외계인이라면 누구나 그러듯이). 그런데 도움이 좀 필요해요. 미로는 여러 개의 방으로 이루어져 있어요. 각 방에는 고를 수 있는 문이 여러 개 있고, 문마다 열쇠가 필요해요. 문에 맞지 않는 열쇠를 사용하면 문이 폭발할 가능성이 높아요(로봇 걱정은 마세요. 로봇이 다치는 건 아니지만, 그을린 자국을 닦아내는 건 레슬리에게 꽤 성가신 일이에요). 로봇이 어떤 문도 열 수 있는 열쇠를 가지고 있지 않으면 방 전체가 폭발해요! (다시 말하지만 걱정은 마세요. 로봇이 다치지는 않지만 청소하기는 성가실 거예요.)

레슬리를 도와주려면 각 방에 맞는 올바른 열쇠를 로봇에게 쥐여 주면 돼요. 로봇은 방 안의 각 문에 순서대로 열쇠를 사용하고, 처음으로 열리는 문으로 지나가요.

각 테스트는 하나의 미로를 나타내고, 각 단언은 하나의 방이에요. 인자가 서로 같으면 T로 평가되고, 그렇지 않으면 NIL로 평가되는 술어 함수를 작성해야 해요.

1. 객체 동일성의 미로

이 미로에는 방이 하나뿐이에요. 두 객체가 같은 객체인지 판별하는 함수를 제공해야 해요.

예를 들어요:

a                         ; => "pizza"
b                         ; => "pizza"

(key-object-identity a a) ; => T
(key-object-identity a b) ; => NIL

a와 b는 같아 보이지만, 사실 같은 객체는 아니기 때문이에요.

2. 숫자의 미로

이 미로에는 방이 두 개 있어요. 첫 번째 방에는, 두 숫자가 서로 엄격하게 같은지 판별하는 열쇠로 열 수 있는 문이 있어요. 두 번째 방에는 숫자가 같은지 확인할 때 어느 정도 유연함을 허용하는 열쇠가 필요해요.

예를 들어요:

a                                    ; => 13
b                                    ; => 13.0
c                                    ; => 13

(key-numbers a b)                    ; => NIL
(key-numbers a c)                    ; => T
(key-numbers-of-different-types a b) ; => T

a와 b는 수치적으로는 같지만 서로 다른 타입이기 때문이에요.

3. 문자의 미로

이 미로에는 방이 두 개 있어요. 첫 번째 방에는, 두 문자가 서로 엄격하게 같은지 판별하는 열쇠로 열 수 있는 문이 있어요. 두 번째 방에는 문자를 대소문자 구분 없이 비교할 수 있는 열쇠가 필요해요.

예를 들어요:

a                                       ; => #\X
b                                       ; => #\x
c                                       ; => #\X

(key-characters a b)                    ; => NIL
(key-characters a c)                    ; => T
(key-characters-case-insensitively a b) ; => T

두 문자를 비교할 때 대소문자를 무시하는 것은 아주 관대한 동등성 술어뿐이기 때문이에요.

4. 문자열의 미로

문자의 미로와 마찬가지로, 첫 번째 방에는 두 문자열이 같은지 확인하는 열쇠가 필요하고, 두 번째 방에는 대소문자 구분 없이 비교할 수 있는 열쇠가 필요해요.

예를 들어요:

a                                    ; => "pizza"
b                                    ; => "PIZZA"
c                                    ; => "pizza"

(key-strings a b)                    ; => NIL
(key-strings a c)                    ; => T
(key-strings-case-insensitively a b) ; => T

두 문자열을 비교할 때 대소문자를 무시하는 것은 아주 관대한 동등성 술어뿐이기 때문이에요.

5. 콘스의 미로

이 미로는 방이 아주 많은 큰 미로예요. 각 방에는 콘스가 같은 것들을 포함 하는지 확인하는 열쇠가 필요해요. 각 방은 내용이 서로 다른 콘스의 동등성을 확인해요. 심볼, 숫자, 문자, 그리고 어떤 방들은 그런 내용에 대해 더 유연한 동등성 정의가 필요해요.

예를 들어요:

syms-a                                     ; => (left . right)
syms-b                                     ; => (up . down)
syms-c                                     ; => (left . right)
chars-a                                    ; => (#\x . #\y)
chars-b                                    ; => (#\x . #\Y)
chars-c                                    ; => (#\x . #\y)
nums-a                                     ; => (13 . 23)
nums-b                                     ; => (13 . 23.0)
nums-c                                     ; => (13 . 23)


(key-conses-of-symbols syms-a syms-b)      ; => NIL
(key-conses-of-symbols syms-a syms-c)      ; => T

(key-conses-of-characters chars-a chars-b) ; => NIL
(key-conses-of-characters chars-a chars-c) ; => T
(key-conses-of-characters-case-insensitively 
  chars-a chars-b)                         ; => T

(key-conses-of-numbers nums-a nums-b)      ; => NIL
(key-conses-of-numbers nums-a nums-c)      ; => T
(key-conses-of-numbers-of-different-types 
  nums-a nums-b)                           ; => T

콘스 내부를 살펴보는 동등성 술어라도, 비교할 때 대소문자나 숫자 타입을 무시할 만큼 관대하지 않을 수 있기 때문이에요.

6. 배열의 미로

이 미로는 방이 두 개뿐이라 더 단순해요. 첫 번째 방에는 두 배열이 같은 배열인지 확인하는 열쇠가 필요해요. 두 번째 방에는 배열의 내용을 확인하는 열쇠가 필요해요.

예를 들어요:

a                        ; => #[13 23]
b                        ; => #[13 23]
c                        ; => #[13 23.0]

(key-arrays a b)         ; => NIL
(key-arrays a c)         ; => NIL
(key-arrays-loosely a b) ; => T
(key-arrays-loosely a c) ; => T

배열의 내용이 같은지까지 확인하는 것은 아주 관대한 동등성 검사기뿐이기 때문이에요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Common Lisp Exercism

비교의 열쇠 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Common Lisp 트랙을 개념 27개연습 문제 92개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.