基本类型
Table of Contents
Lisp的数据组成:
- 原子
- CONS
- Bool类型
- NIL
Common Lisp 操作符分三类:
- 宏
- 函数
- 特殊形式
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
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