一:function builder
Compose
1:Lambda中args是对外面的一个接口,本身这个函数会返回一个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)