Track
/
Common Lisp
Common Lisp
/
Esercizi
/
La chiave del confronto
La chiave del confronto

La chiave del confronto

Esercizio di apprendimento

Introduzione

Uguaglianza

Common Lisp ha molti predicati di uguaglianza diversi. Questo la distingue da altri linguaggi di programmazione, che magari ne hanno solo uno o due (per esempio == e ===). Alcuni di questi predicati in Common Lisp sono specifici per i tipi, mentre altri sono generici. Qui ci occuperemo proprio di questi ultimi.

Esistono quattro predicati di uguaglianza generici e si distinguono per quanto sono restrittivi su ciò che considerano «uguale». Sono, in ordine dal più restrittivo al meno restrittivo: eq, eql, equal e equalp.

Ecco un rapido elenco di definizioni (tralasciando qualche dettaglio):

  • eq: definisce l'uguaglianza nel senso che i due oggetti sono identici. ad esempio:
(eq 'a 'a)                 ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
  • eql: definisce l'uguaglianza nel senso che due numeri hanno lo stesso tipo e lo stesso valore, due caratteri sono identici, oppure, per qualsiasi altra cosa, che sono eq. ad esempio:
(eql 1 1)     ; => T
(eql #\a #\a) ; => T
(eql 1 1.0)   ; => NIL
(eql #\a #\A) ; => NIL
  • equal: definisce l'uguaglianza così: due liste sono equal se ogni loro elemento è a sua volta equal; due stringhe sono equal se ogni loro elemento è eql; due array sono equal se sono eq; tutto il resto è equal se è eql. ad esempio:
(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2))         ; => NIL
(equal "foo" "foo")           ; => T
  • equalp: definisce l'uguaglianza così: stringhe e caratteri vengono confrontati senza distinguere maiuscole e minuscole; i numeri vengono confrontati con una qualche conversione di tipo; liste e array sono equalp se ogni loro elemento è a sua volta equalp, le strutture se sono dello stesso tipo e tutti gli slot sono equalp, e le tabelle hash se le loro chiavi e i loro valori sono equalp ad esempio:
(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

Istruzioni

Leslie, l'alieno Lisp, sta programmando il suo robot da labirinto (come i giovani alieni Lisp sono soliti fare) ed ha bisogno di aiuto. Il labirinto è composto da una serie di stanze. Ogni stanza ha una scelta di porte. Ogni porta richiede una chiave. Se usi la chiave sbagliata su una porta, è probabile che esploda (non preoccuparti per il robot: non ci saranno danni, ma lucidare via i segni di bruciatura è piuttosto fastidioso per Leslie). Se il robot non ha una chiave che apra nessuna delle porte, l'intera stanza esploderà! (Ancora una volta, non preoccuparti: non ci saranno danni, ma sarà fastidioso da pulire.)

Quello che devi fare per aiutare Leslie è dare al robot la chiave corretta per ogni stanza. Il robot userà la chiave su ogni porta della stanza, in ordine, e passerà attraverso la prima porta che si apre.

Ogni test rappresenta un labirinto ed ogni asserzione è una stanza. Dovrai scrivere funzioni predicato che restituiscono T quando gli argomenti sono uguali e NIL quando non lo sono.

1. Il labirinto dell'uguaglianza tra oggetti

Questo labirinto ha una sola stanza. Devi fornire una funzione che sappia dire se due oggetti sono lo stesso oggetto.

Per esempio:

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

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

perché, anche se a e b sembrano uguali, non sono lo stesso oggetto.

2. Il labirinto dei numeri

Questo labirinto ha due stanze. La prima ha una porta che può essere aperta se la tua chiave sa dire se due numeri sono strettamente uguali tra loro. La seconda stanza richiede una chiave che dia una certa flessibilità nel controllo dell'uguaglianza tra numeri.

Per esempio:

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

perché, anche se a e b sono numericamente uguali, sono di tipi diversi.

3. Il labirinto dei caratteri

Questo labirinto ha due stanze. La prima ha una porta che può essere aperta se la tua chiave sa dire se due caratteri sono strettamente uguali tra loro. La seconda stanza richiede una chiave che permetta un confronto tra caratteri che ignori maiuscole e minuscole.

Per esempio:

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

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

perché solo predicati di uguaglianza molto permissivi ignorano le maiuscole e le minuscole quando confrontano due caratteri.

4. Il labirinto delle stringhe

Come nel labirinto dei caratteri, la prima stanza richiede una chiave che controlli se due stringhe sono uguali; la seconda stanza richiede una chiave che permetta un confronto che ignori maiuscole e minuscole.

Per esempio:

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

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

perché solo predicati di uguaglianza molto permissivi ignorano le maiuscole e le minuscole quando confrontano due stringhe.

5. Il labirinto dei cons

Questo è un labirinto grande, con molte stanze. Ogni stanza richiede una chiave che controlli se i cons contengono le cose che sono uguali tra loro. Ogni stanza controllerà l'uguaglianza di cons con contenuti diversi: simboli, numeri, caratteri; ed alcune avranno bisogno di definizioni di uguaglianza più flessibili per quei contenuti.

Per esempio:

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

perché, anche con un predicato di uguaglianza che guarda dentro un cons, potrebbe comunque non essere abbastanza permissivo da ignorare maiuscole e minuscole o il tipo numerico durante il confronto.

6. Il labirinto degli array

Questo labirinto è più semplice, con solo due stanze. La prima richiede una chiave che controlli se gli array sono gli stessi array. La seconda richiede una chiave che controlli il contenuto degli array.

Per esempio:

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

perché solo controlli di uguaglianza molto permissivi verificano se il contenuto dell'array è uguale.

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Common Lisp Exercism

Vuoi iniziare La chiave del confronto?

Iscriviti a Exercism per imparare e padroneggiare Common Lisp con 27 concetti92 esercizi e il mentoring di persone reali, tutto gratis.