lisp初体验-Practical Common Lisp笔记-8.变量
原本以为这章很好理解,结果..杯具了。
在lisp中,变量分为两种:词汇变量(lexical),动态变量(dynamic)。#感觉很别扭,估计我翻坏了,虽然与其他语言做了类比,相当于:局部变量和全局变量。可是看了后面,似乎不是这么个回事啊。如果有资深lisper能够给出这两个变量的专业名称(中文),那真是不胜感激了。目前,好吧,就姑且从字面上来称呼这两种变量吧。
作为变量的基本要素之一就是,变量可以持有一个值。而在Lisp中,甚至无所谓值的类型。很爽,但也会有麻烦:只有到了编译的时候才会发现这方面的错误。
最常见的定义变量方式之一:
(defun foo (x y z) (+ x y z))
在定义函数时,不知不觉就定义了3个变量(x,y,z),这样的好处就是:不同的函数调用方互不影响。
另一种定义变量的方式:
(let (variable*) body-form*)(let ((x 10) (y 20) z) ...)
为了表现出变量的情况:
(defun foo (x) (format t "Parameter: ~a~%" x) ; |<------ x is argument (let ((x 2)) ; | (format t "Outer LET: ~a~%" x) ; | |<---- x is 2 (let ((x 3)) ; | | (format t "Inner LET: ~a~%" x)) ; | | |<-- x is 3 (format t "Outer LET: ~a~%" x)) ; | | (format t "Parameter: ~a~%" x)) ; |
这里有用到LET,他还有一个变种:LET*,简单理解,就是let不允许用前面定义的变量,而let*则可以,例:
(let* ((x 10) (y (+ x 10))) (list x y)) ;这是可以的(let ((x 10) (y (+ x 10))) (list x y)) ;这是不行的(let ((x 10)) (let ((y (+ x 10))) (list x y))) ;这样也是可以的,与第一种等价
词汇变量及闭包(好别扭的名字)
这里一段比较晦涩的描述,简单代码表示为:
(let ((count 0)) #'(lambda () (setf count (1+ count))))
这里面的count变量只能在随后的匿名函数中使用,过期不候,所以"局部又闭包"啊。
当然,万事有例外,要是函数在关闭时,定义、操作了个全局变量,那么就又不一样了:
(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))
试试:
(funcall *fn*)1(funcall *fn*)2(funcall *fn*)3
动态变量
lisp的动态变量有两种声明方式:
(defvar *count* 0 "Count of widgets made so far.")(defparameter *gap-tolerance* 0.001 "Tolerance to be allowed in widget gaps.")
两者间的差别是:defparameter必须带变量值,而defvar则无所谓。
虽说全局变量到处都能改,不过规范成一个函数,到处调用还是相当靠谱的:
(defun increment-widget-count () (incf *count*))
还有,虽然全局变量很好用,不过最好还是必须时用,天知道你的代码啥时候变量冲突了捏,一步小心影响了全局可就够喝一壶了。
下面有一段代码,类似于"局部屏蔽全局":
(defvar *x* 10)(defun foo () (format t "X: ~d~%" *x*))(foo)X: 10NIL(let ((*x* 20)) (foo))X: 20NIL(foo)X: 10NIL(defun bar () (foo) (let ((*x* 20)) (foo)) (foo))(bar)X: 10X: 20X: 10NIL
想要操作全局变量的话:
(defun foo () (format t "Before assignment~18tX: ~d~%" *x*) (setf *x* (+ 1 *x*)) (format t "After assignment~18tX: ~d~%" *x*))(foo)Before assignment X: 10After assignment X: 11NIL(bar)Before assignment X: 11After assignment X: 12Before assignment X: 20After assignment X: 21Before assignment X: 12After assignment X: 13NIL
如果这段代码理解不了,那么停住,理解了再继续。
全局变量的*号是约定成俗的,理论上来说,这样基本上就可以避免局部变量与整体变量间人为失误导致的冲突。
常量
定义方式:
(defconstant name initial-value-form [ documentation-string ])
常量算是一种特殊变量,绑定值后就不再变化。命名约定规则与动态变量相似,用加号(+)包夹。
分配
对于变量而言,赋值是通过宏setf进行的:
(setf place value)
还能一次赋值多个变量:
(setf x 1 y 2)
setf返回值为变量值,所以也可以多变量指同一个值:
(setf x (setf y (random 10)))
Lisp与其他语言赋值对应表:
Assigning to ...Java, C, C++Perl Python
... variable x = 10; $x = 10; x = 10
... array elementa[0] = 10;$a[0] = 10; a[0] = 10
... hash table entry-- $hash{'key'} = 10; hash['key'] = 10
... field in objecto.field = 10;$o->{'field'} = 10; o.field = 10
Simple variable: (setf x 10)
Array: (setf (aref a 0) 10)
Hash table: (setf (gethash 'key hash) 10)
Slot named 'field': (setf (field o) 10)
其他的一些修改变量值的方式:
如果需要对变量进行一些针对原值的操作的话,比如:
(setf x (+ x 1))
着实麻烦了些,所以Lisp有内置的一些宏:
(incf x) === (setf x (+ x 1))(decf x) === (setf x (- x 1))(incf x 10) === (setf x (+ x 10))
还有ROTATEF和SHIFTF两种常用的宏:
(rotatef a b)
调换a b的值,
(shiftf a b 10)
将值左移:b的值给a,10赋给b。
这一章还有很多晦涩的描述(个人能力有限啊),不过,大体上lisp的变量就是如此了。结尾段给了好多的宏,呵呵,下一章将进入lisp之所以为lisp的世界:宏!
(未完待续)