第一章SKILL 語言基礎(chǔ) 1.1 SKILL 語言簡介 SKILL 是用于Cadence 軟件二次開發(fā)的語言。我們關(guān)于SKILL 的介紹基于SKILL 語言參考檔和Allegro SKILL的應(yīng)用。 1.1.1 語法格式 SKILL語法支持lisp 格式的語法,但是我們不推薦,推薦類C 的語法格式。 Lisp示例: (max 5 3) => 5 C 示例: max(5 3) => 5 Max是求最大值的函數(shù),lisp 格式的不便閱讀。 1.1.2 簡單的SKILL 運(yùn)行環(huán)境 Allegro 的SKILL 運(yùn)行環(huán)境:在Allegro 的命令窗口輸入set telskill 窗口就可以用來運(yùn)行就可以打開一個(gè)新的窗口,這個(gè)SKILL 命令。窗口的大小可以手動調(diào)節(jié)。 1.1.3 Load Skill 可以直接在SKILL development里面運(yùn)行SKILL,可以把寫好的代碼放load命令一次性裝載。 在說明load 之前,先說一下getSkillPath和setSkillPath 這2 個(gè)函數(shù)。因?yàn)閘oad 要找到SKILL 來裝載,通常我們都不喜歡通過絕對路徑來裝載一個(gè)文件,因?yàn)槟菢影淖址嗔?。文件getSkillPath() 將得到當(dāng)前Allegro 設(shè)置的SKILL 加載路徑,而setSkillPath 用來指定加載路徑。目前先不用考慮這個(gè)setSkillPath。 我的Allegro當(dāng)前默認(rèn)的SKILL 路徑是Allegro 的工作路徑和C:\home\pcbenv\skill這樣我先把hello.il文件放在C:\home\pcbenv\skill\函數(shù)。下面,下一步來加載這個(gè)文件,然后運(yùn)行定義的函數(shù) Hello.il的源代碼: procedure( hello() let( () println('Hello World!') ) ); end procedure 1.1.4 注釋 SKILL支持2種注釋方式 a. 用于注釋多行,像C一樣 b. ; 用于注釋單行,類似 C 的// 1.1.5 基本的數(shù)據(jù)類型 integer 5 float 5.0 string 'abc defg' list '(1 2 3 'a') boolen t/nil 1.1.6 變量 SKILL的變量定義比較隨便,不需要指定其類型,賦什么類型的值就是什么類型。 示例:變量名可以包含字母、數(shù)字、下劃線以及問號,數(shù)字和問號不可以作為變量的第一個(gè)字符。不過為了規(guī)范編程和方便閱讀,對于不同類型的數(shù)據(jù)的變量都使用簡寫的方式來代表,比如定義一個(gè)浮點(diǎn)數(shù)的變量,那么推薦使用f 作為前綴。如fPrice,lCoordinate,sMessage,iAge/nAge,bCheck... f作為前綴。如fPrice,lCoordinate,sMessage,iAge/nAge,bCheck... 1.1.7 操作符 數(shù)學(xué)運(yùn)算 +, -, *, /, ++, ** 比較運(yùn)算 >, =, <=, == 邏輯運(yùn)算 ||, &&, ! 賦值 = 1.1.8 輸出數(shù)據(jù) 示例: %d --- integer,%f --- float,%s --- string,%L---多種數(shù)據(jù)格式 1.1.9 錯(cuò)誤信息 像調(diào)試其它程序一樣,調(diào)試SKILL 也需要仔細(xì)看清楚出錯(cuò)時(shí)的錯(cuò)誤提示信息。 比如: 這個(gè)錯(cuò)誤是說load命令沒有定義,這個(gè)是新手長會碰到的一個(gè)問題,就是在Allegro 的cmd 接調(diào)試窗口直SKILL 函數(shù),而實(shí)際上這是不行的,SKILL命令需要運(yùn)行在SKILL 環(huán)境下,簡單點(diǎn)就是在cmd口下輸入窗skill回車,然后再load,如下所示: 這個(gè)錯(cuò)誤是說類型不匹配,查閱SKILL Language Reference(sklangref.pdf) 會發(fā)現(xiàn)函數(shù)strcat 用操作字符串類型的數(shù)據(jù)。只適用操作字符串類型的數(shù)據(jù)。 1.1.10 總結(jié) 到目前為止,你應(yīng)該對SKILL有一點(diǎn)基本的認(rèn)識了。那么你會: 打開Allegro的SKILL調(diào)試窗口了嗎? 知道有幾種基本的數(shù)據(jù)類型嗎? 知道怎么輸出各種類型的數(shù)據(jù)嗎? 知道怎么定義變量嗎?你的變量容易被人閱讀/識別嗎? 找?guī)讉€(gè)SKILL Language Reference里面的函數(shù)在調(diào)試窗口里面運(yùn)行嗎? 函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,知道問題出在哪里并會解決嗎? 1.2 List數(shù)據(jù)類型 List是Cadence SKILL中常用的數(shù)據(jù)類型。可以把List可以有多種類型的常量組成的列表。理解為一個(gè)數(shù)據(jù)結(jié)構(gòu)表,它可以是空的,也可以有多種類型的常量組成的列表例如: '( 1 2 a b c 'PCB' ) 在上面這個(gè)List中包含的數(shù)據(jù)有整數(shù)、字符、字符串。在List中也可以包含List類型的數(shù)據(jù)。 例如: '( 1 ( 2 a a ) b ) 1.2.1 創(chuàng)建List 數(shù)據(jù) 創(chuàng)建新的List數(shù)據(jù)可以用單引號 ' 或者函數(shù)list來新建一個(gè)List數(shù)據(jù)類型的變量。例如: aList = '( 1 2 a b c) => (1 2 a b c) bList = list( 1 2 'a 'b 'd) => (1 2 a b d) cList = '( 1 ( 2 a a ) b ) => (1 (2 a a) b) 注意:當(dāng)有字符出現(xiàn)的時(shí)候,用list 函數(shù)創(chuàng)建必須在字符前加單引號。 1.2.2 List 在內(nèi)存中的儲存方式 我們可以把List看做是一個(gè)List單元,其中一個(gè)List單元占用兩個(gè)內(nèi)存位置。這樣便于理解。 第一個(gè)位置保存List的首個(gè)數(shù)據(jù)元素,第二個(gè)位置保存后面的List單元(除第一個(gè)元素之外的所有元素組成的表),它可以為空數(shù)據(jù)。 用car函數(shù)可以得到保存在第一個(gè)位置的List元素:car( aList) => 2 用cdr函數(shù)可以得到保存在第二個(gè)位置的List單元: cdr( aList) => (3 4) List 中可以包含有子List,例如: bList = '( 1 ( 2 3 4 ) 5 ) => (1 (2 3 4) 5) bList在內(nèi)存中的儲存方式如下圖所示: List中可以同時(shí)包含List 和字符,例如: bList = '( 1 ( 2 a a ) b ) => (1 (2 a a) b) bList在內(nèi)存中的儲存方式如下圖所示: 1.2.3 讀取List 元素 1) 讀取List 中的第一個(gè)元素 使用car函數(shù)可以讀取List中的第一個(gè)元素。例: car( '(a b c) ) => a z = '(1 2 3) => (1 2 3) y = car(z) => 1 y => 1 z => (1 2 3) car(nil) => nil 2) 讀取List 中后面的List 單元 使用cdr函數(shù)可以讀取List中后面的List 單元。 例: cdr( '(a b c) ) => (b c) z = '(1 2 3) cdr(z) => (2 3) 3) 對List重復(fù)混合使用car 或者cdr 函數(shù)讀取 car 和cdr 函數(shù)混合使用,組合的格式為:ca|d[ a|d ][ a|d ][ a|d ]r,以c 開頭,r 中間可以由多個(gè)結(jié)尾, a 或d 組成函數(shù)。例如:caadr功能等同于car( car( cdr( l_list))),caadr 對函數(shù)List的操作順序?yàn)椋?/p> 1. 先執(zhí)行一次cdr,讀取后面的List單元。 2. 對第1步讀取的值,執(zhí)行car,讀取其第一元素。 3. 對第2步讀取的值,執(zhí)行car,讀取其第一元素。 實(shí)例: caaar('(((1 2 3)(4 5 6))(7 8 9))) => 1 等同于car( car( car( l_list))) caadr('(((1 2 3)(4 5 6))(7 8 9))) => 7 等同于car( car( cdr( l_list))) caar('(((1 2 3)(4 5 6))(7 8 9))) => (1 2 3) 等同于car( car( l_list)) cadr('(1 2 3)) => 2 等同于car( cdr( l_list)) 4) 按元素序號讀取List 中的元素 用nthelem 并輸入所有讀取的元素序號,即可讀取該編號位置的元素。如nthelem(1 l_list)同于等car(l_list)。實(shí)例: nthelem( 1 '( a b c ) ) => a z = '( 1 2 3 ) nthelem( 2 z ) => 2 類似的函數(shù)還有nthcdr。實(shí)例: nthcdr( 3 '( a b c d )) => (d) z = '( 1 2 3 ) nthcdr( 2 z ) => ( 3 ) 5) 讀取List 中最后一個(gè)List 單元 用last函數(shù)可以讀取List 中最后一個(gè)元素單元,其數(shù)據(jù)類型也是一個(gè)List。實(shí)例: last( '(a b c) ) => (c) z = '( 1 2 3 ) last( z ) => (3) 1.2.4 修改List 單元 1) rplaca 函數(shù) 用rplaca函數(shù)可以替換List中的第一個(gè)元素。 aList = '( 1 2 3) => ( 1 2 3 ) bList = rplaca( aList 4 ) => ( 4 2 3 ) aList => ( 4 2 3 ) eq( aList bList ) => t 2) rplacd函數(shù)用rplacd函數(shù)可以替換List中后面的List單元。 aList = '( 1 2 3 ) => ( 1 2 3 ) bList = rplacd( aList '( 4 5 ) ) => ( 1 4 5 ) aList => ( 1 4 5 ) eq( aList bList ) => t 1.2.5 添加List 元素和合并List 1) 在List 前添加元素(cons,xcons) 用cons函數(shù)可以添加元素到List前。 aList = '( 2 3 4 ) aList = cons( 1 aList ) => (1 2 3 4 ) xcons 函數(shù)和cons 函數(shù)的功能一樣,但格式有所區(qū)別,前面的變量為List素。,后面的變量為添加的元 xcons( '( b c ) 'a ) => ( a b c ) 2)在List 后添加元素(append1) 用append1函數(shù)可以添加元素到List后。 append1('(1 2 3) 4) => (1 2 3 4) 3) 合并List(ncons,append) cList = '( 1 2 ) dList = '( 3 4 5 ) eList = '( 6 7 ) append( cList dList ) => ( 1 2 3 4 5 ) cList => ( 1 2 ) dList => ( 3 4 5 ) append函數(shù)只能合并兩個(gè)List,并且不改變所合并List變量的值。 nconc( cList dList eList ) => ( 1 2 3 4 5 6 7 ) cList => ( 1 2 3 4 5 6 7 ) dList => ( 3 4 5 6 7 ) eList => ( 6 7 ) nconc函數(shù)可以合并多個(gè)List,但會改變所合并List 變量的值。 1.2.6 List元素排序 1) 倒序排列(reverse) aList = '( 1 2 3 ) aList = reverse( aList ) => ( 3 2 1 ) anotherList = '( 1 2 ( 3 4 5 ) 6 ) reverse( anotherList ) => ( 6 ( 3 4 5 ) 2 1 ) anotherList => ( 1 2 ( 3 4 5 ) 6 ) 2) 按條件排序(sort) sort的書寫格式為:sort(l_data u_comparefn),其中第一個(gè)變量l_data為List量變量,第二個(gè)變u_comparefn為對比函數(shù)。對比函數(shù)也可以是自定義函數(shù)。 sort( '(4 3 2 1) 'lessp ) => (1 2 3 4) sort( '(d b c a) 'alphalessp) => (a b c d) sort('('U5' 'U10' 'U1' 'U5' 'U2') 'axlStrcmpAlpNum) => ('U1' 'U2' 'U5' 'U5' 'U10') 當(dāng)List元素都是由List組成的時(shí)候,還可以使用sortcar函數(shù)對比子List的第一個(gè)元素排序。 sortcar( '((4 four) (3 three) (2 two)) 'lessp ) => ((2 two) (3 three) (4 four) sortcar( '((d 4) (b 2) (c 3) (a 1)) nil ) => ((a 1) (b 2) (c 3) (d 4)) 1.2.7 查找List 元素 1) member 函數(shù) member 函數(shù)從List 到返回第一個(gè)元素查找到最后,如果找到返回找到的元素開始直到最后的元素,找不返回nil。 member( 3 '( 2 3 4 3 5 )) => (3 4 3 5) member( 6 '( 2 3 4 3 5 )) => nil 2) assoc 函數(shù) assoc函數(shù)的書寫格式為:assoc(g_key l_alist),g_key變量為所查找的關(guān)鍵值,l_alist是為一個(gè)由多個(gè)List組成的List,格式為:((key1 value1) (key2 value2) (key3 alue3) ...)。 assco返回查找到的子List。 aList = '(( 1 'one' )( 2 'two' )( 3 'three' )) assoc( 2 aList ) => ( 2 'two' ) assoc( 5 aList ) => nil 1.2.8 過濾List 元素 1) 按條件過濾List 元素(setof) setof函數(shù)的書寫格式為:setof( s_formalVar l_valueList g_predicateExpression )其中, s_formalVar 變量為局部變量,作用于 g_predicateExpression 表達(dá)式中;l_valueList 變量為要過濾的List變量;g_predicateExpression變量為自定義表達(dá)式。setof函數(shù)會把l_valueList歷賦值給變量中的所有元素,遍s_formalVar局部變量,帶入到g_predicateExpression表達(dá)式中,如果表達(dá)式返回的值為nil輸出的新,在List中會將其元素過濾。 setof( x '(1 2 3 4) (x > 2) ) => (3 4) setof( x '(1 2 3 4) (x < 3) ) => (1 2) setof( x '( 1 2 3 4 5 6 ) oddp(x)) => ( 1 3 5 ) 2) exists 函數(shù) exists 函數(shù)和setof 函數(shù)區(qū)別在于exists 函數(shù)查找List 余元素組成的新中滿足條件的第一個(gè)元素,并返回其元素和其List。 exists( x '(1 2 3 4) (x > 2) ) => (3 4) exists( x '(4 3 4 5) (x < 4) ) => (3 4 5) exists( x '(1 2 3 4) (x > 4) ) => nil 3) forall 函數(shù) forall函數(shù)判斷List中所有的元素是否全部滿足表達(dá)式,全部滿足返回 t,不是就返回nil。 forall( x '(1 2 3 4) (x > 0) )=> t forall( x '(1 2 3 4) (x < 4) )=> nil forall( x '( 2 4 6 8 ) evenp( x ) ) => t forall( x '( 2 4 7 8 ) evenp( x ) ) => nil 1.2.9 移除List 元素 1) remove 函數(shù) remove函數(shù)可以移除List 中所指定的元素,如果List 中沒有所指定的元素,那么返回原 List。remove 函數(shù)不會改變原List變量的值。 aList = '( 1 2 3 4 5 ) remove( 3 aList ) => ( 1 2 4 5 ) aList => ( 1 2 3 4 5 ) remove( '( 1 2 ) '( 1 ( 1 2 ) 3 )) => ( 1 3 ) 1.2.10 遍歷List 元素 1) foreach 函數(shù) foreach( x '(1 2 3 4) println(x)) 1 2 3 4 => (1 2 3 4) foreach( (x y) '(1 2 3) '(4 5 6) (println x y)) 5 7 9 => (1 2 3) 2) mapc函數(shù) mapc( 'list '(1 2 3) '(9 8 7) ) => (1 2 3) mapc( '(lambda (x y) (print (list x y))) '(1 2 3) '(9 8 7) ) (1 9) (2 8) (3 7) => (1 2 3) 3) map 函數(shù) map( 'list '(1 2 3) '(9 8 7) ) => (1 2 3) map( '(lambda (x y) (print (append x y))) '(1 2 3) '(9 8 7) ) (1 2 3 9 8 7) (2 3 8 7) (3 7) => (1 2 3) 4) mapcar 函數(shù) mapcar( 'plus '(1 2 3) '(9 8 7) ) => (10 10 10) mapcar( 'list '(a b c) '(1 2 3) '(x y z) ) => ((a 1 x) (b 2 y) (c 3 z)) mapcar( 'lambda( (x) plus( x 1 )) '(2 4 6) ) => (3 5 7) 5) maplist 函數(shù) maplist( 'length '(1 2 3) ) => (3 2 1) maplist( 'list '(a b c) '(1 2 3) ) => (((a b c)(1 2 3))((b c)(2 3))((c)(3))) 6) mapcan 函數(shù) mapcan( 'list '(1 2 3) '(a b c) ) => (1 a 2 b 3 c) mapcan( (lambda (n) (and (plusp n) (list n))) '(1 -2 3 -4 5)) => (1 3 5) 1.2.11 遍歷List 元素實(shí)例 1) 得到List 中字符串的長度 假設(shè),有一個(gè)由字符串元素組成的List變量stringList: 下面寫一段代碼,它的功能是輸出stringList中每個(gè)字符串元素的字符長度: 如果在沒有熟悉mapcar函數(shù)的情況下,可能會使用以下代碼: 使用mapcar函數(shù)的代碼:其實(shí)mapcar函數(shù)就已經(jīng)包含遍歷List的功能,可以簡化代碼如下: 2) 將List 中的所有元素(包括子List 中的元素)重新組合成新List 假設(shè),有一個(gè)由List組成的List變量x: 下面寫一個(gè)函數(shù)flatten,它的功能是將List中的所有元素重新組合成新List,如下: 下面用mapcan函數(shù),代碼如下: 但上面的代碼有一個(gè)問題,它改變了x變量的值,運(yùn)行flatten( x )后,x的值如下: 可以使用copy函數(shù)復(fù)制List副本來避免改變原list值,代碼: 上面的代碼運(yùn)行結(jié)果如下: 3)將List中的所有元素(多重List)重新組合成新List 假設(shè),有一個(gè)由List和多重List組成的List變量x:下面寫一個(gè)函數(shù)flatten,它的功能是將List中的所有元素重新組合成新List,如下:可以重復(fù)調(diào)用自身函數(shù)來解決多重List讀取的難題,代碼如下: 1.3流程控制 注:下面的一些示例包含多行語句,但是allegro的skill調(diào)試窗口不支持多行的調(diào)試,所以多于多行的情況,要么把所有的代碼重新編輯為一行,或者將代碼放到一個(gè)文件里面,采用第一章里面說的load 的辦法來調(diào)試。 1.3.1 邏輯值(t, nil) 在第一節(jié)說到關(guān)系操作和邏輯操作,結(jié)合這些操作和條件選擇及循環(huán)控制命令可以選擇程序運(yùn)行。關(guān)系操作和邏輯操作的結(jié)果是真值或假值,前面說到 skill 的 boolean 型數(shù)據(jù)有 2 種,一種是 t 表示真值,一種是nil表示假值,其實(shí)skill中把一切非nil的結(jié)果都當(dāng)作真值,而不是僅僅局限于t。 1.3.2 分支控制 1.3.2.1 單分支命令(when, unless) when( bCondition ; bCondition 為邏輯表達(dá)式,nil或其它(真值) expressions ; bCondition 為真的時(shí)候執(zhí)行when 里面的命令 ) 示例 nCount = 5 when ( nCount>=5 println(“nCount is no less than 5”) ) =>“nCount is no less than 5” unless( bCondition expressions ; bCondition為假(nil)的時(shí)候執(zhí)行unless里面的命令 ) 示例 nCount=5 unless( nCount<5 println(“nCount is no less than 5”) ) 注意到when和unless的2個(gè)示例的區(qū)別了嗎?其實(shí)when==(!unless),也就是說when在condition 為 真的時(shí)候執(zhí)行內(nèi)部的表達(dá)式,而unless是在condition 為假的時(shí)候執(zhí)行表達(dá)式。unless算是when的一 個(gè)補(bǔ)充,判斷nil的情況比用wehn要簡潔。 1.3.2.2 雙分支命令(if) if( bCondition then exp1 ;bCondition 為真的時(shí)候執(zhí)行 else if( bCondition then exp1 ;bCondition為真的時(shí)候執(zhí)行 else exp2 ;bCondition 為假的時(shí)候執(zhí)行 ) 示例 nAge=17 if( nAge<18 then print(“E- not 18 years old, can not watch this movie!\n”) else print(“I- enjoy the movie!\n”) ) =>“E- not 18 years old, can not watch this movie!” 如果用when來寫的話,就要寫2個(gè)when 語句。 when(nAge<18 print(“E- not 18 years old, can not watch this movie!\n”) ) when( nAge>=18 print(“I- enjoy the movie!\n”) 1.3.2.3 多分支命令(cond,case) sSymbol = “test” cond( ( !sSymbol println(“it is nil”)) ( numberp(sSymbol) println(“it is a number”)) ( stringp(sSymbol) println(“it is a number”)) ( t println(“I do not care the type”) ); end cond => it is a string。 cond 命令里面有多個(gè)程序塊,程序會逐個(gè)判斷里面各個(gè)塊里面的程序,然后退出塊的條件,直到找到一個(gè)邏輯為真的塊,執(zhí)行cond。cond就像是 if then else的多次疊加。 case(type(sSymbol) ( “fixnum” println(“it is a number”) ) (“floatnum” println(“it is a float num”) ) (“string” println(“it is a string”) ) );end case => it is a string case 命令里面也有多個(gè)程序塊,程序會判斷case 條件的程序塊。后面的那個(gè)表達(dá)式滿足其中的哪個(gè)條件,只執(zhí)行滿足 case和cond的區(qū)別在于cond中的判斷表達(dá)式可以不唯一,而 case只判斷一個(gè)表達(dá)式。比如: cond( ( numberp(“a”) println(“a number”)) ( 5>=3 println( “it is true”)) ( t println(“nothing”)) ); end cond => “it is true” 這里有3個(gè)條件表達(dá)式,第一個(gè)是 numberp(“a”),如果是真值就執(zhí)行后面的println命令;第二個(gè)5>=3 如果是真值就執(zhí)行后面的, println命令;第三個(gè)是t,如果之前的表達(dá)式?jīng)]有真值的,就會執(zhí)行這部分的println語句。 1.3.3 循環(huán)控制(while,for,foreach) while( bCondition expressions ; bCondition 非nil的時(shí)候一直重復(fù)執(zhí)行while里面的命令行 ) 示例: nCount=10 while( nCount>0 printf( “it is %d now\n” nCount ) nCount-- ) =>it is 10 now … It is 1 now while命令和C 語言的很相似。 for( nStep 1 10 printf( “it is %d now\n” nStep ) ) => it is 1 now … It is 10 now For命令的索引只可以遞增,不可以像C語言中那樣支持遞減。 foreach( item alist ; alist 是個(gè)list expr ; alist里面有多少個(gè)元素,就執(zhí)行多少遍 ) foreach( item '(“a” “b” “c”) println(item) ) =>“a” “b” “c” 1.3.4返回命令(return) 用prog和return命令組合可以實(shí)現(xiàn)從prog程序塊中跳出。 prog( (local variable) expressions… ) prog 的一個(gè)作用是定義局部變量,還有一個(gè)函數(shù)有同樣的定義局部變量的功能let,語法格式也一樣。不同的是prog支持return 直接跳出,而let不支持。單單使用局部變量的功能,那么let的效率比prog 高。比如說從一個(gè)for循環(huán)里面跳出,采用下面的方法是不行的,skill不支持。 for(nNum 1 20 when( nNum > 10 return( nNum ) ) ) 必須要在for之前加上一個(gè)prog,如下: prog( () for(nNum 1 20 when( nNum > 10 return( nNum ) ) ) ) =>11 如果要獲得這個(gè)返回值,可以將上面這段代碼賦值給一個(gè)變量,比如: nCount = prog( () … ) nCount=>11 1.3.5 總結(jié) 本章主要介紹了流程控制的命令,學(xué)習(xí)了以后請嘗試使用每一個(gè)流程控制命令各寫一段代碼。 when,unless,if,case,cond,while,for,foreach,prog,return。 1.4 函數(shù) 1.4.1 程序塊 把多行skill語句放到一個(gè){}中形成一個(gè)程序塊,這樣的一個(gè)程序塊就像單行skill程序一樣,可以賦值。這個(gè)功能和前一節(jié)說的prog,let類似。比如:求一個(gè)線段的長度,線段兩頭有2個(gè)坐標(biāo)(x, y) lStardEnd = list(0:0 50:50) fLength = { lStartPoint = car( lStartEnd ) ; list 操作,得到(0 0) lEndPoint = cadr( lStartEnd ) ;得到原 list的第二個(gè)坐標(biāo)(50 50) sqrt((xCoord(lStartPoint)-xCoord(lEndPoint))**2 (yCoord(lStartPoint)-yCoord(lEndPoint))**2) ; xCoord, yCoord 分別得到坐標(biāo)的x,y; ; ** 是冪運(yùn)算 ; sqrt是求平方根 } 這個(gè)用let來寫就是這樣: lStardEnd = list(0:0 50:50) fLength = let( ( lStartPoint lEndPoint ) ; 定義為局部變量 lStartPoint = car( lStartEnd ) lEndPoint = cadr( lStartEnd ) sqrt((xCoord(lStartPoint)-xCoord(lEndPoint))**2 (yCoord(lStartPoint)-yCoord(lEndPoint))**2) ) 在{}和let里面的都叫做程序塊,但是更推薦用let的方式,使用局部變量。用不用{}方式來寫,是個(gè)人的習(xí)慣,有個(gè){}可能模塊看起來更明顯,便于閱讀。不用{}的寫法: lStardEnd = list(0:0 50:50) lStartPoint = car( lStartEnd ) lEndPoint = cadr( lStartEnd ) fLength =sqrt((xCoord(lStartPoint)-xCoord(lEndPoint))**2 (yCoord(lStartPoint)-yCoord(lEndPoint))**2) 1.4.2 函數(shù)定義 定義函數(shù)的最常用的關(guān)鍵字是procedure(不推薦defun),一般的函數(shù)格式為: procedure( func_name(input_para) let( (local variable) ;如果需要支持多種返回值的情況,也就是需要return命令,請使用prog。 skill expressions ); end let ) 上面的那個(gè)計(jì)算線段程度的程序塊加上一個(gè)procedure語句結(jié)構(gòu)就可以成為一個(gè)函數(shù)了,比如 procedure( CalcLength( lStartEnd ) ; 定義函數(shù)名為CalcLength,輸入?yún)?shù)為StartEnd let( ( lStartPoint lEndPoint ) ; 定義2個(gè)局部變量 lStartPoint = car( lStartEnd ) lEndPoint = cadr( lStartEnd ) sqrt((xCoord(lStartPoint)-xCoord(lEndPoint))**2 (yCoord(lStartPoint)-yCoord(lEndPoint))**2) ; let程序塊的返回值為程序塊最后一行執(zhí)行的結(jié)果 ); end let ); end procedure CalcLength lBox = list(0:0 50:50) ; 定義一個(gè)能被CalcLength使用的變量 fLength = CalcLength(lBox) ; 調(diào)用函數(shù)CalcLength,輸入?yún)?shù)是lBox,返回結(jié)果賦值給fLength 1.4.3 局部變量和全局變量 每種編程語言都會說到這2種變量類型,簡單點(diǎn)來說,局部程外好相面序執(zhí)反的行,程的所序過有是程的不知中程道就序有會都產(chǎn)這可生樣以錯(cuò)的使誤變用。量這所存?zhèn)€以在變最的量安,,全如所果的以做不不法同會是的有盡程變量序量變重使定量名用義只導(dǎo)局了在相致部其同程變定名序量義,出字的盡但錯(cuò)函量是的數(shù)少功問或用能題程不;全序而同局塊全的變里局全量面。變局起量變作則量用,正前面提到了prog和let函數(shù)結(jié)構(gòu)本身包含了定義局部變量的部分。另外for,foreach是局部變量,作用域在里面用到的自變量也for和foreach 范圍之內(nèi)。 foreach( item '( 1 2 3) ; 這里的item是局部變量, … ) ; ; item在這個(gè)括號之前有效 println(item) ; 會提示變量不存在的錯(cuò)誤。 1.4.4 函數(shù)調(diào)用與返回值 根據(jù)函數(shù)定義的格式來調(diào)用函數(shù),比如函數(shù)strcat,它的函數(shù)格式為 strcat( S_string1 [ S_string2 ... ] ) => t_result 輸入?yún)?shù)是字符串類型,需要至少一個(gè)字符串作為輸入,所以調(diào)用這個(gè)函數(shù)就strcat(“ab” “cde”) =>”abcde”。獲取返回值其實(shí)就是把返回值賦值給新的變量,比如上面的例子,可以把strcat 賦值給另一個(gè)變量。函數(shù)返回的新的字符串sNew = strcat(“ab” “cde”); sNew=>”abcde”。 1.4.5 總結(jié) 定義一個(gè)找出字符串list中最長的字符串并返回該字符串及字符串的長度的函數(shù) 1.5 文件操作 1.5.1文件操作 Skill 的文件操作支持對簡單文件的寫入和讀出,基本步驟為打開一個(gè)文件 I/O 端口,然后用相應(yīng)的命令讀數(shù)據(jù)或?qū)憯?shù)據(jù)。infile命令用于產(chǎn)生讀取文件的端口,然后用gets或者fscanf操作讀?。籵utfile命令用于創(chuàng)建打開文件寫入的端口,然后用fprintf輸出數(shù)據(jù)到該端口。數(shù)據(jù)操作完畢,最后一步就是關(guān)閉打開的I/O 端口。 1.5.2讀文件(infile, gets, fscanf) pFileIn = infile(filename) => port:filename 這里的 pFileIn 就是個(gè)讀文件的端口(port), gets(sLine pFileIn) => 從 pFileIn端口讀入一行,也就是從 filename 文件里面讀一行,包括換行符,讀到的值存儲在 sLine 里面。如果讀到了文件的末尾,沒有數(shù)據(jù)了,那么返回值為 nil。讀取完畢以后要關(guān)閉這個(gè)打開的端口,close(pFileIn)。 從文件端口讀數(shù)據(jù)的命令除了 gets 還有 fscanf,區(qū)別在于 gets 整行讀取,讀入的數(shù)據(jù)被當(dāng)作字符串處理,而fscanf 則需要根據(jù)文件本身的格式來讀取,讀入的數(shù)據(jù)可以有數(shù)字,字符串等等。 fscanf( pFileIn “%s” sWord ) 從 pFileIn端口讀入一個(gè)字符串,如果一行中數(shù)據(jù)為 abc def ght,那么 sWord=”abc”。 1.5.3寫入文件(outfile, fprintf) pFileOut = outfile(filename) =>創(chuàng)建一個(gè)輸出文件的端口; fprintf( pFileOut “%d %s\n” 15 “abc” ) => 輸出數(shù)據(jù)到文件 filename; close(pFileOut) => 關(guān)閉該文件端口 現(xiàn)在打開文件的話,就會看到文件中有一行數(shù)據(jù) 15 abc。 其它 print 命令也支持輸出數(shù)據(jù)到文件,但是為了程序的清晰起見,還是只推薦使用 fprintf命令輸出數(shù)據(jù)到文件。 1.5.4總結(jié) Allegro 用戶的report命令可以產(chǎn)生很多種的log文件,請?zhí)粢环N嘗試讀取文件里面的信息,然后處理這些信息,并將處理后的信息輸出到另一個(gè)文件中。 |
|