`
iyuan
  • 浏览: 464923 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

lisp初体验-Practical Common Lisp笔记-8.变量

    博客分类:
  • 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)))         ;这样也是可以的,与第一种等价


词汇变量及闭包(好别扭的名字)
这里一段比较晦涩的描述,简单代码表示为:
(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: 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

如果这段代码理解不了,那么停住,理解了再继续。
全局变量的*号是约定成俗的,理论上来说,这样基本上就可以避免局部变量与整体变量间人为失误导致的冲突。

常量
定义方式:
(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 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的世界:宏!

(未完待续)
1
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics