lisp 函数(三)

一:function builder

Compose

1Lambdaargs是对外面的一个接口,本身这个函数会返回一个lambda,然后传给apply fn1 所以fn1将决定传入什么格式的实参。本例sqrt只能是接受一个参数,并且因为最后面是一个列表形式,所以需要用mapcar 把他们拆分开。

2:最里面的函数的返回值必须符合下一个函数的参数个数。如sqrt 返回一个值,而round是接受一个值的函数。

(compose # ' a #'b #'c)等价 #'(lambda (ferest args) (a (b (apply #'c args))))

CL-USER> (defun compose (&rest fns)
	 		 (destructuring-bind (fn1 . rest) (reverse fns)
	   		  #'(lambda (&rest args)     
				 (reduce #'(lambda (v f) (funcall f v))
			      rest
				  :initial-value (apply fn1 args)))))
COMPOSE
CL-USER> (mapcar (compose #'list #'round #'sqrt)
		 '(4 9 16 25))
((2) (3) (4) (5))
Disjoin / Conjoin

CL-USER> (defun disjoin (fn &rest fns)
		   (if (null fns)
		       fn
		       (let ((disj (apply #'disjoin fns)))
			 #'(lambda (&rest args)
			     (or (apply fn args) (apply disj args))))))
DISJOIN
CL-USER> (defun conjoin (fn &rest fns)
	   (if (null fns)
	       fn
	       (let ((conj (apply #'conjoin fns)))
		 #'(lambda (&rest args)
		     (and (apply fn args) (apply conj args))))))
CONJOIN

现在你可以从最简单的说起,当fns = null时,disjoin返回fn.当fns只有一个值的时候,他返回一个函数lambda ,但是你仍需要再funcall/apply disjoin的结果,才能得到形式(apply fn args) or (apply fns args)。当你进行这个式子的时候,你应该知道你想要的结果是(apply fn args) or (apply fn1 args) or ……。现在假设我们就只有一个fn,很容易得到的是if (null fns) fn 当我们有两个的时候。这个时候你就应该考虑一个问题返回值了,如果只有一个的话,返回fn,然后funcall (disjoin fn) 你就可以再后面直接加参数了。但是你得想到一个问题,如果我们有很多的话,你肯定不能说返回fn fn1这种形式的,而是应该返回一个函数lambda对象。如果返回lambda对象的话,你就应该自动进行转化成apply fn args形式,这样才能够使funcall fn 等价于 funcall (lambda (apply fn arg)),至于参数的话,因为你调用apply肯定需要有参数传过来的,这个东西是从lambda的实参传过来。两个的话,(apply fn args) 好了,还差对fn1如果提取出来,你发现仍旧可以把fn1当成fns,然后调用disjoin方法,返回一个fn1。所以你就可以写成(apply fn args) or (apply (apply #'disjoin fn1))也就是(apply (apply #'disjoin fns)

#'fn:它的值就是一个指向一个函数。所以如果你去掉#'的话,他就会自动作为一个函数来处理后面的参数。


Oddp  Is this integer odd? 前提是必须先是整数。如果你给他一个字符什么的,他会报错,而不是返回一个nil.

Interger  后面可以跟object

可以通过下面的例子佐证,disjoin 或者conjoin都首先调用的都是fn,然后fn1 …。注意比较第一个和第三个的区别,因为and/or都有短路效果,所以当是conjoin时候,因为integer可以接受object,所以直接就是nil,后面的就不需要判断,就没有报错。

CL-USER>   (mapcar (disjoin #'integerp #'oddp) '(1 a "a" 1 4))
; No value 
CL-USER>   (mapcar (disjoin #'integerp #'symbolp) '(1 a "a" 1 4))
(T T NIL T T)  
CL-USER>  (mapcar (conjoin #'integerp #'oddp) '(a "a" 1 4))
(NIL NIL T NIL)

可以认为他们是用于增加实参的个数的,关键要分清,lambda的参数是curry的结果,所以调用时候它是在curry结果之后。

CL-USER> (defun curry (fn &rest args)
	   #'(lambda (&rest args2)
	       (apply fn (append args args2))))

CURRY
CL-USER> (defun rcurry (fn &rest args)
	   #'(lambda (&rest args2)
	       (apply fn (append args2 args))))
RCURRY
CL-USER> (funcall (curry #'- 3) 4)
-1
CL-USER> (funcall (rcurry #'- 3) 4)
1

Build function:
cddr        = (compose #'cdr #'cdr)
nth         = (compose #'car #'nthcdr)
atom        =(compose #'not #'consp)
<=          = (disjoin #'< #'=)
Listp        = (disjoin #'null #'consp)
            = (rcurry #'typep 'list)
complement  = (curry #'compose #'not) 比如上面(complement #'oddp)
mapcan      = (compose (curry #'apply #'nconc) #'mapcar)   

二:Dynamic scope

But it's almost the same distinction, because local variables are nearly always lexical variables, and global variables are always special variables.

局部变量默认含有语法范围,比方我们在包含x变量的环境中定义了一个函数,那么在这个函数体中的x指向这个环境中的变量,而不管调用它的地方是否重新定义了这么一个x

CL-USER> (let ((x 10))
	   (defun foo ()
	     x))
FOO
CL-USER> (let ((x 20)) (foo))
10

在动态范围中,有的时候我们想用调用这个函数的环境中的变量,而不是定义这个函数的地方,这个时候我们在这个参数出现的地方都声明为special. 如果你去掉调用函数中declare语句,他会报unbound x

CL-USER> (let ((x 10))
	     (defun foo ()
	       (declare (special x))
	       x))
FOO
CL-USER> (let ((x 20))
	   (declare (special x))
	   (foo))
20

通过setf建立的全局变量,隐式为special

CL-USER> (setf x 30)
30
CL-USER> (foo)
30

Where is dynamic scope useful? Usually it is used to give some global variable a new value temporarily..

CL-USER> (let ((*print-base* 16))
	   (princ 32))
20    ;;in hexadecimal
32    ;;default in decimal

三:closure

CL-USER> (defun make-adder (n)
	   #'(lambda (x)
	       (+ x n)))
MAKE-ADDER
CL-USER> (setf add3 (make-adder 3))
#<CLOSURE (LAMBDA (X) :IN MAKE-ADDER) {2537E8B5}>
CL-USER> (funcall add3 2)
5
CL-USER> (setf add27 (make-adder 27))
#<CLOSURE (LAMBDA (X) :IN MAKE-ADDER) {259D9CAD}>
CL-USER> (funcall add27 2)
29

We can even make several closures share variables.
CL-USER> (let ((counter 0))
	   (defun reset2 ()
	     (setf counter 0))
	   (defun stamp ()
	     (setf counter (+ counter 1))))
STAMP
CL-USER> (list (stamp) (stamp) (reset) (stamp))
(1 2 NIL 3)








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值