支持两种类型变量:词法变量(lexical)和动态变量(dynamic),分别对应于其他语言中的局部变量和全局变量,只能说是大致相似,并不是所有的语言都有lisp中的闭包的词法作用域变量
与其他语言一样,Common Lisp中的变量时一些可以保存值的具体位置,但他不像java,C++等语言那样带有确定的类型,也就是说不用声明对象所保存的对象的类型。
Common lisp中所有的值都是对象的引用,将一个变量赋予心智就会改变该变量所指向的对象。
1:引入新变量的方式:
1:定义函数形参。形参列表定义了当函数被调用时用来保存实参的变量,也就是用来指向特定对象。绑定代表了变量在运行期的存在。
2:Let特殊操作符。
(let (variable*) body-form*)
平时如果你直接( setf i 4)他总会提示说undefined variable,这是如果你在前面引入let就行了。
如果嵌套引入了同名变量的绑定形式,最内层的变量绑定将覆盖外层的绑定,
用于体现当传给x值时是进行栈式的,刚开始x指向1,然后2进栈,他就指向了2,一旦出词法作用域,最上面的就会出栈,从而暴漏上一个绑定。
CL-USER> (defun foo(x)
(format t "Parameter:~a~%" x)
(let ((x 2))
(format t "outer let:~a~%" x)
(let ((x 3))
(format t "inner let:~a~%" x))
(format t "outer let:~a~%" x))
(format t "Parameter:~a~%" x))
FOO
CL-USER> (foo 1)
Parameter:1
outer let:2
inner let:3
outer let:2
Parameter:1
NIL
CL-USER> (let ((x 10))
(let ((x (+ x 10)))
(list x)))
(20)
CL-USER> (let ((x 10))
(let ((x (+ x 10)))
(list x))
(list x))
(10)
Let与Let*的区别:(let (variable *) body-form*)
Let 中被绑定的变量名只能用在let的形式体中,即body-form
Let* 每个变量的初始形式,都可以引入列表中比他先引入的变量。Variable中的变量y可以用他前面的x来定义
CL-USER> (let* ((x 10)
(y (+ x 10)))
(list x y))
(10 20)CL-USER> (let ((x 10)
(y (+ x 10)))
(list x y))
3:动态变量
Common lisp 提供两种创建全局变量的方式DEFVAR/DEFPARAMETER
CL-USER> (defvar *count* 0)
*COUNT*
CL-USER> (defparameter *ap-tolerance* 0.1)
*AP-TOLERANCE*
两种区别:DEFPARAMETER总是将初始值赋给命名的变量,而DEFVAR只有当变量未定义时才这么做,DEFVAR也可以不带初始值使用,这时为未绑定。
从实践上来说DEFVAR定义某些变量,这些变量所含数据是持久存在的。
DEFVAR 可以使用setf来设定,或者用MAKUNBOUND先将其未绑定,再重新求值DEFVAR形式。
多数语言将标准输入与输出流保存在全局变量里面,*standard-output*
所有的全局变量事实都是动态变量。Let只会临时改变其绑定,而setf会直接改变所指位置的值。
一个动态变量的每个新绑定都将被推到一个用于该变量的绑定栈中,而对该变量的引用总是使用最近的绑定,当绑定形式返回时,他们所创建的绑定会从栈上弹出,从而暴露前一个绑定。(这句话正好总结了前面let 中输出1 2 3 2 1 的原因),从下面的例子你也就看出为啥全局变量都是动态变量啦。
CL-USER> (defvar *x* 10)
*X*
CL-USER> (defun foo()(format t "x: ~d~%" *x*))
STYLE-WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
CL-USER> (defun bar ()
(foo)
(let ((*x* 20)) (foo))
(foo))
BAR
CL-USER> (bar)
x: 10
x: 20
x: 10
NIL
CL-USER> (defun foo()
(format t "before assginment ~18tx: ~d~%" *x*)
(setf *x* (+ 1 *x*))
(format t "after assginment ~18tx: ~d~%" *x*))
FOO
CL-USER> (bar)
before assginment x: 10
after assginment x: 11
before assginment x: 20
after assginment x: 21
before assginment x: 11
after assginment x: 12
4:赋值
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)
(incf x ) ==(setf x (+ x 1))(decf x ) ==(setf x (- x 1))
(incf x 10) == (setf x (+ x 10)
incf decf都是修改宏,类似的还有如下俩个
rotatef 返回nil, shiftf 返回第一个参数最初的值
(rotatef a b c) 也就是互相交换值,c->b->a->c
(shiftf a b c 10) 10->c->b->a 它们两个都是从右往前赋值,但是rotatef正如单词它是轮,所以是一个连接起来的话,因为值相当于往前挪了一位,
CL-USER> (let ((a 1)(b 2)(c 3))
(format t "~s ~s ~s~%" a b c)
(format t "~s~%" (rotatef a b c))
(format t "~s ~s ~s~%" a b c)
(format t "~s~%" (shiftf a b c 10))
(format t "~s ~s ~s~%" a b c)
(format t "~s~%" (shiftf a b c ))
(format t "~s ~s ~s~%" a b c))
1 2 3
NIL
2 3 1
2
3 1 10
3
1 10 10
NIL
下面是C++语言中值传递跟引用传递的例子
int swap(int a, int b) //交换a,b的值
{
int temp = a;
a = b;
b = a;
}
void main()
{
int m = 10, n =20;
swap(m,n);
cout<<m<<endl;
cout<<n<<endl;
//这里m还是10,n还是20,因为传给swap的其实是m,n的拷贝,swap改变的是m和n的拷贝的值,并没有改变m,n的值,因为swap的参数是按值传递的。也就是说在swap中,其实创建了两个临时变量,一个的值为m的值10,另一个的值是n的值20,然后交换了这两个临时变量的值.
}
本文探讨Lisp中的变量类型,包括词法变量(类似局部变量)和动态变量(类似全局变量)。Lisp的变量无需预先声明类型,它们是对象的引用。通过函数形参、Let和Let*特殊操作符来引入新变量,Let*允许内部变量引用外部变量。全局变量由DEFVAR和DEFPARAMETER定义,动态变量的绑定存在于栈中,每次设置都会影响最近的绑定。文章还介绍了incf、decf、rotatef和shiftf等修改宏。
1823

被折叠的 条评论
为什么被折叠?



