特殊操作符、函数、宏
0.0 format
CL-USER>(format t "hello world") ;t=>*standard-output*
hello world
NIL
CL-USER>(format t "~a:~10t~a" "This is" "a shinny day")
This is: a shinny day
NIL
~a=>美化输出(如字符串去引号、关键字去前导冒号),消耗任意一个实参
~10t=>制表(从上一个~t开始共10个空格)
~%=>换行
总之变量都是以~开始
1.函数
1.0 y-or-n-p
专门的输入提示函数
(y-or-n-p "continuing [y/n]?") ;当没有输入y Y N n时重新提示,输入这四个字符之一时则返回t or nil
1.1 defvar 、 defparameter
定义变量
CL-USER>(defvar *db* nil) ;定义变量值为nil(双*暗示这是一个全局变量)
*DB*
defvar 定义的变量可以没有初值,一般为持久存在的
defparameter 定义变量必须带初值
1.2 funcall 、apply
与#’配合,调用作为数据打包然后传进来的函数
funcall=>传入的实参个数已知
CL-USER>(funcall #‘(lambda (x y) (format t "~a:~a" x y)) 2 3)
2:3
NIL
apply=>传入的实参个数未知
apply在传入作为数据的函数后 期待一个参数列表(列表前灵活的可以有若干孤立实参)
1.3 random
(random number &optional state)
[0 number)
1.4 一些数学函数
1.4.0 expt
CL-USER>expt(2 3)
8
2特殊操作符
2.0 if=>(if test-form then-form [else-form])
exp:
(if x (format t"yes") (format t "no"))
如同c里面的 表达式?x:y(表达式为真则执行x 否则执行y)、if-else
2.1 阻止求值
2.1.0 ’ (quote)
避免求值简写为’ 或者说’ 是quote操作符的语法糖
CL-USER>(quote (+ 1 2))
(+ 1 2)
CL-USER>‘(+1 2)
(+ 1 2)
CL-USER>(+ 1 2)
3
2.1.1 ` =>反引号
与, ,@ 等特殊符号结合在不求值中求值
CL-USER>`(0 ,(list 1 2 3))
(0 (1 2 3))
CL-USER>`(0 ,@(list 1 2 3))=>,@不仅后允求值而且将列表(必须是一个列表)的值提取出来
(0 1 2 3)
2.2 eql
对两个lisp对象进行判等
equal更为宽松 允许视递归上具有相同结构和内容的列表为等价
equalp更更为宽松 允许忽略比较字符串时忽略大小写 相同数学意义的数字等价如1 与 1.0
eq 则比eql 更为严格,它不保证1等价于1,它的使用只有点存在争议的象征意义
2.3 return-from
从函数特定的部分返回特定的值(默认在最后一个表达式处返回最后一个表达式的值)
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
;;这个编辑器居然连括号匹配都没有。。。orz更不用说那硬编码风格的缩进。。。我对csdn开始有点失望
(return-from foo (list i j))))))
2.4 #’(function)
将函数打包为数据
2.5 lambda
创建匿名函数
(lambda (parameters) body)
2.6 and、or、not
连接表达式,支持短路求值
3宏
这里的宏是生成代码的“函数”,全部的宏在用函数compile-file编译时会递归的展开成只有函数和特殊形式的代码
3.0 defun
定义函数
(defun name (parameter*)
“Optional documentation string.”
body-form*)
CL-USER>(defun hello-world()
(format t "hello world"))
HELLO-WORLD
CL-USER>(hello-world)
hello world
3.0.1 &optional
可选形参
(defun foo (a b &optional (c 3 c-supplied-p) (d b)
(list a b c c-supplied-p d))
&option 之后的形参可选 可选形参可以加默认值和检测是否传入了实参的变量(一般以-supplied-p结尾)
默认值可以为之前的必要形参
3.0.2 &rest
剩余形参
满足必要形参和可选形参的其余所有形参被收集到剩余形参列表
(defun + (&rest numbers) …)
3.0.3 &key
关键字形参
同可选形参一样(形参名 默认值 检测实参是否传入的变量)
调用时在形参前加冒号
3.0.4 同时使用多种形参
必要形参-》可选形参-》剩余形参-》关键字形参
显然,关键字形参会对剩余形参产生影响
而且对可选形参会导致当传入的多余实参不能把可选形参全部吃掉时,可选形参会吃掉传入的关键字形参吃掉
CL-USER>(defun foo (x &optional y &key z))
FOO
CL-USER>(foo 1 :z 3) ;=>error
所以建议最多混用可选形参与剩余形参
3.1 setf
修改位置的宏
既然是宏,那它的适用范围必是相当广了
(setf name value)
3.1.0
3.1.1 incf 、decf
自增\自减(默认为1)
3.1.2 aref gethash
数组访问\哈希表查找
(setf (aref a 0) 10) ;将数组a 的0号元素设置为10
(setf (gethash ‘key hash) 10) ;将名为hash的哈希表检索项为key的值置为10
3.1.3 shiftf rotatef
将值左移\位置之间向左轮换值
(shiftf a b c)
(rotatef a b c)
3.2 let
在一个控制体中将某些变量暂时置为自定义值,控制体外不受影响(真·多线程)
(let (parameter *) body-form*)
(let ( (x 1) (y 2) z)…)
let控制体中的函数是闭包的(这句话这别扭)
CL-USER>(defparameter *fn* (let ((count 0)) #‘(lambda () (setf count (1+ count)))))
*FN*
CL-USER>*fn*
1
CL-USER>*fn*
2
3.3 cond
相当于c里面的switch
(cond a (test-1)
b (test-2)
t (test-final)) ;t=>非nil,相当于default
3.4 do、dolist、dotimes、loop
循环有关 ,可以使用return 中断
3.4.0 dolist=>方便列表上的循环,类似python中的map
(dolist (var list-form)
body-form*)
CL-USER>(dolist (x ‘(1 2 3)) (print x))
1
2
3
NIL
3.4.1 dotimes=>简单的计数循环
(dotimes (var count-form)
body-form*)
CL-USER>(dolist (x 3) (print x))
0
1
2
NIL
3.4.2 do=>啊,循环的最高抽象,真·奥义!!
(do (variable-definition*) ;(variable-definition)=>(var init-form next-value)
(end-test-form result-form*)
statement*)
斐波那契数列第10个数
CL-USER>(do ((n 0 (1+ n))
(cur 0 next)
(next 1 (+ cur next))
((= n 9) (return cur)))
34 ;返回值被设为cur
CL-USER>(= (1+ n) (+ 1 n)) ; =
T
3.4.3 loop =>隐藏在lisp里的微语言
关键字神多
across、 and、 below 、collecting 、counting 、finally 、for 、from、 summing、 then、 to
CL-USER>(loop for i from 1 to 10 collecting i)
(1 2 3 4 5 6 7 8 9 10)
CL-USER>(loop for x from 1 to 5 summing x)
10
CL-USER>(loop for x across "abc abc abc" counting (find x "ac"))
6
CL-USER>(loop for n below 9
and cur = 0 then next
and next = 1 then (+ cur next)
finally(return cur)) ;简直不是lisp了,总觉得括号少了点哪里不对劲(233),同样是求第十个斐波那契数字
34
关键字
0. nil
false 空列表(唯一的即是原子又是列表的对象)
1. t
true *standard-output*
格式化习惯
0.缩进
应该利用编译器的自动缩进而不是数括号来检查笔误
1.0函数缩进
至少四个空格 ,如果折行是必须的,那么同一层次的项显然应该对齐
2.0 宏及特殊形式
两个空格
3.0 分号
与所注释代码保持相同缩进
1.括号
闭括号总位于它们所闭合列表最后一个元素所的行
exp:
(defun foo ()
(dotimes (i 10)
(format t "~d.hello~%"i)))
不要强行c语言
2.注释
分号进行注释
2.0 ;;;;应该注释整个文件
2.1 ;;;注释之后的大段代码
2.2 ;; 注释紧接着的代码
2.3 ; 就注释所在行就行了