基本类型

Table of Contents

Lisp的数据组成:

  1. 原子
  2. CONS
  3. Bool类型
  4. NIL

Common Lisp 操作符分三类:

  1. 函数
  2. 特殊形式

1. 符号(symbol)

每个符号(symbol)都属于某一个包(package)

符号属于某个包,叫“符号被 intern了”,可调用 intern 函数;不是所有符号都被 intern 了,没有被 intern 的符号是 uninterned,gensym 便是。

每个符号对象都有个名字称为“print name”

为什么使用符号?一个有值的符号被称作变量。

为什么不用字符串表示?因为字符串在对比时,要一个字符一个字符做判断,效率低;符号只用判断是否同一内存地址即可。另外符号可以帮助使用宏。

当我们第一次输入一个符号名字时,解析器就在当前package里创建了一个符号对象。可以用一段代码来测试:

(defun list-symbols ()
  (do-all-symbols (s)
    (when (eq *package*
              (symbol-package s))
      (print s))))

1.1. 定义符号

defvar:定义一个符号,如果符号存在的话,不会修改符号。 defparameter:定义一个符号,如果符号存在,就变成新值,否则创建。

1.2. 符号引用

引用:拒绝被求值,(atom 'a) 表达的是代码,第一个元素叫操作符,其余的叫自变量;'(atom 'a) 表达的是数据,这种代码和数据用同样结构表达的叫作同像性。引用的作用就是区分代码和数据。'(atom a b c) 作为数据,它是一个 CONS,我们就可以定义操作这种结构的操作符了——car 和 cdr。

Lisp 定义的七个原始操作符:quote、atom、eq、car、cdr、cons 和 cond,除了 quote 和 cond,其他都叫作“函数”,因为它的自变量总是要被求值的,而 quote 是不求值,并且 cond 有自己的求值方式——它是一个特殊形式。

例,'foo 等同于(quote foo)。

1.3. 属性列表(property list)

属性列表现在已经基本上被 hash 取代了,因为太老了,起源于 Lisp 1.5。每个符号都有属性列表,详细见“数据结构”一节。

1.4. 绑定

“绑定(binding)”的定义如下:

  • 符号和值建立了联系,称为“bound”,如:(defvar a 1)。类似其他编程语言中给变量复制的过程;
  • 符号没有值称为”unbound”,如:(defvar x);
  • 如果符号出现在函数参数列表中,称为“lambda绑定(binding)”;
  • 如果符号出现在 let 或 let* 形式中,叫“LET-binding”,或“本地绑定”。

1.4.1. 本地绑定

let:绑定一个本地变量。函数体最后个值作为表达式返回值。

(let ((x 1))
  (print x))

let*:和 let 区别在于,let* 绑定符号时,多个绑定的变量可以在 bindings 里互相调用:

(let* ((x 1) (y (1+ x)))
  (format t "~a ~a" x y))

再看这段代码:

(let* ((x 1) (y x))
  (print y))

如果改用 let 就会报错:The variable X is unbound.

1.4.2. 解构绑定

解构,就是按位置赋值,类似 Python 的:

>>> a, b, c = [1, 2, 3]
>>> print(a, b, c)
1 2 3

destructuring-bind 宏就用于解构绑定:

(destructuring-bind (a b c) '(1 2 3)
  (format t "~a ~a ~a" a b c))

2. 布尔(boolean)

在 Common Lisp 中,只有 4 种表示会返回 false:'()、()、'nil 和 nil:

(eq 'nil nil) ; => T
(eq 'nil ()) ; => T
(eq 'nil '()) ; => T
(eq nil '()) ; => T

nil:来自拉丁语,意指“什么也没有”。

3. 字符串(string)

双引号包围起来的就是字符串,如:"123"。

3.1. 示例

字符串连接

(concatenate 'string "a" "b") ; => "ab"

字符串替换

(defun string-replace (string replace new-string)
  (with-output-to-string (out)
    (loop for part-len = (length replace)
       for pos = 0 then (+ pos1 part-len)
       for pos1 = (search replace string
                          :start pos)
       do (write-string string out
                        :start pos
                        :end (or pos1 (length string)))
       when pos1 do (write-string new-string out)
       while pos1)))

按空格分割字符串

(defun split-string-by-space (string)
  "按空格分割字符串"
  (loop for i = 0 then (1+ n)
     for n = (position #\Space string :start i)
     collect (subseq string i n)
     while n))

大小写字母转换

(string-downcase "Hello World") ; => "hello world"
(string-upcase "hello world") ; => "HELLO WORLD"

列表转成字符串

(format nil "~{~A~}" '(1 2 3)) ; => "123"

join 操作

(format nil "~{~A~^.~}" '(1 2 3)) ; => "1.2.3"

遍历字符串每个字符

(loop for c across "hello world" do (print c))
;; 或:
(map nil (lambda (c) (print (char-code c))) "hello world")
;; 或:
(map nil (lambda (c) (print (char-code c))) "hello world")

4. 数字(number)

Common Lisp 提供了整数、浮点数、比值和复数四种数字类型,Common Lisp 的数字类型集成关系来源于数学思想里的术语。

一个 Interger 被称为 fixnum,fixnum 的值范围由 most-negative-fixnum 和 most-positive-fixnum 两个常量定义,每种 Common Lisp 实现上,这两个常量取值都不一定是一样的,其值不小于 -215 和 215。如果一个数值超过 fixnum 的范围,就变成 bignum 类型。

Common Lisp 实现了 4 种浮点类型:short-float、single-float、double-float 和 long-float,其范围大小都是由 most-negative-* 和 most-positive-* 定义

类型判断:

(typep most-positive-fixnum 'fixnum) ; => T
(typep (1+ most-positive-fixnum) 'bignum) ; => T

有理数(rational)类型包含了 integer 和 ratios 类型:

(rationalp 1) ; => T
(rationalp 1/2) ; => T
(rationalp 1.1) ; => NIL

实数(real)类型包含了 rational 和浮点型(floating-point):

(realp 1) ; => T
(realp 1.0) ; => T
(realp 1/2) ; => T

number 类型包含了 real 和复数:

(numberp #c(5 -1)) ; => T
(numberp (complex 1 2)) ; => T
(numberp 1) ; => T

判断浮点类型:

(floatp 1.0) ; => T
(floatp 1) ; => NIL
(floatp 1/2) ; => NIL

除法:

(float 3/2) ; => 1.5

指数:

(expt 2 5) ; => 32

对数:

(log 32 2) ; => 5.0

平方根:

(sqrt 4) ; => 2.0

位移操作: bit-vector 位移操作,Common Lisp 提供了几个位移操作的函数:

  • bit-and,逻辑与
  • bit-ior,逻辑或
  • bit-xor,异或
  • bit-not,逻辑非

等等

(bit-and #*0011 #*1101) ; => #*0001

boole 提供了位移操作,第二个参数需要指定位操作符,如 boole-and,boole-xor 等。

(boole boole-and 1 9) ; => 1
(boole boole-xor 1 1) ; => 0

进制:

;; 16 进制
#x5fbcbb                                ; => 6274235

5. 字符(character)

Common Lisp 有 STANDARD-CHAR 和 EXTENDED-CHAR 两种字符,ASCII 码范围内的字符属于 STANDARD-CHAR。

#\c 表示一个字符 c,每个字符都对应一个数值(ASCII 码)

(type-of #\a) ; => STANDARD-CHAR
(type-of #\哈) ; => EXTENDED-CHAR

6. 序列(sequence)

序列,包括 list 和 vector,可用 make-sequence 函数创建。

判断是否为序列:

(typep '(1 2 3) 'sequence) ; => T
(typep (vector) 'sequence) ; => T
(typep (make-array 10) 'sequence) ; => T

详细函数可以见:http://clhs.lisp.se/Body/17_a.htm#sequencefunctions

7. 向量(vector)

使用 mismatch 函数可判断两个向量元素是否相等,如果相等则返回 NIL:

(mismatch #(1 2 3) #(1 2 3)) ; => NIL
(mismatch #(1 2 3) #(4 5 6)) ; => 0
(mismatch #(1 2 3) #(2)) ; => 0
(mismatch #(1 2 3) #(1)) ; => 1