明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 9763|回复: 24

[讨论] 有趣的程序语句

    [复制链接]
发表于 2013-8-17 12:13:31 | 显示全部楼层 |阅读模式
本帖最后由 yeahyeah 于 2013-8-18 11:48 编辑

首先需要说明的是:这是一个讨论帖、个人体会积攒帖、各方资源搜集贴,是新人的随笔感悟,而非高手们的厚德分享哦~~~

希望大家一起来说说学习中的小收获,尤其是刚刚学习LISP的朋友们。
当然,也需要和希望各位前辈、高手、师哥师姐们的不吝指教。

那么,下面我们开始吧!哈!我先来!嘿嘿!

========================================================================
(command "pline" (foreach pt data_list (command pt)))   ;data_list是一个表,表的元素是点坐标。

这是个很好玩的语句。前后有两个command。

我第一回看见它时,我是先分析foreach那个括号里的内容,然后我就看不懂了。command一个点啥意思呢?
当我从整个语句来看时,豁然开朗!哈哈哈!


人的思维总是想从内及外,“一层层扒皮”,然后整体分析,这样就与LISP语句的执行顺序不一样了。
也不知道会不会有新人和我一样“想不开”过,把我的体会分享一下。

command执行顺序:
         autolisp执行都是顺序执行,先执行command,调用CAD中的pline命令,然后一一将所需数据代进去。当遇到括号,需要进行求值时,前面执行的command保持,停止,等待,求取括号中的语句值后,command继续执行,将刚求取的值代进去。如此进行下去,直到语句彻底执行完。
        为什么第二个command可以简写?为什么可以这么写?就是和这样的执行顺序有关。已经调用了command,并且处于等待,所以第二个command可以简写。

foreach执行顺序:
先将表中每个元素一次赋给变量。
再对表达式求值。
最后返回最后一次计算(表达式)求值结果。

==================================================================
怎么突然觉得不对劲呢?按照command调用pline命令,后面要加的都是点,还有一个空格呢,这个这么写,怎么能等价呢?

foreach 对每一个表达式求值,但最后仅返回最后一个计算的表达式的结果。
那么(foreach pt data_list (command pt))这个语句中的最后一个计算的表达式的结果是什么呢?

在最后一个点的处理上,是否执行了两次command(一次是函数内求值,一次是foreach函数返回值)呢?
我试一下:
(command "pline" '(100 100) '(100 100) )  ;很明显还要接着画线,还没执行完。那么对于同一点连线到底执行没有呢?
(command "pline" '(100 100) '(100 100) "") ;执行了,有一个点。是点吗?我看看组码。不是,是多段线,两个点同坐标的多段线。

这说明执行相同两次的猜测是错的。
====================================================================
可不可以分开写呢?
(setq data_list (list'(1 2) '(4 2) '( 7 4) '(1 6) '(3 9)))
(command "pline")
(foreach pt data_list (command pt))

(setq data_list (list'(1 2) '(4 2) '( 7 4) '(1 6) '(3 9)))
(command "pline" (foreach pt data_list (command pt)))  

经测试,这两种执行的结果是不一样的。第一种依然处于要画多段线状态,第二种已经执行完。

(setq data_list (list'(1 2) '(4 2) '( 7 4) '(1 6) '(3 9)))
(command "PLINE")
(foreach pt data_list (command pt))
(command “”) ;或
(command )
这种倒是与上面的相同结果。
========================================================================
foreach 对每一个表达式求值,但最后仅返回最后一个计算的表达式的结果。
最后仅返回最后一个计算的表达式的结果。最后仅返回的是结果。结果是什么?
根据函数参考,command的返回值均为 nil 。
那试一下这个:(command "pline"'(1 2) '(4 2) nil)
完全可以画线!

问题答案终于揭开!怪不得(foreach pt data_list (command pt))后面并没有加“”呢!

(setq data_list (list'(1 2) '(4 2) '( 7 4) '(1 6) '(3 9)))
(command "pline" (foreach pt data_list (command pt)))
可以分开改写成这样:
(setq data_list (list'(1 2) '(4 2) '(7 4) '(1 6) '(3 9)))
(command "pline")
(command '(1 2) )
(command '(4 2)  )
(command  '(7 4) )
(command  '(1 6) )
(command '(3 9) )
(command nil)


这种充分利用 nil 值的做法,以后一定要注意和大力发扬!
========================================================================
我有了上面的基础,再看下面的,简单多了!
(command "pline")
(command (list 0 1))
(command (list 9 10))
(command "")

(command "pline")
(command '(0 1))
(command '(9 10))
(command "")

(command "pline")
(command '(0 1))
(command '(9 10))
(command )

(command "PLINE")
(foreach p zbb (command p))
(command "")

评分

参与人数 5明经币 +5 金钱 +80 收起 理由
zctao1966 + 1 很给力!
qjchen + 2 + 30 写得很好,很有钻研精神,支持好文章
oldenn + 1 很给力!
lsjj + 50 赞一个!
tryhi + 1 赞一个!

查看全部评分

本帖被以下淘专辑推荐:

 楼主| 发表于 2013-8-17 13:54:05 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-18 10:58 编辑

(defun c:cd()
    (setq a '("line" (100 100) (1000 1000) ""))
    (apply 'command a)
  )

这个写法蛮有新意的。
在list的函数解释中写到:单引号 ( ' ) 被定义为 quote 函数。
那么quote是什么意思呢?quote 返回表达式而不对其求值。

apply  将参数表传给指定的函数
是参数表,而没有规定表中的数据类型。那么自然也包括command 后面跟的那些主命令、副命令、参数值了!


可是问题在于,一般情况下,我们这么写:
(command "line" '(100 100) '(1000 1000) "")
而如果这么写,则会判定为错误:
(command "line" (100 100) (1000 1000) "")

这说明,这不是直接的简单替换。

command语法解释中写到:
(command [arguments]...)
arguments 是AutoCAD 的命令或副命令。

也就是说command后面跟的每个参数都是“命令型“!
既然是命令,必需说明是什么命令。直接写(100 100),则会被判断为错误(函数错误: 100),因为命令名称不能为数字。这一点可以在控制台里面试试。有趣的是“”则被判断为正确,并有返回值,是它本身“”。

(defun c:cd()
    (setq a '("line" (100 100) (1000 1000) ""))
    (apply 'command a)
  )

这样写,a是一次性地作为一个命令给command的,而(100 100)和 (1000 1000)只是("line" (100 100) (1000 1000) "")的一个参数,而"line"是这句的命令(前面我也已经试过了,“”这种形式会被判断为正确,并有返回值,是它本身“”)。

所以,这样写并不提示错误。
=============================================
(setq mpt (mapcar '(lambda(x y) (* (+ x y) 0.5)) pa pb))

这是求取两点中点的一个程序语句,这也是我长久以来没有弄明白的一个语句。
如果我没记错,我记得好像是vormittag老师在群里告诉我的。

看了《明镜通道Autolisp函数参考》,加上自己的“品味”,我的猜想如下!哈哈哈!

《明镜通道Autolisp函数参考》中写到:
mapcar
将作为本函数参数的一个或多个表的各个元素提供给指定函数进行求值,并将由求值结果构成的表返回。
(mapcar function list1... listn)
表的数目必须与 function 参数中要求的参数个数相等。

为什么要相等?不是把“一个或多个表的各个元素提供给指定函数进行求值”么?那就先取第一个表的元素,取完了再取第二个表中的,然后依次进行下去,直到最后那个表的元素取净完事呗,为什么要让表的数目必须与 function 参数中要求的参数个数相等呢?

好像不应该是表数与 function 参数个数相等啊,而是表中元素个数和 function 参数相等啊。但是那样的话,结果就不是中点了。。

哦,原来这个函数的意思是从后面每个表里面一次性各取一个元素代入 function 中依次求值。求值结果依次放在一个新表里。都求取完了,就把新表做为一个返回值返回!

那么在(setq mpt (mapcar '(lambda(x y) (* (+ x y) 0.5)) pa pb))中,假如pa、pb中的元素个数要是不等会怎样呢?
我试试:
(setq pa '(1 2 3 4 5 6))
(setq pb '(7))
(setq mpt (mapcar '(lambda(x y) (* (+ x y) 0.5)) pa pb))
返回结果是:(4.0),并没有提示出错。也就是说,返回结果表中的元素个数是和短的表的个数相等,以短的为准。

lambda前面为什么要带个小撇(小点)我还不知道。反正无论是和 apply还是和mapcar连用,都带着这个撇(点)。
先这么记着吧。希望哪位老师能让我解惑,小师弟将在此感激不尽!嘻嘻!

点评

也就是说APPLY函数的第一个参数是符号(而不是函数),MAPCAR函数也是一样的。  发表于 2013-8-21 22:30
"lambda前面为什么要带个小撇(小点)我还不知道。" 帮助文档中有函数的说明: (apply ’function list) 参数:'function--函数,可以是表示 defun 或 lambda 表达式的符号。 也就是说APPLY函数的   发表于 2013-8-21 22:28
 楼主| 发表于 2013-8-17 14:43:54 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-18 12:06 编辑

收集相关帖子:
繁花落叶:apply和mapcar的用法

;-------------------------------------------------
;从点列表(point list)得到坐标范围(coordinate extents).
;例如:
(defun GetExtents (plist /)
  (list
    (apply 'mapcar (cons 'min plist))
    (apply 'mapcar (cons 'max plist))
  )
)
(GetExtents '((1 0 0) (2 2 0) (1 2 0) (3 4 0)))

Gu_xl的解释:
(apply 'mapcar (cons 'min '((1 0 0) (2 2 0) (1 2 0) (3 4 0))))
==>
(mapcar 'min '(1 0 0) '(2 2 0) '(1 2 0) '(3 4 0))
==>
(list (min 1 2 1 3) (min 0 2 2 4) (min 0 0 0 0))
----------------------------------------------------------------------------------------------------------
我在下面补充一下:

cons  向表的头部添加一个元素,或构造一个点对

(cons 'min '((1 0 0) (2 2 0) (1 2 0) (3 4 0)))
==>
('min '((1 0 0) (2 2 0) (1 2 0) (3 4 0)))

(apply 'mapcar ('min '((1 0 0) (2 2 0) (1 2 0) (3 4 0))))  ;一次性给mapcar
==>
(apply 'mapcar 'min  '((1 0 0) (2 2 0) (1 2 0) (3 4 0)))  ;等价于分开写,这就像本帖沙发里的一次性给command一样
==>
(list (min 1 2 1 3) (min 0 2 2 4) (min 0 0 0 0))


点评

(mapcar '(lambda (a b) (apply 'mapcar (cons a (eval b)))) '(min max) '(l1 l2) )  发表于 2013-8-18 15:17
 楼主| 发表于 2013-8-17 14:48:42 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-18 15:45 编辑

收集相关帖子:mapcar lambda函数求解
(mapcar '(lambda ( a b ) (apply 'mapcar (cons a b))) '(min max) (list l1 l2))
------------------------------------------------------------------------------------------------------------------
我先试一下这个语句的功能:
(setq l1  '((1 3 2) (2 2 4) (3 4 2)))
(setq l2 '(( 3 6 2) (4 3 3) (5 0 1)))
(mapcar '(lambda (a b) (apply 'mapcar (cons a b)))
               '(min max)
               (list l1 l2)
)
最后结果是((1 2 2) (5 6 3))
的确是找的区域边界。
------------------------------------------------------------------------------------------------------------------
在此感谢群里的[vormittag][pzweng][ZZXXQQ]三位老师为我解疑破疑!

(mapcar '(lambda ( a b ) (apply 'mapcar (cons a b))) '(min max) (list l1 l2))
==>
mapcar function: '(lambda ( a b ) (apply 'mapcar (cons a b)))
mapcar list1: '(min max)
mapcar list2: (list l1 l2)
==>
(apply 'mapcar (cons 'min l1)))    ;为什么不是(apply 'mapcar (cons min l1)))?
(apply 'mapcar (cons 'max l2)))   ;为什么不是(apply 'mapcar (cons max l2)))?
==>
(mapcar  'min  l1))
(mapcar  'max l2))

_$ (cons max '(1 2 3))
(#<SUBR @0eb4d558 MAX> 1 2 3)
_$ (cons 'max '(1 2 3))
(MAX 1 2 3)

(eval (cons max '(1 2 3)))
(eval (cons 'max '(1 2 3)))
为什么这两句就相等呢?
 楼主| 发表于 2013-8-17 15:00:55 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-17 15:48 编辑

收集相关帖子:关于eval、mapcar和foraech的用法困惑

实验一结果:
命令: (setq p0 '(1 1 1))
(1 1 1)
命令: (setq p1 '(2 4 6))
(2 4 6)
命令: (setq p2 '(3 7 11))
(3 7 11)
命令: (foreach p '(p1 p2) (setq p (mapcar '- (eval p) p0)))
(3 7 11)
实验二结果;
命令: (setq p0 '(1 1 1))
(1 1 1)
命令: (setq p1 '(2 4 6))
(2 4 6)
命令: (setq p2 '(3 7 11))
(3 7 11)
命令: (foreach p '(p1 p2) (setq p (mapcar '- p p0)))
; 错误: 参数类型错误: listp P1
红色部分为不相同的地方,在我的理解中,实验二应该为正统的方法(结果证明不对),因为p和p0两者是相同的,p1将值赋给p,p再和p0运算,这有什么不对嘛,为什么还要加(eval p),才正确呢?

==================================================
注意 '(P1 P2) 中的' 等同于 quote函数。
而quote是返回表达式而不对其求值
意味着里面的 '(p1 p2)  = (list 'p1 'p2)
而不是(list p1 p2)
所以用 foreach的时候,P 代表的是表达式,为了得到正确的结果应该要(eval p)

==================================================
 楼主| 发表于 2013-8-17 15:04:06 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-18 10:49 编辑

收集相关帖子:为什么用mapcar画pline线出错

(command "pline")
(mapcar '(lambda (x) (command x)) s1)
(command)      
;不带参数调用 command 相当于键入 ESC 键,这样可取消大多数 AutoCAD 命令。

下面ZZXXQQ老师说得对!
“mapcar返回的是一个表,如上程序返回((command p1)(command p2) ...)没有执行command命令。”

mapcar
将作为本函数参数的一个或多个表的各个元素提供给指定函数进行求值,并将由求值结果构成的返回

它和执行foreach的结果的区别就在于foreach还要返回最后一个计算的表达式的结果值

点评

command "pline")(mapcar'command s1)(commnad)  发表于 2015-10-20 02:13
mapcar返回的是一个表,如上程序返回((command p1)(command p2) ...)没有执行command命令  发表于 2013-8-17 16:30
发表于 2013-8-17 17:33:33 | 显示全部楼层
收下了。。。。新手的福音

点评

我也是新手。学习和探索中。一起努力吧!哈!  发表于 2013-8-18 00:07
 楼主| 发表于 2013-8-17 19:33:27 | 显示全部楼层
本帖最后由 yeahyeah 于 2013-8-18 10:40 编辑

========================================================
语法
    (command [arguments]...)
功能及参数
    此函数可使 AutoLISP 能在 AutoCAD 中执行命令, 然后传回 nil。arguments 是AutoCAD 的命令或副命令。每一个参数在经过分析之后, 将被送至 AutoCAD 系统中来响应其提示语句。命令的名称是以字符串来表示的, 2D点是一个含有两个实型数的表, 3D 点则是一个含有三个实型数的表。
【注意】命令的名称只能在 Command:提示号后才可被AutoCAD接受。
范例
         (setq pt1 '(1.45 3.23))
         (setq pt2 (getpoint "Enter a point:"))
         (command "line" pt1 pt2)
         (command "")
    第一行语法是指定第一点 pt1 的值 。第二行语法是指示使用键入第二点pt2的值。第三行语法是要执行 AutoCAD 上的 Line 命令, 并以此二点为起始点及终点绘出一条线。在此 command 后面的参数是一个字符串及已定义的点, 但您也可用实型数或整型数来作为 command的参数。第四行命令上, command 的参数是一个空字符串 (""), 代表由键盘键入一个空格, 而这种不包含参数的方式 , 即相当于按下 Ctrl + C 键来中止AutoCAD 命令。
假如 AutoCAD 系统变量 CMDECHO (可从 setver 和 getvar存取)设定为 0, 那么由 command 函数所执行的命令将不会返回到屏幕上。command 函数是由 AutoLISP 使用 AutoCAD命令的基本方法。
【注意】getxxx 用户输入函数 (如 getangle、getstring、getint、getpoint 等) 不可以包含在 command 函数中。企图去做这件事将会产生下列信息 error:AutoCAD rejected function并终止这个函数的进行。如果需要用户的输入, 则请如前所述事先启动 getxxx 函数, 或将它们放置于连续的 command 函数调用之间。
    对需要选择一个图形的 AutoCAD 命令(如 BREAK 和 TRIM 命令) 而言, 您可以提供一个以 entsel 得到的表而不需要以鼠标来选取图形。
       AutoCAD 的DTEXTSKETCH命令是直接读取键盘和数字化仪上的输入值, 因此不能使用AutoLISP 的 command 函数。
        如果正在执行 AutoCAD 时, 碰到了PAUSE符号, 而它是command 函数中的一个参数, 那么这个 command 函数将会暂时停止, 以等待用户来直接输入(或作动态牵引)。
【注意】
① 目前在 command 函数中, PAUSE 符号与单一的反斜线所组成的字符串意义相同。您可以直接使用反斜线, 而不使用 PAUSE 符号。但是, 假如这个COMMAND 函数是从菜单项来运行的话, 当 AutoCAD 读到反斜线时, 它将不会暂停 command 函数, 而是将菜单项暂停。而且, 此暂停的结构在AutoLISP以后的版本也可能会需要一个不同的触发值(trigger value)。因此, 我们建议您在command 函数中, 使用 PAUSE 符号而不要使用一个反斜线。
    ② 假如一个命令需要键入一个字符串或属性值时, 正好碰到 PAUSE, 那么只有在系统变量 TEXTEVAL 设定值不为零的情况下, AutoCAD 才会暂停来等待输入。否则, PAUSE 符号的值 (单一的反斜线) 会被当做是要输入的文字, 因而不会发生暂停的状况。
    ③ 当 command 函数暂停来让用户输入时, 此函数仍然是在运行的状态下, 所以用户在这个时候不可以输入另一个 AutoLISP 表达式来求值。
    下述就是一使用 PAUSE 符号的范例:
          (setq blk "MY_BLOCK")   
          (setq old_lay (getvar "clayer"))
          (command "layer" "set" "NEW_LAY" "")
          (command "insert" blk pause "" "" pause)
          (command "layer" "set" old_lay "")
    上述这个程序片段将设定目前层到 NEW_LAY, 暂停已等待使用者输入图块图形的插入点。MY_BLOCK 是一个 X 与 Y 插入比例系数都是 1 的图块图形, 然后暂停等待使用这输入旋转角度, 最后将图层设回原来的图层上。
    如果 command 函数指定了 PAUSE 到 SELECT 命令上, 而且PICKFIRST 设定为启动, 那么 SELECT 命令将得到 PICK FIRST 的效果而不会暂停。
在 DIM:提示符下发出的 Radius Diameter 副指令在某些情形下将产生其他的提示语句。这将导致某些在 R11 期间所写的 LISP 程序(使用这两个副命令的AutoLISP 程序)发生问题。
====================================================================
(command "line" pause pause "")     ; 第一条line
(command "line" pause pause "")     ; 第二条line
====================================================================
收集相关帖子:
请教PAUSE在command 中的应用

Andyhon:

(command "line")
(while (= (logand (getvar "CmdActive") 1) 1) (command pause))
====================================================================
收集相关帖子:
如何取得command pause的返回值

caoyin:

(defun c:tt (/ a)  ;pause 时,用户如拾取新的点,则返回新点坐标
  (command "._move" (entlast) "" '(0 0 0))
  (setq a (getvar "lastpoint"))
  (command pause)
  (if (equal a (getvar "lastpoint") 0.00001)
    (princ "未拾取点")
    (princ "拾取点")
  )
  (princ)
)
发表于 2013-8-17 22:18:46 | 显示全部楼层
支持
 楼主| 发表于 2013-8-17 23:01:18 | 显示全部楼层
什么情况??????这只是我这么个新人自己研究研究,瞎想乱猜,找找资料,就加了精华????
谢谢大家的支持!
感谢ZZXXQQ老师!点评里面我没办法回复。。。
感谢院长提携加精。。。现在。。还有些觉得不正常和不应该。。。。。
不说啥了。。这周六过得有惊喜。。惊着了。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-12-27 01:16 , Processed in 0.183857 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表