基本类型

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便是。

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

为什么使用Symbol?

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

当我们第一次输入一个符号名字时,解析器就在当前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

进制:

CL-USER> #x5fbcbb ; 16进制
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