写给新手,也谈已知选择集中根据实体类型筛选实体及代码优化
本帖最后由 firstinti 于 2012-4-26 11:11 编辑看到斑竹在讨论这个问题,http://bbs.mjtd.com/thread-84990-1-1.html
刚好最近写程序也遇上,以前虽然也有做过但是没有系统考虑过,而且斑竹也提到代码简化的问题,所以现在整理一下把成果发上来,大家讨论讨论,一起感受下lisp的语言之美
我们目的就是要从一个已知选择集中剥离出需要的类型实体,一种办法可以采用网友lazybug 的办法:
(defun c:test ()
(setq ss1 nil ss2 nil)
(if (setq ss1 (ssget '((0 . "LINE,circle"))))
(progn
(setq ss2 (ssget "p" '((0 . "circle"))))
(command "select" ss1 "")
(setq ss1 (ssget "p" '((0 . "LINE"))))
)
)
)
这个代码关键其实就是上面的红字部分,采用命令select来解决重复选择的问题,但是一般对我来说是尽量避免直接采用命令来嵌入程序,因为有时候会有副作用
我的做法是采用(sssetfirst nil ss),也可以达到目的
下面是筛选天正实体的一段代码:
(setq ss(ssget))
;墙
(setq ss-wall(ssget "p" (list(cons 0 "TCH_WALL"))))
(sssetfirst nil ss)
;窗
(setq ss-window(ssget "p" (list(cons 0 "TCH_OPENING")(cons71 1))))
(sssetfirst nil ss)
;柱
(setq ss-column(ssget "p" (list(cons 0 "TCH_COLUMN"))))
这两种办法既有区别也有相通之处,区别下面再说,相同之处就是如果要筛选出不同的实体集就需要不停的重复选择,筛选实体写多了的话就会有大段大段的重复代码。这其实不符合lisp精简高效的结构,所以代码需要优化。经验告诉我们,只要重复的,就是多余的。
比如下面这段代码:
(setq ss(ssget))
;圆
(setq ss-circle(ssget "p" (list(cons 0 "circle"))))
(sssetfirst nil ss)
;线
(setq ss-line(ssget "p" (list(cons 0 "*line"))))
(sssetfirst nil ss)
;文字
(setq ss-text(ssget "p" (list(cons 0 "*text"))))
sssetfirst nil ss)
仅仅筛选三个实体集,就写了这么长的一段,太冗长,这种情况可以用子函数解决:
(defun wmg-ssgetp (ss filter)
(sssetfirst nil ss)
(ssget "p" (list (cons 0 filter)))
)
(这里我顺便说一下,我习惯采用 (list (cons 0 filter)))这种结构,因为参数是可以变化的,这样的做法对于lisp来说好处非常多,而'((0 . "circle"))的局限比较多,尽量少用为好)
后面的代码就简单了:
(setq ss-circle(wmg-ssgetpss "circle") )
(setq ss-lline(wmg-ssgetpss "*line") )
(setqss-text(wmg-ssgetpss "*text") )
筛选选择集个数越多,子函数的优势越明显。但是,是不是觉得还是有很多代码结构是一样的?所以代码还可以进一步优化。
现在面临的问题就是:如何把筛选出的多个选择集赋给多个变量?这里我采用的是字符串转为变量的办法,看看下面这段代码:
(defun c:tt ()
(setq ss (ssget))
(setq vartxtlst (list "ss1" "ss2" "ss3")
filterlst (list "circle" "*line" "*text")
)
(mapcar (function (lambda (x y) (set x (wmg-ssgetp ss y))))
(mapcar 'read vartxtlst)
filterlst
)
)
上面这段代码有两个小技巧,一个是read函数,一个是set函数,这两个函数其实用处是相当大的,但是很容易被忽略,我个人是把这样的函数归类为代码函数,就是专门用来优化程序语言本身的函数,程序优化后,完整的代码如下:
(defun c:tt ()
(defun wmg-ssgetp (ss filter)
(sssetfirst nil ss)
(ssget "p" (list (cons 0 filter)))
)
(setq ss (ssget))
(setq vartxtlst (list "ss1" "ss2" "ss3")
filterlst (list "circle" "*line" "*text")
)
(mapcar (function (lambda (x y) (set x (wmg-ssgetp ss y))))
(mapcar 'read vartxtlst)
filterlst
)
)
到此好像大功告成了,但是真的就结束了么?不是的,其实上面的代码是有问题的,上面代码运行是不会得到想要的结果的。
问题在哪?其实是出在 (sssetfirst nil ss)这个语句上,正确的做法是把 (sssetfirst nil ss)改为(vl-cmdf "select" ss ""),这也就是前面我说有区别的原因。
最后的代码:
(defun c:tt ()
(defun wmg-ssgetp (ss filter)
(vl-cmdf "select" ss "")
(ssget "p" (list (cons 0 filter)))
)
(setq ss (ssget))
(setq vartxtlst (list "ss1" "ss2" "ss3")
filterlst (list "circle" "*line" "*text")
)
(mapcar (function (lambda (x y) (set x (wmg-ssgetp ss y))))
(mapcar 'read vartxtlst)
filterlst
)
)
但是,这样就完了?只要你愿意,你完全可以把上述代码写成一个函数,以后直接调用就好了,lisp的优化之路永远没有尽头。
=======================================================
其实好几年我一直在明经潜水,在这里学习了很多,收获了很多,今天刚好看到斑竹的问题,突然想特别写一个给新手的帖子,因为我也是从新手菜鸟过来的,但是个人水平很差,其实不应该写这些的,希望高手包含。
说得非常好!之前没有关注到这个帖子 (defun C:swed(/ ss ssgetpy)
(defun ssgetpy(ssall entpy)
(sssetfirst nil ssall)
;(vl-cmdf "SELECT" ssall "")
(ssget "P" (list (cons 1 entpy)))
)
(if (setq ss (ssget '((0 . "*TEXT") (1 . "A*,B*,C*,D*") (7 . "宋 宽0.7 高3,宋 宽0.75 高3"))))
(progn
(setq
alst (ssgetpy ss "A*")
blst (ssgetpy ss "B*")
clst (ssgetpy ss "C*")
dlst (ssgetpy ss "D*")
)
)
)
)
这个程序中ssgetpy函数只有效一次,不知道是啥原因! 直接保存选择集是不行的,可以变通一下,选择集转图元表,再转handent表,后表存为全局LDATA。不要再问记录以后怎么调用,能实现记录,自然就知道了该如何读取。 学习了,现在还是新手,要学的还很多啊 我的明细表程序中也有大量的read和set、eval等函数 写得太好了!!! 楼主写的很好,很值得我们新手去研究。我这儿有一处选择集的问题想请教,比如程序中用entmake创建了text的list 表,可否将这个表定义为一个新的ssget,供程序下一步提取应用? 偶也来学习学习 做个记号,回头来学习。 代码优化:质和量。。。无止境啊 谢谢楼主,学习学习。 讲的很详细,学习无止境