學習軌道
/
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,全部免費。