手冊(cè)實(shí)戰(zhàn):從數(shù)學(xué)運(yùn)算到選擇集操作的核心技巧)
簡(jiǎn)介《LISP函數(shù)大全分類手冊(cè)》是一份面向AutoLISP初學(xué)者與CAD二次開發(fā)者的函數(shù)速查資料以PDF形式系統(tǒng)梳理了AutoLISP常用函數(shù)幫助讀者在編寫和調(diào)試LISP程序時(shí)快速定位語法與用途。資源包內(nèi)僅含1個(gè)PDF文件大小約47KB輕量便攜適合隨時(shí)查閱。手冊(cè)按功能劃分為數(shù)學(xué)運(yùn)算、檢驗(yàn)與邏輯運(yùn)算、轉(zhuǎn)換運(yùn)算、列表處理等模塊逐條列出函數(shù)原型與返回值說明例如數(shù)學(xué)運(yùn)算中的加減乘除、絕對(duì)值、三角函數(shù)與最大公約數(shù)列表處理中的car、cadr、cons、mapcar、nth等并延伸至字符串、文件、等待輸入及幾何運(yùn)算等常用函數(shù)。內(nèi)容預(yù)覽顯示其條目清晰、示例直接便于對(duì)照記憶。目前已有765人學(xué)習(xí)下載適合需要系統(tǒng)掌握AutoLISP函數(shù)體系、提升CAD編程效率的開發(fā)者作為案頭參考。1. 從一份函數(shù)清單說起AutoLISP 手冊(cè)到底能解決什么問題如果你在 CAD 二次開發(fā)這條路上待過一陣大概率遇到過這種場(chǎng)景想寫個(gè)批量改圖層的小工具腦子里知道大概要遍歷選擇集、改對(duì)象屬性但具體用哪個(gè)函數(shù)、參數(shù)順序是什么、返回值是列表還是點(diǎn)對(duì)全得靠翻舊筆記或者去搜零散帖子。手邊那份《LISP函數(shù)大全分類手冊(cè)》就是干這個(gè)用的——它不是教程是一份按功能域歸類的 AutoLISP 函數(shù)速查表覆蓋數(shù)學(xué)運(yùn)算、邏輯判斷、類型轉(zhuǎn)換、列表處理、字符串與文件、交互輸入、幾何運(yùn)算、對(duì)象與選擇集操作、符號(hào)表、系統(tǒng)變量控制、流程控制、錯(cuò)誤處理、顯示打印、內(nèi)存管理一直到 ADS/ARX 外部函數(shù)和 3D 渲染相關(guān)調(diào)用粗算下來十九個(gè)大類、三百多個(gè)函數(shù)條目。這份手冊(cè)適合兩類人一類是剛接觸 AutoLISP、需要一份能按功能查的索引來建立函數(shù)地圖的新手另一類是有一定經(jīng)驗(yàn)、但記不全冷門函數(shù)比如boole的位運(yùn)算參數(shù)、trans的坐標(biāo)系統(tǒng)轉(zhuǎn)換、nentsel返回的嵌套實(shí)體信息結(jié)構(gòu)的老手。它不教你編程思想但能讓你在寫代碼時(shí)少翻幾次文檔。下面我按「怎么把這份清單用起來」的思路拆幾個(gè)關(guān)鍵模塊的實(shí)操方式和踩坑點(diǎn)。2. 數(shù)學(xué)與邏輯函數(shù)別被「簡(jiǎn)單」兩個(gè)字騙了2.1 數(shù)學(xué)運(yùn)算函數(shù)的參數(shù)陷阱手冊(cè)里數(shù)學(xué)部分列了二十來個(gè)函數(shù)看起來最沒技術(shù)含量但實(shí)際寫幾何計(jì)算時(shí)翻車最多的恰恰是這里。先看基礎(chǔ)四則和取整;; 基礎(chǔ)數(shù)學(xué)運(yùn)算示例 (setq a 10.0) (setq b 3) ;; 除法AutoLISP 的 / 是實(shí)數(shù)除法不是整數(shù)截?cái)?(/ a b) ; 返回 3.33333 (/ 10 3) ; 返回 3兩個(gè)整數(shù)相除結(jié)果為整數(shù) (/ 10.0 3) ; 返回 3.33333 ;; fix 截?cái)嗳≌皇撬纳嵛迦?(fix 3.9) ; 返回 3 (fix -3.9) ; 返回 -3 ;; rem 取余符號(hào)跟隨被除數(shù) (rem 10 3) ; 返回 1 (rem -10 3) ; 返回 -1 ;; gcd 最大公約數(shù) (gcd 48 36) ; 返回 12這里第一個(gè)坑就是整數(shù)除法和實(shí)數(shù)除法的區(qū)別。(/ 10 3)返回 3 而不是 3.333因?yàn)閮蓚€(gè)參數(shù)都是整數(shù)AutoLISP 會(huì)做整數(shù)除法。很多人在算比例、算間距的時(shí)候忘了這一點(diǎn)結(jié)果精度直接丟掉。解決辦法很簡(jiǎn)單至少讓一個(gè)操作數(shù)是實(shí)數(shù)寫成(/ 10.0 3)或者(/ 10 3.0)。第二個(gè)坑是fix的行為。它不是四舍五入是直接截?cái)嘈?shù)部分。(fix 3.9)得 3(fix -3.9)得 -3。如果你需要四舍五入得自己寫(fix ( x 0.5))但注意負(fù)數(shù)情況下這個(gè)公式會(huì)出問題負(fù)數(shù)要寫成(fix (- x 0.5))。第三個(gè)坑是三角函數(shù)的角度單位。手冊(cè)里寫得很清楚sin、cos、atan的參數(shù)和返回值都是弧度不是角度。但實(shí)際畫圖時(shí)用戶輸入的角度往往是度數(shù)所以中間必須做轉(zhuǎn)換;; 角度轉(zhuǎn)弧度 (defun dtr (deg) (* deg (/ pi 180.0)) ) ;; 弧度轉(zhuǎn)角度 (defun rtd (rad) (* rad (/ 180.0 pi)) ) ;; 使用示例求 30 度的正弦值 (sin (dtr 30)) ; 返回 0.5dtr和rtd這兩個(gè)轉(zhuǎn)換函數(shù)幾乎是每個(gè) AutoLISP 項(xiàng)目的標(biāo)配建議直接放進(jìn)你的工具函數(shù)庫里。pi是內(nèi)置常數(shù)不需要自己定義直接用就行。2.2 邏輯比較函數(shù)的返回值語義邏輯函數(shù)這塊手冊(cè)列了、/、、、、、and、or、not、eq、equal等??雌饋矶际潜容^但返回值語義有細(xì)微差別用錯(cuò)了會(huì)導(dǎo)致條件判斷走錯(cuò)分支。;; 數(shù)值比較 ( 1 1) ; 返回 T ( 1 1.0) ; 返回 T數(shù)值相等類型不同也返回 T ( abc abc) ; 返回 T字符串比較 ( abc ABC) ; 返回 nil字符串比較區(qū)分大小寫 ;; eq 和 equal 的區(qū)別 (setq list1 (1 2 3)) (setq list2 (1 2 3)) (eq list1 list2) ; 返回 nileq 比較的是同一對(duì)象引用 (equal list1 list2) ; 返回 Tequal 比較的是內(nèi)容 ;; equal 可以帶差量參數(shù)用于浮點(diǎn)數(shù)比較 (equal 1.0 1.0001) ; 返回 nil (equal 1.0 1.0001 0.001) ; 返回 T差量在 0.001 以內(nèi)視為相等eq和equal的區(qū)別是新手最容易混淆的地方。eq比較的是兩個(gè)表達(dá)式是否指向同一個(gè)對(duì)象引用相等equal比較的是內(nèi)容是否相同值相等。對(duì)于列表和字符串幾乎總是應(yīng)該用equal。浮點(diǎn)數(shù)比較更是必須用equal帶差量參數(shù)因?yàn)楦↑c(diǎn)運(yùn)算的精度誤差會(huì)讓返回意料之外的結(jié)果。and和or的短路特性也值得注意。(and a b c)會(huì)從左到右求值遇到第一個(gè) nil 就停止并返回 nil(or a b c)遇到第一個(gè)非 nil 就停止并返回那個(gè)值。這意味著你可以把開銷大的判斷放在后面利用短路來優(yōu)化性能。3. 列表處理與類型轉(zhuǎn)換AutoLISP 的核心操作3.1 列表處理函數(shù)的組合用法AutoLISP 的名字里就帶著 List列表處理是這門語言最核心的部分。手冊(cè)第四章列了car、cadr、caddr、cdr、cons、list、append、length、nth、member、assoc、subst、reverse、mapcar、foreach這些函數(shù)。單獨(dú)看每個(gè)都不難但組合起來能做的事情很多。;; 構(gòu)建和拆解列表 (setq pts (list (0 0) (10 0) (10 10) (0 10))) ;; car 取第一個(gè)元素cdr 取剩余部分 (car pts) ; 返回 (0 0) (cdr pts) ; 返回 ((10 0) (10 10) (0 10)) ;; cadr 等價(jià)于 (car (cdr ...))取第二個(gè)元素 (cadr pts) ; 返回 (10 0) ;; caddr 取第三個(gè)元素 (caddr pts) ; 返回 (10 10) ;; nth 按索引取元素索引從 0 開始 (nth 2 pts) ; 返回 (10 10) ;; length 取列表長(zhǎng)度 (length pts) ; 返回 4 ;; append 合并列表 (append (1 2) (3 4)) ; 返回 (1 2 3 4) ;; cons 將元素添加到列表頭部 (cons (5 5) pts) ; 返回 ((5 5) (0 0) (10 0) (10 10) (0 10)) ;; reverse 反轉(zhuǎn)列表 (reverse pts) ; 返回 ((0 10) (10 10) (10 0) (0 0))這里有個(gè)容易踩的坑append和cons的區(qū)別。append把兩個(gè)列表拼接成一個(gè)新列表cons把單個(gè)元素加到列表頭部。如果你用(cons (1 2) (3 4))得到的是((1 2) 3 4)而不是(1 2 3 4)。想要拼接效果必須用append。另一個(gè)坑是nth的索引從 0 開始不是從 1 開始。寫循環(huán)的時(shí)候如果忘了這一點(diǎn)會(huì)漏掉第一個(gè)元素或者越界。mapcar是列表處理里最強(qiáng)大的函數(shù)之一它把某個(gè)函數(shù)應(yīng)用到多個(gè)列表的對(duì)應(yīng)元素上返回結(jié)果列表;; 對(duì)列表中每個(gè)元素加 1 (mapcar 1 (1 2 3 4)) ; 返回 (2 3 4 5) ;; 兩個(gè)列表對(duì)應(yīng)元素相加 (mapcar (1 2 3) (10 20 30)) ; 返回 (11 22 33) ;; 提取點(diǎn)列表中的所有 X 坐標(biāo) (setq xs (mapcar car pts)) ; 返回 (0 10 10 0) ;; 提取所有 Y 坐標(biāo) (setq ys (mapcar cadr pts)) ; 返回 (0 0 10 10)用mapcar配合car/cadr來批量提取坐標(biāo)是處理點(diǎn)列表的標(biāo)準(zhǔn)操作。比手寫foreach循環(huán)簡(jiǎn)潔得多而且不容易出錯(cuò)。3.2 類型轉(zhuǎn)換函數(shù)的精度與格式控制手冊(cè)第三章的轉(zhuǎn)換函數(shù)在實(shí)際項(xiàng)目中出場(chǎng)率極高尤其是rtos、atof、itoa、angtos這幾個(gè)。CAD 開發(fā)中經(jīng)常需要在數(shù)值和字符串之間來回轉(zhuǎn)換——比如把計(jì)算結(jié)果寫到圖紙標(biāo)注里或者從用戶輸入的字符串里解析出數(shù)值。;; 實(shí)數(shù)轉(zhuǎn)字符串控制小數(shù)位數(shù) (rtos 3.1415926 2 2) ; 返回 3.14模式 2 為十進(jìn)制精度 2 位小數(shù) (rtos 3.1415926 2 4) ; 返回 3.1416 (rtos 100.0 2 0) ; 返回 100 ;; 字符串轉(zhuǎn)實(shí)數(shù) (atof 3.14) ; 返回 3.14 (atof abc) ; 返回 0.0無法解析時(shí)返回 0 ;; 字符串轉(zhuǎn)整數(shù) (atoi 42) ; 返回 42 (atoi 3.14) ; 返回 3截?cái)嘈?shù)部分 ;; 整數(shù)轉(zhuǎn)字符串 (itoa 42) ; 返回 42 ;; 角度轉(zhuǎn)字符串 (angtos 1.5708 0 2) ; 返回 90.00模式 0 為十進(jìn)制度數(shù) (angtos 1.5708 1 4) ; 返回 90d00\模式 1 為度分秒rtos的模式參數(shù)決定了輸出格式模式 1 是科學(xué)計(jì)數(shù)法模式 2 是十進(jìn)制模式 3 是工程格式模式 4 是建筑格式。精度參數(shù)控制小數(shù)位數(shù)。這兩個(gè)參數(shù)配合使用可以精確控制輸出字符串的樣子。atof和atoi在解析失敗時(shí)不會(huì)報(bào)錯(cuò)而是返回 0。這個(gè)行為有時(shí)候是便利有時(shí)候是隱患。如果你需要嚴(yán)格校驗(yàn)用戶輸入不能只靠atof的返回值來判斷得先用distof或者自己寫正則檢查。trans函數(shù)是坐標(biāo)轉(zhuǎn)換的關(guān)鍵手冊(cè)里只給了一行說明但實(shí)際用起來參數(shù)含義需要仔細(xì)理解;; 將用戶坐標(biāo)系中的點(diǎn)轉(zhuǎn)換到世界坐標(biāo)系 (trans (10 20 0) 1 0) ; 從 UCS 轉(zhuǎn)到 WCS ;; 將世界坐標(biāo)系中的點(diǎn)轉(zhuǎn)換到當(dāng)前用戶坐標(biāo)系 (trans (10 20 0) 0 1) ; 從 WCS 轉(zhuǎn)到 UCS ;; 將點(diǎn)從當(dāng)前坐標(biāo)系轉(zhuǎn)到指定視口坐標(biāo)系 (trans pt 1 2) ; 從 UCS 轉(zhuǎn)到 DCS顯示坐標(biāo)系trans的第二個(gè)參數(shù)是原坐標(biāo)系代碼第三個(gè)參數(shù)是新坐標(biāo)系代碼。常用的代碼有0 表示 WCS世界坐標(biāo)系1 表示當(dāng)前 UCS用戶坐標(biāo)系2 表示 DCS顯示坐標(biāo)系。寫涉及坐標(biāo)拾取和轉(zhuǎn)換的代碼時(shí)如果不做trans轉(zhuǎn)換拿到的點(diǎn)可能跟你預(yù)期的不一樣尤其是在用戶旋轉(zhuǎn)了 UCS 之后。4. 對(duì)象操作與選擇集從「拿到對(duì)象」到「改對(duì)屬性」4.1 選擇集構(gòu)建的過濾技巧手冊(cè)第九章的選擇集函數(shù)是 CAD 批量操作的基礎(chǔ)。ssget是最核心的一個(gè)它的過濾列表參數(shù)決定了你能多精確地篩選目標(biāo)對(duì)象。;; 選取所有實(shí)體 (setq ss (ssget X)) ;; 選取所有圓 (setq ss (ssget X ((0 . CIRCLE)))) ;; 選取圖層 WALL 上的所有直線 (setq ss (ssget X ((0 . LINE) (8 . WALL)))) ;; 選取半徑大于 50 的圓 (setq ss (ssget X ((0 . CIRCLE) (-4 . ) (40 . 50.0)))) ;; 選取顏色為紅色的對(duì)象顏色號(hào) 1 (setq ss (ssget X ((62 . 1)))) ;; 組合條件圖層為 WALL 或 DOOR 上的直線 (setq ss (ssget X ((0 . LINE) (-4 . OR) (8 . WALL) (8 . DOOR) (-4 . ENDOR))))過濾列表的格式是點(diǎn)對(duì)(組碼 . 值)。組碼 0 是對(duì)象類型8 是圖層名62 是顏色號(hào)40 是圓的半徑。邏輯運(yùn)算符用-4組碼配合字符串表示OR、AND、NOT、、、等。這些運(yùn)算符可以嵌套但要注意配對(duì)閉合。一個(gè)常見的坑是ssget的過濾條件對(duì)不同類型的對(duì)象行為不一致。比如組碼 40 對(duì)圓是半徑對(duì)文字是字高對(duì)多段線是全局寬度。如果你寫了一個(gè)過濾條件但沒指定對(duì)象類型可能會(huì)選到意料之外的東西。所以過濾列表里最好總是帶上(0 . 類型)。遍歷選擇集的標(biāo)準(zhǔn)寫法;; 遍歷選擇集統(tǒng)計(jì)對(duì)象數(shù)量 (setq ss (ssget X ((0 . LINE)))) (setq count (sslength ss)) (setq i 0) (while ( i count) (setq ent (ssname ss i)) (setq edata (entget ent)) ;; 在這里處理每個(gè)對(duì)象 (setq i (1 i)) )sslength返回選擇集中的對(duì)象數(shù)量ssname按索引取出對(duì)象名索引從 0 開始entget取出對(duì)象的屬性列表。這個(gè)三段式循環(huán)是 AutoLISP 批量處理的標(biāo)準(zhǔn)骨架幾乎每個(gè)工具都會(huì)用到。4.2 實(shí)體屬性修改的正確姿勢(shì)拿到實(shí)體數(shù)據(jù)列表之后修改屬性的流程是用entget取出數(shù)據(jù)用subst替換要改的組碼值用entmod寫回最后用entupd刷新顯示。;; 修改選中直線的圖層 (defun c:chglayer ( / ent edata newdata) (setq ent (car (entsel \n選擇要修改的直線: ))) (setq edata (entget ent)) ;; 替換組碼 8圖層名的值 (setq newdata (subst (cons 8 NEWLAYER) (assoc 8 edata) edata)) ;; 寫回修改后的數(shù)據(jù) (entmod newdata) ;; 刷新實(shí)體顯示 (entupd ent) (princ \n圖層已修改。) (princ) )這段代碼里subst的用法是關(guān)鍵(subst 新項(xiàng) 舊項(xiàng) 列表)會(huì)在列表中查找舊項(xiàng)并替換為新項(xiàng)。assoc用來找到組碼對(duì)應(yīng)的點(diǎn)對(duì)。順序不能反subst的第一個(gè)參數(shù)是新值第二個(gè)是舊值。entmod修改數(shù)據(jù)后屏幕上的圖形不一定會(huì)立即刷新尤其是修改了頂點(diǎn)坐標(biāo)或者文字內(nèi)容的情況。entupd強(qiáng)制刷新指定實(shí)體的顯示。如果修改的是塊定義內(nèi)部的實(shí)體還需要用entupd傳入塊名來刷新整個(gè)塊。另一個(gè)坑是entmod不能修改某些只讀屬性比如實(shí)體的句柄組碼 5和某些擴(kuò)展數(shù)據(jù)。嘗試修改這些屬性會(huì)靜默失敗不報(bào)錯(cuò)但也不生效。遇到修改不生效的情況先檢查是不是動(dòng)了不該動(dòng)的組碼。5. 避坑與排查那些手冊(cè)不會(huì)告訴你的翻車現(xiàn)場(chǎng)5.1 變量未聲明導(dǎo)致的「玄學(xué)」行為AutoLISP 的變量默認(rèn)是全局的除非你在defun的變量列表里用/分隔聲明為局部變量。不聲明局部變量的后果是函數(shù)執(zhí)行完后變量還留在內(nèi)存里下次調(diào)用時(shí)可能帶著上次的殘留值導(dǎo)致結(jié)果時(shí)對(duì)時(shí)錯(cuò)。;; 錯(cuò)誤寫法i 和 ent 是全局變量 (defun c:badfunc () (setq i 0) (while ( i 10) (setq ent (ssname ss i)) (setq i (1 i)) ) ) ;; 正確寫法i 和 ent 聲明為局部變量 (defun c:goodfunc ( / i ent) (setq i 0) (while ( i 10) (setq ent (ssname ss i)) (setq i (1 i)) ) )現(xiàn)象是函數(shù)第一次運(yùn)行正常第二次運(yùn)行結(jié)果不對(duì)。原因是第一次運(yùn)行后i的值留在了內(nèi)存里第二次運(yùn)行沒有重新初始化。解決辦法就是在defun的變量列表里用/把局部變量列出來。這個(gè)坑幾乎每個(gè) AutoLISP 新手都會(huì)踩一次而且因?yàn)楝F(xiàn)象不穩(wěn)定排查起來很費(fèi)時(shí)間。5.2 選擇集為空時(shí)的靜默失敗ssget在沒有選中任何對(duì)象時(shí)返回nil但后續(xù)的sslength和ssname對(duì)nil操作不會(huì)報(bào)錯(cuò)而是返回 0 或者nil。這導(dǎo)致代碼看起來在正常運(yùn)行實(shí)際上什么都沒做。;; 有問題的寫法沒有檢查 ssget 返回值 (setq ss (ssget X ((0 . CIRCLE)))) (setq n (sslength ss)) ; ss 為 nil 時(shí)返回 0 (princ (strcat \n找到 (itoa n) 個(gè)圓。)) ;; 穩(wěn)妥的寫法先判斷選擇集是否有效 (setq ss (ssget X ((0 . CIRCLE)))) (if ss (progn (setq n (sslength ss)) (princ (strcat \n找到 (itoa n) 個(gè)圓。)) ) (princ \n沒有找到符合條件的圓。) )現(xiàn)象是程序不報(bào)錯(cuò)但也不產(chǎn)生任何效果。原因就是選擇集為空后續(xù)循環(huán)一次都沒執(zhí)行。解決辦法是在ssget之后立即用if判斷返回值為空時(shí)給出提示或者提前返回。5.3 坐標(biāo)轉(zhuǎn)換遺漏導(dǎo)致的偏移在用戶自定義坐標(biāo)系下拾取的點(diǎn)如果不做trans轉(zhuǎn)換就直接用于entmake或者command調(diào)用畫出來的東西會(huì)偏離預(yù)期位置。;; 有問題的寫法直接使用 getpoint 的返回值 (defun c:badcircle ( / pt) (setq pt (getpoint \n指定圓心: )) (command CIRCLE pt 50) ; UCS 旋轉(zhuǎn)時(shí)位置會(huì)偏 ) ;; 正確的寫法轉(zhuǎn)換到 WCS 再使用 (defun c:goodcircle ( / pt) (setq pt (getpoint \n指定圓心: )) (setq pt (trans pt 1 0)) ; 從 UCS 轉(zhuǎn)到 WCS (command CIRCLE pt 50) )現(xiàn)象是在默認(rèn) WCS 下運(yùn)行正常用戶一旦旋轉(zhuǎn)了 UCS畫出來的圓就跑到別的地方去了。原因是getpoint返回的是當(dāng)前 UCS 下的坐標(biāo)而command調(diào)用的CIRCLE命令在某些情況下期望的是 WCS 坐標(biāo)。解決辦法是在拾取點(diǎn)之后、使用之前統(tǒng)一用(trans pt 1 0)轉(zhuǎn)到 WCS。5.4 字符串比較的大小寫陷阱用于字符串比較時(shí)區(qū)分大小寫但用戶輸入往往大小寫不固定。用直接比較用戶輸入和預(yù)設(shè)字符串很容易因?yàn)榇笮懖灰恢露袛嗍 ?; 有問題的寫法區(qū)分大小寫 (if ( (getstring \n輸入選項(xiàng)(Y/N): ) Y) (princ \n你選擇了是。) (princ \n你選擇了否。) ) ;; 穩(wěn)妥的寫法統(tǒng)一轉(zhuǎn)大寫再比較 (if ( (strcase (getstring \n輸入選項(xiàng)(Y/N): )) Y) (princ \n你選擇了是。) (princ \n你選擇了否。) )現(xiàn)象是用戶明明輸入了 Y程序卻走了否的分支。原因是用戶輸入的是小寫 y而比較時(shí) y 不等于 Y。解決辦法是用strcase把輸入統(tǒng)一轉(zhuǎn)成大寫或小寫再比較。strcase的第二個(gè)參數(shù)傳T轉(zhuǎn)小寫不傳或傳nil轉(zhuǎn)大寫。5.5 文件句柄未關(guān)閉導(dǎo)致的鎖定用open打開文件讀寫后如果中間發(fā)生錯(cuò)誤導(dǎo)致close沒執(zhí)行文件會(huì)一直被鎖定后續(xù)操作無法訪問。;; 有風(fēng)險(xiǎn)的寫法出錯(cuò)時(shí)文件不會(huì)關(guān)閉 (setq f (open data.txt w)) (write-line some data f) (close f) ;; 穩(wěn)妥的寫法用錯(cuò)誤處理確保文件關(guān)閉 (defun c:writefile ( / f) (setq f (open data.txt w)) (if f (progn (write-line some data f) (close f) ) (princ \n無法打開文件。) ) )現(xiàn)象是程序運(yùn)行一次后再次運(yùn)行時(shí)報(bào)錯(cuò)說文件無法訪問。原因是上一次運(yùn)行中文件句柄沒有正常關(guān)閉。解決辦法是在打開文件后立即判斷句柄是否有效并在所有可能的退出路徑上都執(zhí)行close。更嚴(yán)謹(jǐn)?shù)淖龇ㄊ怯?error*自定義錯(cuò)誤處理函數(shù)在出錯(cuò)時(shí)關(guān)閉文件。6. 進(jìn)階技巧用 mapcar 和 apply 寫出更緊湊的代碼6.1 mapcar 配合 lambda 實(shí)現(xiàn)匿名函數(shù)mapcar的第一個(gè)參數(shù)可以是函數(shù)名也可以是一個(gè)lambda表達(dá)式。用lambda可以避免為了一次性操作單獨(dú)定義命名函數(shù)。;; 用 lambda 對(duì)列表每個(gè)元素做平方 (mapcar (lambda (x) (* x x)) (1 2 3 4 5)) ; 返回 (1 4 9 16 25) ;; 用 lambda 提取點(diǎn)列表中的 Z 坐標(biāo) (setq pts ((0 0 0) (10 0 5) (10 10 3))) (mapcar (lambda (p) (caddr p)) pts) ; 返回 (0 5 3) ;; 用 lambda 對(duì)兩個(gè)列表做自定義運(yùn)算 (mapcar (lambda (a b) ( (* a 2) b)) (1 2 3) (10 20 30)) ; 返回 (12 24 36)lambda的寫法是(lambda (參數(shù)) 表達(dá)式)前面的單引號(hào)不能少否則 AutoLISP 會(huì)試圖求值。這個(gè)技巧在需要對(duì)列表做一次性轉(zhuǎn)換時(shí)特別有用代碼量能減少一半。6.2 apply 將列表展開為參數(shù)apply把函數(shù)應(yīng)用到參數(shù)列表上相當(dāng)于把列表「展開」成函數(shù)的多個(gè)參數(shù)。;; 用 apply 求和 (apply (1 2 3 4 5)) ; 返回 15 ;; 用 apply 求最大值 (apply max (3 1 4 1 5 9)) ; 返回 9 ;; 用 apply 調(diào)用自定義函數(shù) (defun my-add (a b c) ( a b c)) (apply my-add (10 20 30)) ; 返回 60apply和mapcar的區(qū)別mapcar對(duì)列表的每個(gè)元素分別應(yīng)用函數(shù)返回結(jié)果列表apply把整個(gè)列表作為參數(shù)傳給函數(shù)返回單個(gè)結(jié)果。當(dāng)你有一個(gè)參數(shù)列表需要傳給一個(gè)接受多個(gè)參數(shù)的函數(shù)時(shí)apply就是正確的工具。6.3 用 assoc 和 subst 做數(shù)據(jù)驅(qū)動(dòng)的屬性修改結(jié)合assoc、subst和mapcar可以寫出很緊湊的批量屬性修改代碼;; 批量修改選擇集中所有對(duì)象的顏色 (defun c:batchcolor ( / ss i ent edata) (setq ss (ssget X ((0 . LINE,CIRCLE,ARC)))) (if ss (progn (setq i 0) (while ( i (sslength ss)) (setq ent (ssname ss i)) (setq edata (entget ent)) ;; 如果已有顏色組碼則替換沒有則添加 (if (assoc 62 edata) (setq edata (subst (cons 62 1) (assoc 62 edata) edata)) (setq edata (append edata (list (cons 62 1)))) ) (entmod edata) (setq i (1 i)) ) (princ (strcat \n已修改 (itoa (sslength ss)) 個(gè)對(duì)象的顏色。)) ) (princ \n沒有找到符合條件的對(duì)象。) ) (princ) )這段代碼里有個(gè)細(xì)節(jié)值得注意如果實(shí)體本身沒有顏色屬性組碼 62assoc返回nil此時(shí)不能用subst替換而應(yīng)該用append把新的點(diǎn)對(duì)追加到數(shù)據(jù)列表末尾。這個(gè)判斷分支是很多批量修改工具容易忽略的地方——對(duì)于「默認(rèn)顏色」的實(shí)體直接subst會(huì)失敗因?yàn)檎也坏揭鎿Q的舊項(xiàng)。從那以后我每次寫批量修改屬性的代碼都會(huì)先檢查目標(biāo)組碼是否存在存在就subst不存在就append這個(gè)習(xí)慣幫我省了不少調(diào)試時(shí)間。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取