前言
Lisp 家族語言 (含 Common Lisp) 不嚴格區分運算子和函式,因為 Lisp 使用前綴表示法,不需要區分兩者。像是 Common Lisp 的 + 就是函式而非運算子。
本文仍會介紹一些在 Algol 家族語言視為運算子的符號,讓讀者學習幾個 Common Lisp 中實用的指令。
代數運算 (Arithmetic Operation)
以下是 Common Lisp 提供的代數運算指令:
(註) 整數相除會得到整數或有理數,不會自動轉為浮點數。
在 Common Lisp 中,代數運算是以函式實作,可接收兩個以上的運算元。例如,(+ 1 2 3 4 5)
會轉為 1 + 2 + 3 + 4 + 5
,(- 1 2 3 4 5)
會轉為 1 - 2 - 3 - 4 - 5
。
以下是使用例:
(defun main ()
(assert (equal 6 (+ 1 2 3)))
(assert (equal -4 (- 1 2 3)))
(assert (equal 6 (* 1 2 3)))
(assert (equal 1/6 (/ 1 2 3)))
(assert (equal 3 (mod 3 4)))
(quit))
(main)
二元運算 (Bitwise Operation)
二元運算是指使用二進位數的代數運算。以下是 Common Lisp 的二元運算指令:
logand
:且 (bitwise and)logior
:或 (bitwise or)logxor
:互斥或 (bitwise exclusive or)logeqv
:相等 (bitwise equal)ash
:平移,可左移和右移complement
:取補數
logand
的運算方式如下:
p | q | p and q |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
logior
的運算方式如下:
p | q | p ior q |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
logxor
的運算方式如下:
p | q | p xor q |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
logeqv
的運算方式如下:
p | q | p eqv q |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 1 |
由於 logeqv
函式在其他語言較少見,讀者可注意一下。
complement
(取補數) 的運算方式如下:
p | comp p |
---|---|
1 | 0 |
0 | 1 |
在了解二元運算的運算方式後,可以參考以下實例:
(defun main ()
#| 0000 0101
and) 0000 0011
----------------
0000 0001 |#
(assert (= 1 (logand 5 3)))
#| 0000 0101
or) 0000 0011
----------------
0000 0111 |#
(assert (= 7 (logior 5 3)))
#| 0000 0101
xor) 0000 0010
----------------
0000 0110 |#
(assert (= 6 (logxor 5 3)))
#|<<) 0000 0101
----------------
0000 1010 |#
(assert (= 10 (ash 5 1)))
#|>>) 0000 0101
----------------
0000 0010 |#
(assert (= 2 (ash 5 -1)))
(quit))
(main)
二元運算在平時較少用,因為較不直觀。但二元運算比等效的代數運算還快,在低階運算 (low-level computing) 中大量使用二元運算來加速。
由於 Common Lisp 是較高階抽象的語言,在其標準文件中未提到二元運算是否會比代數運算快。所以,不太需要刻意用二元運算來加速,只要在需要二元運算時才使用即可。
比較運算 (Comparison Operation)
比較數字
以下是用在數字間比較大小關係的指令:
(註) Common Lisp 有多個檢查值是否相等的函式,=
用於比較數字。
要注意比較運算可子放入多個值。例如
(> 3 2 1)
-> 3 > 2 > 1
-> 3 > 2 && 2 > 1
所以,在 Common Lisp 的比較運算子中,隱含著多個數之間的比較。
以下是使用例:
(defun main ()
(assert (= 3 3 3))
(assert (/= 3 4 5))
(assert (> 3 2 1))
(assert (>= 3 2 1))
(assert (< 1 2 3))
(assert (<= 1 2 3))
(assert (= 5 (max 1 2 3 4 5)))
(assert (= 1 (min 1 2 3 4 5)))
(quit))
(main)
比較字元
以下字元比較函式會考慮大小寫差異:
char=
:相等char/=
:不等char<
:小於char<=
:小於或等於char>
:大於char>=
:大於或等於
以下字元比較函式會忽略大小寫差異:
char-equal
:相等char-not-equal
:不等char-lessp
:小於char-not-greaterp
:不大於 (小於或等於)char-greaterp
:大於char-not-lessp
:不小於 (大於或等於)
比較字串
以下字串比較函式會考慮大小寫差異:
string=
:相等string/=
:不等string<
:小於string<=
:小於或等於string>
:大於string>=
:大於或等於
以下字串比較函式會忽略大小寫差異:
string-equal
:相等string-not-equal
:不等string-lessp
:小於string-not-greaterp
:不大於 (小於或等於)string-greaterp
:大於string-not-lessp
:不小於 (大於或等於)
通用的比較運算 (Equality)
Common Lisp 對於相等的概念相當細微,所以使用多種函式來比較 (不同概念的) 相等。以下是 Common Lisp 的通用比較運算:
- eq
- 相同的物件 (記憶體位罝)
- eql
- 數字:相同值
- 字元:相同值
- 其他:等同
eq
- equal
- 數字:等同
eql
- 字元:等同
eql
- 字串:相同值,會區分大小寫
- 列表:元素的值相同,會區分大小寫
- 數字:等同
- equalp
equal
相等的皆相等- 字串:相同值,不區分大小寫
- 列表:元素的值相同,不區分大小寫
在大部分情形下,使用 equal
是最安全的選擇。除非有明確的理由,才會去用其他的相等運算函式。
以下是實例,讀者可花點時間閱讀一下:
;; Comparison between two equal integers.
(let ((ia 4)
(ib 4))
; implementation-dependent.
; (assert (eq ia ib))
(assert (eql ia ib))
(assert (equal ia ib))
(assert (equalp ia ib)))
;; Comparison between two equal floats.
(let ((fa 4.0)
(fb 4.0))
; implementation-dependent.
; (assert (eq fa fb))
(assert (eql fa fb))
(assert (equal fa fb))
(assert (equalp fa fb)))
;; Comparison between two equal complex numbers.
(let ((ca #c(3 -4))
(cb #c(3 -4)))
; implementation-dependent.
; (assert (eq ca cb))
(assert (eql ca cb))
(assert (equal ca cb))
(assert (equalp ca cb)))
;; Comparison between two equal symbols.
(let ((sa 'sym)
(sb 'sym))
(assert (eq sa sb))
(assert (eql sa sb))
(assert (equal sa sb))
(assert (equalp sa sb)))
;; Comparison between two equal strings.
(let ((sa "foo")
(sb "foo"))
; implementation-dependent.
; (assert (eq sa sb))
; (assert (eql sa sb))
(assert (equal sa sb))
(assert (equalp sa sb)))
;; Comparison between two equal strings
;; in case-insensitive context.
(let ((sa "foo")
(sb "Foo"))
; implementation-dependent.
; (assert (eq sa sb))
; (assert (eql sa sb))
(assert (not (equal sa sb)))
(assert (equalp sa sb)))
;; Comparison between two element-wisely equal lists.
(let ((la '(a b c))
(lb '(a b c)))
(assert (not (eq la lb)))
(assert (not (eql la lb)))
(assert (equal la lb))
(assert (equalp la lb)))
;; Comparison between two element-wisely equal lists
;; in case-insensitive context.
(let ((la '("foo" "bar" "baz"))
(lb '("Foo" "Bar" "baz")))
(assert (not (eq la lb)))
(assert (not (eql la lb)))
(assert (not (equal la lb)))
(assert (equalp la lb)))
在 Common Lisp the Language 這本 Common Lisp 聖經中,對相等運算有更詳盡的說明,有興趣的讀者可以自行查閱。
邏輯運算 (Logic Operation)
以下是 Common Lisp 的邏輯運算指令:
and
:且 (logical and)or
:或 (logical or)not
:否 (logical not)
and
的運算方式如下:
p | q | p and q |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
or
的運算方式如下:
p | q | p or q |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
not
的運算方式如下:
p | not p |
---|---|
true | false |
false | true |
以下是實例,讀者可和運算方式對照著看:
(defun main ()
; AND
(assert (equal t (and t t)))
(assert (equal nil (and t nil)))
(assert (equal nil (and nil t)))
(assert (equal nil (and nil nil)))
; OR
(assert (equal t (or t t)))
(assert (equal t (or t nil)))
(assert (equal t (or nil t)))
(assert (equal nil (or nil nil)))
; NOT
(assert (equal nil (not t)))
(assert (equal t (not nil)))
(quit))
(main)