Further Reading:http://lee-mac.com/mapcarlambda.html
官方说明:
(mapcar <function> <list-1> <list-2> ... <list-n>)
将作为本函数参数的一个或多个表的各个元素提供给指定函数进行求值,并将由求值结果构成的表返回。
list1... listn
一个或多个表。表的数目必须与 function 参数中要求的参数个数相等。
返回值:返回由结果组成的表。
(setq a 10 b 20 c 30)
(mapcar '1+ (list a b c))
;;返回 (11 21 31)
等价:((1+ a)(1+ b)(1+ c))
明经:
mapcar 的参数是( 函数 一系列表)
mapcar最让初学者迷惑的地方是因为范例容易让人误解
$ (mapcar '(lambda (x) (+ x 3) ) '(10 20 30) ) =>(13 23 33)
如果再加一个范例
$ (mapcar '(lambda (x) (+ x y) ) '(10 20 30) '(1 2 3))=> (11 22 33)
就也许更清楚了
因为mapcar是把所有表的相同位置的元素取出来作为参数交给函数运算然后返回每次结果构成的新表
上例就是(list (+ 10 3)(+ 20 3)(+ 30 3))
而AUDESK的范例容易让人误会仅仅是把表中的每个元素做为参数,交给函数运算然后返回结果构成的表
所以这个(mapcar 'min '(1 0 0) '(2 2 1) '(1 2 1) '(3 4 0))
就是(list (min 1 2 1 3) (min 0 2 2 4) (min 0 1 1 0))=>(1 0 0)
Example 1
(foreach str (reverse '( "adam" "ben" "claire" "david")) (setq lst (cons (strcase str) lst)) (print str) )
使用mapcar语句:
(mapcar 'strcase '("adam" "ben" "claire" "david"))
等价于:
(list (strcase "adam") (strcase "ben") (strcase "claire") (strcase "david"))
Notice also that mapcar has been supplied with the function strcase prefixed with an apostrophe so that the strcase symbol is not evaluated, but rather treated as an argument for the mapcar function. This could also be achieved using the quote function in the following way:
注意:mapcar 后面的 'strcase 有单引号
同时等价为:
(mapcar (quote strcase) (quote ("adam" "ben" "claire" "david")))
Or using the function function to declare strcase as a function:
或者使用function 函数将 strcase 声明为一个函数。
(mapcar (function strcase) (quote ("adam" "ben" "claire" "david")))
Example 2
Functions with More than One Argument 当mapcar后面的函数有多个参数时
(mapcar '+ '(1 2 3 4 5) '(3 4 5 6 7))
==>
(4 6 8 10 12) = ((+ 1 3) (+ 2 4) (+ 3 5) (+ 4 6) (+ 5 7))
If the supplied lists are unequal in length, mapcar will cease evaluation when the shortest list has been processed, e.g.:
如果提供的参数表的长度不一致,mapcar 将以最短的表的长度为准进行计算,超过该长度的表会被截断。
_$ (mapcar '+ '(1 2 3 4 5) '(3 4 5)) (4 6 8)
The Lambda Function
Suppose that we wish to convert each string to 'Titlecase' such that the first letter of each word is capitalised, for example:
("adam" "ben" "claire" "david")==>Adam" "Ben" "Claire" "David")
//首字母大写
We could again implement a method using the foreach function, and shuffle through our list:
(foreach str (reverse '("adam" "ben" "claire" "david")) (setq lst (cons (strcat (strcase (substr str 1 1)) (substr str 2)) lst)) )
But we could also use mapcar to avoid the need to reverse the list and involve the extra variables. However, there is no function in LISP that allows us to capitalise the first letter of a word, so how to can we provide mapcar with a function to evaluate?
然而我们同样可以使用mapcar 来避免翻转表和其他的赋值,然而lisp里面没有将首字母大写的函数。。。。
Since the function supplied to mapcar is arbitrary, one possible solution could be to define a function ourselves and supply it:
(defun titlecase ( str ) (strcat (strcase (substr str 1 1)) (substr str 2)) ) _$ (mapcar 'titlecase '("adam" "ben" "claire" "david")) ("Adam" "Ben" "Claire" "David")
But this means that we now have an extra function definition in our code that may only be used once and could potentially be located elsewhere in the code, making it far less readable. A far better way to accomplish this task would be to use the lambda function.
但是这(定义一个函数)意味着现在我们的代码里多了一个函数定义,而这个函数可能仅仅使用一次,但是却可以潜在地被代码里的任何地方调用,这使得代码的健壮性降低。一个更好的方法是使用lambda函数来完成这项任务。
//另根据官方帮助文件,将不经常的某一表达式定义为一个函数,会增加系统运行的开销。
The lambda function defines an anonymous function (a defun without a name if you like) and is usually used when the overhead of defining a new function is not justified - as in our case.
(mapcar '(lambda ( s ) (strcat (strcase (substr s 1 1)) (substr s 2))) '("adam" "ben" "claire" "david"))
The code is now far more comprehensible since the operations performed by the anonymous lambda function are in-line with the flow of the code.
使用匿名的lambda函数 在一行代码中就可以将功能实现, 这在“代码流”中比使用函数更连贯更简洁。
Further Examples
The mapcar and lambda functions are best illustrated and understood using examples, so here are a few more for you to study:
Adding one to each element of a list:(将表中的元素做自增1运算)
_$ (mapcar '1+ '(1 2 3 4 5)) (2 3 4 5 6)
Adding two to each element of a list, using a defined function: 通过一个自定义函数将表中各元素数值加二
(defun addtwo ( x ) (+ x 2)) _$ (mapcar 'addtwo '(1 2 3 4 5)) (3 4 5 6 7)
Using an anonymous lambda function: 使用lambda匿名函数完成上述任务
(mapcar '(lambda ( x ) (+ x 2)) '(1 2 3 4 5))
Concatenating a string with an integer, using a defined function taking two arguments: 自定义函数链接字符串和整数
(defun join ( str int ) (strcat str (itoa int)))
_$(mapcar 'join '("a" "b" "c") '(1 2 3))
("a1" "b2" "c3")
Using an anonymous lambda function: 使用lambda匿名函数完成上述任务
(mapcar '(lambda ( str int ) (strcat str (itoa int))) '("a" "b" "c") '(1 2 3))
One final example to scramble the brain: a function to return the average point of a list of points:
一个返回 点表的中心的函数
(defun averagepoint ( lst / n ) (setq n (float (length lst))) (mapcar '/ (apply 'mapcar (cons '+ lst)) (list n n n)) )
解释:(mapcar '/ (apply 'mapcar (cons '+ lst)) (list n n n))
==>
(mapcar 'function (list1) (list2))
(apply 'mapcar (cons '+ lst)) //(apply ’function list)
==>(mapcar '+ lst) ==>
mapcar 的参数是( 函数 一系列表)即
(+ (list1) (list2) (list3) (list4)….. (listn))// 因此((2 3 1) (2 4 1) (1 5 1))子表的数目可以不止3个!!!
Notice that the above function exploits the fact that mapcar will cease evaluation when the shortest list is exhausted, since calling the function with 3D points will result in a 3D average point, and supplying 2D points will result in a 2D average point:
_$ (averagepoint '((2 3 1) (2 4 1) (1 5 1))) (1.66667 4.0 1.0) _$ (averagepoint '((2 3) (2 1) (5 1))) (3.0 1.66667)
Observe that this behaviour could also be obtained by using a lambda function to perform the division: 使用lambda匿名函数完成上述任务
(defun averagepoint ( lst / n ) (setq n (float (length lst))) (mapcar (function (lambda ( x ) (/ x n))) (apply 'mapcar (cons '+ lst))) )
Further Reading
If this tutorial has sparked your interest in all things mapcar and lambda, the following publication from the Autodesk University written by Darren Young also provides an outline of mapcar, lambda and the apply function: