Percursos
/
Common Lisp
Common Lisp
/
Exercícios
/
A chave da comparação
A chave da comparação

A chave da comparação

Exercício de aprendizagem

Introdução

Igualdade

O Common Lisp tem muitos predicados de igualdade diferentes. Isto é diferente de outras linguagens de programação, que podem ter apenas um ou dois (por exemplo, == e ===). Alguns destes predicados no Common Lisp são específicos de tipos, enquanto outros são genéricos. São estes últimos que vamos abordar aqui.

Há quatro predicados de igualdade genéricos e diferem no grau de restrição daquilo que consideram "igual". São, por ordem do mais restritivo para o menos restritivo: eq, eql, equal e equalp.

Um conjunto rápido de definições (omitindo alguns detalhes) é o seguinte:

  • eq: define a igualdade como significando que os dois objetos são idênticos. por exemplo:
(eq 'a 'a)                 ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
  • eql: define a igualdade como significando dois números com o mesmo tipo e o mesmo valor, dois carateres que são iguais, ou, para qualquer outra coisa, se forem eq. por exemplo:
(eql 1 1)     ; => T
(eql #\a #\a) ; => T
(eql 1 1.0)   ; => NIL
(eql #\a #\A) ; => NIL
  • equal: define a igualdade como significando: duas listas são equal se cada elemento também for equal; duas strings são equal se cada elemento for eql; dois arrays são equal se forem eq; tudo o resto é equal se for eql. por exemplo:
(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2))         ; => NIL
(equal "foo" "foo")           ; => T
  • equalp: define a igualdade como significando: as strings e os carateres são comparados independentemente de serem maiúsculas ou minúsculas; os números são comparados com alguma conversão de tipo; as listas e os arrays são equalp se todos os elementos também forem equalp, as estruturas se forem do mesmo tipo e todos os slots forem equalp, e as hash-tables se as suas chaves e valores forem equalp por exemplo:
(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

Instruções

A Leslie, a alienígena Lisp, está a programar o seu robô de labirinto (como é hábito entre os jovens alienígenas Lisp) e precisa de uma ajuda. O labirinto é composto por uma série de salas. Cada sala tem uma escolha de portas. Cada porta precisa de uma chave. Se usares a chave errada numa porta, é provável que ela expluda (não te preocupes com o robô, não vai haver danos, mas polir as marcas de queimaduras é bastante aborrecido para a Leslie). Se o robô não tiver uma chave que abra alguma das portas, a sala inteira explode! (Mais uma vez, não te preocupes, não vai causar danos, mas vai ser chato de limpar.)

O que precisas de fazer para ajudar a Leslie é dar ao robô a chave correta para cada sala. O robô usa a chave em cada porta da sala, por ordem, e passa pela primeira porta que se abrir.

Cada teste representa um labirinto e cada asserção é uma sala. Vais ter de escrever funções de predicado que devolvem T quando os argumentos são iguais e NIL quando não são.

1. O labirinto da igualdade de objetos

Este labirinto tem apenas uma sala. Precisas de fornecer uma função que consiga dizer se dois objetos são o mesmo objeto.

Por exemplo:

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

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

porque, embora a e b pareçam ser iguais, não são o mesmo objeto.

2. O labirinto dos números

Este labirinto tem duas salas. A primeira tem uma porta que se pode abrir se a tua chave conseguir dizer se dois números são estritamente iguais um ao outro. A segunda sala precisa de uma chave que permita alguma flexibilidade na verificação de números iguais.

Por exemplo:

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

porque, embora a e b sejam numericamente iguais, são de tipos diferentes.

3. O labirinto dos carateres

Este labirinto tem duas salas. A primeira tem uma porta que se pode abrir se a tua chave conseguir dizer se dois carateres são estritamente iguais um ao outro. A segunda sala precisa de uma chave que permita uma comparação de carateres que não distinga maiúsculas de minúsculas.

Por exemplo:

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

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

porque só predicados de igualdade muito permissivos não distinguem maiúsculas de minúsculas ao comparar dois carateres.

4. O labirinto das strings

Tal como o labirinto dos carateres, a primeira sala precisa de uma chave que verifique se duas strings são iguais; a segunda sala precisa de uma chave que permita uma comparação que não distinga maiúsculas de minúsculas.

Por exemplo:

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

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

porque só predicados de igualdade muito permissivos não distinguem maiúsculas de minúsculas ao comparar duas strings.

5. O labirinto dos conses

Este é um labirinto grande, com muitas salas. Cada sala precisa de uma chave que verifique se os conses contêm as coisas que são iguais. Cada sala verifica a igualdade de conses com conteúdos diferentes: símbolos, números, carateres, e algumas vão precisar de definições de igualdade mais flexíveis para esses conteúdos.

Por exemplo:

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

porque, mesmo com um predicado de igualdade que olha para dentro de um cons, pode ainda não ser suficientemente permissivo para ignorar a distinção entre maiúsculas e minúsculas ou o tipo numérico ao comparar.

6. O labirinto dos arrays

Este labirinto é mais simples, com apenas duas salas. A primeira precisa de uma chave que verifique se os arrays são os mesmos arrays. A segunda precisa de uma chave que verifique o conteúdo dos arrays.

Por exemplo:

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

porque só verificadores de igualdade muito permissivos verificam se o conteúdo do array é igual.

Editar via GitHub A ligação abre numa nova janela ou separador
Common Lisp Exercism

Estás pronto para começar A chave da comparação?

Inscreve-te no Exercism para aprenderes e dominares Common Lisp com 27 conceitos92 exercícios, e mentoria humana real, tudo grátis.