Треки
/
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; два рядки тексту (англ. string) є equal, якщо кожен елемент є eql; два масиви є equal, якщо вони eq; будь-що інше є equal, якщо воно eql. наприклад:
(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2))         ; => NIL
(equal "foo" "foo")           ; => T
  • 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. Лабіринт рядків тексту (англ. string)

Як і в лабіринті символів, першій кімнаті потрібен ключ, який перевіряє, чи два рядки тексту рівні; другій кімнаті потрібен ключ, який дозволяє порівнювати їх без урахування регістру.

Наприклад:

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

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

адже лише дуже поблажливі предикати рівності ігнорують регістр, порівнюючи два рядки тексту.

5. Лабіринт cons

Це великий лабіринт із багатьма кімнатами. Кожній кімнаті потрібен ключ, який перевірить, чи cons містять рівні між собою речі. Кожна кімната перевірятиме рівність cons із різним вмістом: символів Lisp, чисел, символів, а деяким знадобляться гнучкіші визначення рівності для цього вмісту.

Наприклад:

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

адже навіть предикат рівності, який зазирає всередину cons, може виявитися недостатньо поблажливим, щоб ігнорувати регістр чи числовий тип під час порівняння.

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 вправи та справжнє наставництво від людей, і все це безкоштовно.