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