lisp函数(二)apply/mapcar

一:local function

Functions defined via defun or setf of symbol-function are global functions.

局部函数可以由labels来定义,他跟let类似,只是第一个参数不是用来指定新的变量而是一组函数的定义。该参数的形式如下:

( name parameters  body)

(labels ((addlO (x) (+ x 10))
		  (consa (x) (cons 'a x)))
	   (consa (addlO 3)))
(A . 13)

let不同的是,labels定义的函数可以访问labels中定义的任意函数,包括自身,所以说我们可以用它实现递归。

(do ((x a (b x))
     (y c (d y)))
    ((test x y) (z x y))
  (f x y))
Is equivalent to 
CL-USER> (labels ((rec (x y)
		    (cond ((test x y)
			   (z x y))
			  (t
			   (f x y)
			   (rec (b x)(d y ))))))
	   (rec a c))
二:Feature of lisp programming 
1:Lisp consists mostly of Lisp functions,you can modify the language to suit your ideas

2: the functional style seems even better adapted for writing reusable software,You can do the same thing in your own programs by writing utilities.

Mapint 找出n个符合函数fn的对象

Filter   从一个list中找出符合fn的对象

Most   从一个list中找出最符合某有一个条件的对象。

CL-USER> (defun map-int (fn n)
	   (let ((acc nil))
	     (dotimes (i n)
	       (push (funcall fn i) acc))
	     (nreverse acc)))
MAP-INT
CL-USER> (map-int #'identity 10)
(0 1 2 3 4 5 6 7 8 9)
CL-USER> (defun filter (fn lst)
	   (let ((acc nil))
	     (dolist (x lst)
	       (let ((val (funcall fn x)))
		 (if val (push val acc))))
	     (nreverse acc)))
FILTER
CL-USER> (map-int #'(lambda (x) (random 100)) 10)
(81 13 90 83 12 96 91 22 63 30)
CL-USER> (defun most (fn lst)
	   (if (null lst)
	       (values nil nil)
	       (let* ((wins (car lst))
		      (max (funcall fn wins)))
		 (dolist (obj (cdr lst))
		   (let ((score (funcall fn obj)))
		     (when (> score max)
		       (setf wins obj
			     max  score))))
		 (values wins max))))
MOST
CL-USER> (most #'length '((a b) (a b c) (a)))
(A B C)
3

Typecase:which takes arguments of any type and combines them in a way appropriate to their type.

CL-USER> (defun combiner (x)
	   (typecase x
	     (number #'+)
	     (list #'append)
	     (t #'list)))
COMBINER
CL-USER> (defun combine (&rest args1)
	    (apply (combiner (car args1)) ;;从另一方面显示args1是一个列表形式。其实args 跟args1他们的值是不一样的。
		  args))
COMBINE
CL-USER> (combine 2 3)
5
CL-USER> (combine '(a b) '(c d))   
(A B C D)     ;; 根据car判断用哪个函数,然后 
CL-USER> (defun combine (&rest args)
			   (funcall (combiner (car args))
				  args))
COMBINE
CL-USER> (combine '(a b) '(c d))
((A B) (C D))

apply 实现机理:

Apply 如果后面是多个列表的话,它会把这些列表调用list* 方法,然后对生成的列表调用values-list方法。注意values-list 他会返回prolist中的每个元素。而如果包含dot-list的话就会报错。所以这也就是为啥apply 最后一个参数必须是一个prolist。数字或者dotlist都是不行的。

为啥说最后一个参数必须是列表呢?因为list*只会对最后一个列表进行处理,并且一个列表中top-level不可能存在dot-list,比如((1 2).3 (4 5))肯定是不可能的,因为如果中间3是一个点列的话,就不会又后续了。如果次level为点对结构我就不管了,((1 2)(3.4)(5 6))只要这个dotlist不是在最后一个位置就行,((1 2)(3 4)(5 .6))因为它不像前面如果是dot-list的话,不管他,而现在如果你是dotlist,经过append以后。你的点就向外暴漏了一层。((1 2)(3 4)(5 .6))-->((1 2)(3 4)5 .6)这个时候values-list的话,就困惑了。

list* 如果单个列表的话,返回这个列表

      如果多个列表就(append (n-1)-list nth-list)

      如果是单个数字的话,返回这个数字

CL-USER> (list* '(1 2) '(4 6) (cons (cons 4 5) 7))           ((1 2) (4 6) (4 . 5) . 7)
CL-USER> (append (list '(1 2) '(4 6)) (cons (cons 4 5) 7))   ((1 2) (4 6) (4 . 5) . 7)
CL-USER> (list* 1)                                            1
CL-USER> (values-list 1)                                      no value

Values-list 后面必须是一个列表,返回prolist中的元素(Prolist : must be a proper list.)

CL-USER>  (values-list (list* '(1 3) '(9 7)))   ;;凡是节点上的点对结构都是它的一个值。
(1 3)
9
7
CL-USER> (values-list '((1 3) (3 4) 6))
(1 3)
(3 4)
6

出错的例子:

CL-USER> (list* '(1 3) '(4 5) (cons 4 5))
((1 3) (4 5) 4 . 5)
CL-USER> (values-list (list* '(1 3) '(4 5) (cons 4 5)) )

总结:apply 后参数的形式:1:不能是单个的数字

                                                   2:如果是多列表的话,最后一个单元不能是数字或者dotlist

                                                   3:单个列表的话,最后一个单元不能是dotlist

证明apply 实现机理:

CL-USER>   (apply (lambda (x y z)
		 	   (cons z (append x y )))
		 	    '(1 3) '(5 6))
(6 1 3 . 5)
问题1:也许你会想既然apply可以接受一个list,那么如果一个参数的形参也是&rest args,也就相当于我一个列表中不论多少个元素他都会接受。但是你注意了,如果你过多的给他参数的话,程序就会报错。
CL-USER> (apply (lambda (x y z)
		  	(append x y z)) (list '(1 3) '(5 6) '(7 9)))
(1 3 5 6 7 9)
CL-USER>  (apply (lambda (x y z)
		  		(append x y z)) (list '(1 3) '(5 6) '(7 9) '(11 12 13)))

你应该同样会疑问为啥有个arg-0 = 4 吧。并且上面描述说 invalid number of argument 4. 也就是说参数的个数不符。然后我当时的怀疑是这个 4 的话,

1:只会在abend的时候才会赋值给arg-0 (比较可能)

2::本身args就会有一个args-0用于存储到底接受多少个参数。然后不匹配的话就报错。其实对于上面这两种情况我也想检测到底是哪个原因,唉……找不出来方法。。

3:或者这个args跟我们参数中的args仅仅是名字一样而已,不是同一个东西。

总结:Apply  不管三七二十一,它会把后面的先把后面的参数都经过 list* 生成一个 list再 说,然后如果新 list经过values-list处理过以后 ,参数太多的话,就会报错。并且注意最后一个 arg 不能是 dot-list 或数字。
            funcall  不管你给多少实参,我就是需要多少拿多少。

问题二:实参跟实际给函数时的形式不一样,即args != args1

因为我们前面已经说过,当你传递多个列表作为实参的时候,会先调用list*方法,然后调用values-list 会把新表中的每一个元素分好,

1:用于对应于函数的形参,比如上面的式子apply (lambda (x y z)

2:函数的形参也是&rest args1的话,他会把在传递给args1,把刚才values-list分好的块,然后组成一个新的列表,这个的好处首先是易于管理了,整个实参都在args1里面,并且比方说如果函数内部在有用apply方式调用args1的话,因为现在就是一个列表形式,它就不会再因为进行list*/values-list 而与传进来         时的值不一样了。

CL-USER> (apply (lambda (&rest args1)
		  (format t "~a ~%" args1)) '((2 3) (4 5)) '(8 9))
(((2 3) (4 5)) 8 9) 
NIL
问题三:Mapcar/apply的区别

apply 后面的列表必须是量力而行。经过list* 和values-list处理过以后的实参数等于调用args的函数所需要的参数个数。并且其返回值的数量由调用函数来定。function所接受的参数类型,决定了经过函数调用以后,传给他的必须是这个类型。如本例它是oddp,所以args最后取values-list以后必须是只含一个atom的数字。也就是只能有一个元素传递给args1,这个也是为啥下面函数complement函数需要用mapcar的原因,它只能是一次传递一个否则会报错。

Mapcar function :function 需要几个参数,就跟几个列表。每一次调用function都是从各个列表中取出一个。

CL-USER>  (mapcar (complement #'oddp)
				 '(1 2 3 4 5 6))
(NIL T NIL T NIL T)
CL-USER> (defun our-complement (f)
	 		  #'(lambda (&rest args)
	   		    (not (apply f args))))
OUR-COMPLEMENT
CL-USER> (mapcar (our-complement #'oddp) '(1 2 4 5 7 8))
(NIL T T NIL NIL T)
现在有个问题就是,你应该还记得下面这个式子,我当时感觉不伦什么,mapcar都是会传递一个值给函数。如果complement函数中args 接受了一个值1,那么到调用的时候肯定会出错。我们前面已经说了,apply后面的参数不能够是单个数字1.
CL-USER> (mapcar #'+ '(1 2 4 5 6))           (1 2 4 5 6)   ;;+是一个二元函数
CL-USER> (mapcar #'+ '(1 2 4 5) '( 5 6 7))   (6 8 11)
原因是如果形参为&rest args,当 mapcar  后面的函数形参是 &rest  的时候,那么最后 args 的值将会将传过来的值封装起来到一个 list , 而如果就是一个单纯的变量叫 args ,他的值就是传过来时的情形。其实 mapcar 还是仅仅只传一个数字的值,只是碰到 &rest  被封装了,其实我感觉把他封装起来的话,更好进行其它处理,因为 args 的值都在一个 list 中,你可以检验一下 &rest  后面 args 究竟是个什么东西。
CL-USER> (mapcar (lambda (&rest args)
		   (typecase args
		     (number (format t "~a is a number ~%" args))
		     (list   (format t "~a is a list   ~%" args)))) '(1 2 4))
(1) is a list   
(2) is a list   
(4) is a list   
(NIL NIL NIL)
CL-USER> (mapcar (lambda (args)
		   (typecase args
		     (number (format t "~a is a number ~%" args))
		     (list   (format t "~a is a list   ~%" args)))) '(1 2 4))
1 is a number 
2 is a number 
4 is a number 
(NIL NIL NIL)
对上面complement的分析:

首先(our-complement #'oddp) 它会返回一个lambda函数,表面上看着它是接受args个参数,既然接受一个列表,能够用mapcar 也就同样能够用apply了。但是关键的地方是里面有一个(apply f args)这个args它接受的参数还需要f来定。如本例oddp假设你将mapcar换为了apply,现在args='(1 2 4 5 7 8).然后(apply #'oddp '(1 2 4 5 7 8)),而oddp只能是接受一个参数,而现在你一下子给了他6个值,所以会报错。

CL-USER> (apply (our-complement #'+ ) '(1 2 3 5 6))  ;;对比上面mapcar是function的区别

NIL

对+的说明

因为#‘+是一个多元函数,其实下面几种形式都是等价的,初看的话funcall中的形式比较直观,因为给的就是值,而apply通过list*和values-list转化以后同样是孤零零的值传给形参args,然后args会把这些值在重新放到一个列表里面。所以我感觉至于#’+内部肯定是用apply来调用args.而不是funcall.哈哈,如果还

用apply调用的话,你会发现它自己是个环。所以那部分是臆想。

CL-USER> (apply #'+ '( 1 3 4 5))
13
CL-USER> (funcall #'+ 1 3)
4
CL-USER> (funcall #'+ 1 3  4 5 5)
18

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值