轨道
/
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 外星人 Leslie 正在给自己的迷宫机器人编程(年轻的 Lisp 外星人总爱干这种事),需要一些帮助。迷宫由一系列房间组成。每个房间都有若干扇门可供选择。每扇门都需要一把钥匙。如果在某扇门上用错了钥匙,那扇门很可能会爆炸(不用担心机器人,它不会受损,只是要把焦痕打磨掉,对 Leslie 来说相当烦人)。如果机器人没有能打开任何一扇门的钥匙,整个房间都会爆炸!(同样不用担心,不会造成损坏,只是清理起来很烦。)

你要帮 Leslie 做的,就是为每个房间给机器人正确的钥匙。机器人会按顺序把钥匙用在该房间的每一扇门上,并从第一扇打开的门穿过去。

每个测试代表一个迷宫,每个断言就是一个房间。你需要编写谓词函数,当参数相等时求值为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. cons 迷宫

这是一个有很多房间的大迷宫。每个房间需要的钥匙都要检查 cons 是否_包含_相等的元素。每个房间都会检查内容不同的 cons 是否相等:符号、数字、字符,其中一些房间还需要为这些内容使用更灵活的相等定义。

例如:

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,借助 27 个概念92 个练习 和真人导师指导,学习并掌握 Common Lisp,全部免费。