版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
./ProgramminginLuaLua程序設計作者:RobertoIerusalimschy翻譯:制作:中國lua開發(fā)者相關(guān)鏈接:lua中文在線手冊譯序"袁承志知道若再謙遜,那就是瞧人不起,展開五行拳,發(fā)拳當胸打去。榮彩和旁觀三人本來都以為他武功有獨到之秘,哪知使出來的竟是武林中最尋常不過的五行拳。敵對三人登時意存輕視,溫青臉上不自禁露出失望的神色。"榮彩心中暗喜,雙拳如風,連搶三下攻勢,滿擬自己的大力魔爪手江南獨步,三四招之間就可破去對方五行拳,那知袁承志輕描淡寫的一一化解。再拆數(shù)招,榮彩暗暗吃驚,原來對方所使雖是極尋常的拳術(shù),但每一招均是含勁不吐,意在拳先,舉手抬足之間隱含極渾厚的內(nèi)力。"——金庸《碧血劍》編程語言之于程序員,若武功招式之于習武之人,招式雖重要,卻更在于使用之人。勝者之道,武功只行于表,高手用劍,片草只葉亦威力無窮。當今武林,派別林立,語言繁雜,林林總總不計其數(shù)。主流文化的C/C++、Java、C#、VB\o""[1];偏安一隅的Fortran;動態(tài)語言中的Perl、Tcl、Ruby、Forth、Python,以及本書介紹的Lua;……,等等等等。再加上世界上那些不知道躲在哪的旮旯的奇奇怪怪的hacker搗鼓出來的異想天開的語言,要想各類語言樣樣精通,不異于癡人說夢。不信可欣賞一下BrainFuck語言\o""[2]的HelloWorld程序,語言本身依如其名。-->+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.#>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++...[-]>++++++++[<++++>-]<+.[-]++++++++++.雖說語言的威力依使用者本身的修為高低而定,但不同語言本身的設計又有不同。若讓用Java寫寫操作系統(tǒng)內(nèi)核、Perl寫寫驅(qū)動程序、C/C++寫寫web應用,都無異于舍近求遠,好刀只用上了刀背。Lua本身是以簡單優(yōu)雅為本,著眼于處理那些C不擅長的任務。借助C/C++為其擴展,Lua可閃現(xiàn)無窮魅力。Lua本身完全遵循ANSIC而寫成,只要有C編譯器的地方,Lua便可發(fā)揮她的力量。Lua不需要追求Python那樣的大而全的庫,太多累贅,反而破壞了她的優(yōu)美。語言的優(yōu)美,來自于使用者自己的感悟。Lua的優(yōu)雅,也只有使用后才會明白。揚起帆,讓我們一同踏上Lua的學習之旅……本書的翻譯,是中朋友們共同努力的結(jié)果。下面是參與翻譯與校對的朋友:--file:'thanks.lua'--desc:toprintthelistofthecontributingguyshelpful_guys={"參與翻譯","buxiu","鳳舞影天","zhang3","morler","lambda","sunlight","\n","參與校對","鳳舞影天","doyle","flicker","花生魔人","zhang3","kasicass","\n"}for_,einipairs<helpful_guys>doprint<e>end翻譯組2005第一篇語言第0章序言本章包括作者的序言、文章的體例〔convention以及其它一些"每本書開頭都會的內(nèi)容"。
0.1序言目前很多程序語言都專注于幫你編寫成千上萬行的代碼,所以此類型的語言所提供的包、命名空間、復雜的類型系統(tǒng)及無數(shù)的結(jié)構(gòu),有上千頁的文檔需要操作者學習。而Lua并不幫你編寫大量的代碼的程序,相反的,Lua僅讓你用少量的代碼解決關(guān)鍵問題。為實現(xiàn)這個目標,像其他語言一樣Lua依賴于其可擴展性。但是與其他語言不同的是,不僅用Lua編寫的軟件易于擴展,而且用其他語言比如C/C++編寫的軟件也很容易使用Lua擴展其功能。一開始,Lua就被設計成很容易和傳統(tǒng)的C/C++整合的語言。這種語言的二元性帶來了極大的好處。Lua是一個小巧而簡單的語言,因為Lua不致力于做C語言已經(jīng)做得很好的領(lǐng)域,比如:性能、底層操作以及與第三方軟件的接口。Lua依賴于C去做完成這些任務。Lua所提供的機制是C不善于的:高級語言、動態(tài)結(jié)構(gòu)、簡潔、易于測試和調(diào)試等。正因為如此,Lua具有良好的安全保證,自動內(nèi)存管理,簡便的字符串處理功能及其他動態(tài)數(shù)據(jù)的改變。Lua不僅是一種易于擴展的語言,也是一種易整合語言〔gluelanguage;Lua支持基于組件的,我們可以將一些已經(jīng)存在的高級組件整合在一起實現(xiàn)一個應用軟件。一般情況下,組件使用像C/C++等靜態(tài)的語言編寫。但Lua是我們整合各個組件的粘合劑。又通常情況下,組件〔或?qū)ο蟊憩F(xiàn)為具體在程序開發(fā)過程中很少變化的、占用大量CPU時間的決定性的程序,例如窗口部件和數(shù)據(jù)結(jié)構(gòu)。對那種在產(chǎn)品的生命周期內(nèi)變化比較多的應用方向使用Lua可以更方便的適應變化。除了作為整合語言外,Lua自身也是一個功能強大的語言。Lua不僅可以整合組件,還可以編輯組件甚至完全使用Lua創(chuàng)建組件。除了Lua外,還有很多類似的腳本語言,例如:Perl、Tcl、Ruby、Forth、Python。雖然其他語言在某些方面與Lua有著共同的特色,但下面這些特征是Lua特有的:①可擴展性。Lua的擴展性非常卓越,以至于很多人把Lua用作搭建領(lǐng)域語言的工具〔注:比如游戲腳本。Lua被設計為易于擴展的,可以通過Lua代碼或者C代碼擴展,Lua的很多功能都是通過外部庫來擴展的。Lua很容易與C/C++、java、fortran、Smalltalk、Ada,以及其他語言接口。②簡單。Lua本身簡單,小巧;內(nèi)容少但功能強大,這使得Lua易于學習,很容易實現(xiàn)一些小的應用。他的完全發(fā)布版〔代碼、手冊以及某些平臺的二進制文件僅用一張軟盤就可以裝得下。③高效率。Lua有很高的執(zhí)行效率,統(tǒng)計表明Lua是目前平均效率最高的腳本語言。④與平臺無關(guān)。Lua幾乎可以運行在所有我們聽說過的系統(tǒng)上,如NextStep、OS/2、PlayStationII<Sony>、MacOS-9、OSX、BeOS、MS-DOS、IBMmainframes、EPOC、PalmOS、MCF5206eLITEEvaluationBoard、RISCOS,及所有的Windows和Unix。Lua不是通過使用條件編譯實現(xiàn)平臺無關(guān),而是完全使用ANSI<ISO>C,這意味著只要你有ANSIC編譯器你就可以編譯并使用Lua。Lua大部分強大的功能來自于他的類庫,這并非偶然。Lua的長處之一就是可以通過新類型和函數(shù)來擴展其功能。動態(tài)類型檢查最大限度允許多態(tài)出現(xiàn),并自動簡化調(diào)用內(nèi)存管理的接口,因為這樣不需要關(guān)心誰來分配內(nèi)存誰來釋放內(nèi)存,也不必擔心數(shù)據(jù)溢出。高級函數(shù)和匿名函數(shù)均可以接受高級參數(shù),使函數(shù)更為通用。Lua自帶一個小規(guī)模的類庫。在受限系統(tǒng)中使用Lua,如嵌入式系統(tǒng),我們可以有選擇地安裝這些類庫。若運行環(huán)境十分嚴格,我們甚至可以直接修改類庫源代碼,僅保留需要的函數(shù)。記住:Lua是很小的〔即使加上全部的標準庫并且在大部分系統(tǒng)下你仍可以不用擔心的使用全部的功能。0.2Lua的使用者Lua使用者分為三大類:使用Lua嵌入到其他應用中的、獨立使用Lua的、將Lua和C混合使用的。第一:很多人使用Lua嵌入在應用程序,比如CGILua〔搭建動態(tài)網(wǎng)頁、LuaOrb〔訪問CORBA對象。這些類型用Lua-API注冊新函數(shù),創(chuàng)建新類型,通過配置Lua就可以改變應用宿主語言的行為。通常,這種應用的使用者并不知道Lua是一種獨立的語言。例如:CGILua用戶一般會認為Lua是一種用于Web的語言。第二:作為一種獨立運行的語言,Lua也是很有用的,主要用于文本處理或者只運行一次的小程序。這種應用Lua主要使用它的標準庫來實現(xiàn),標準庫提供模式匹配和其它一些字串處理的功能。我們可以這樣認為:Lua是文本處理領(lǐng)域的嵌入式語言。第三:還有一些使用者使用其他語言開發(fā),把Lua當作庫使用。這些人大多使用C語言開發(fā),但使用Lua建立簡單靈活易于使用的接口。本書面向以上三類讀者。書的第一部分闡述了語言的本身,展示語言的潛在功能。我們講述了不同的語言結(jié)構(gòu),并用一些例子展示如何解決實際問題。這部分既包括基本的語言的控制結(jié)構(gòu),也包括高級的迭代子和協(xié)同。第二部分重點放在Lua特有的數(shù)據(jù)結(jié)構(gòu)——tables上,討論了數(shù)據(jù)結(jié)構(gòu)、持久性、包及面向?qū)ο缶幊?這里我們將看到Lua的真正強大之處。第三部分介紹標準庫。每個標準庫一章:數(shù)學庫、table庫、string庫、I/O庫、OS庫、Debug庫。最后一部分介紹了Lua和C接口的API,這部分介紹在C語言中開發(fā)應用而不是Lua中,應用對于那些打算將Lua嵌入到C/C++中的讀者可能會對此部分更感興趣。0.3Lua的相關(guān)資源如果你真得想學一門語言,參考手冊是必備的。本書和Lua參考手冊互為補充,手冊僅僅描述語言本身,因此他既不會告訴你語言的數(shù)據(jù)結(jié)構(gòu)也不會舉例說明,但手冊是Lua的權(quán)威性文檔,可以得到手冊的內(nèi)容。--Lua用戶社區(qū),提供了一些第三方包和文檔--本書的更新勘誤表,代碼和例子http://www.inf.puc-rio.br/~roberto/book/另外本書僅針對Lua5.0,如果你的版本不同,請查閱Lua手冊或者比較版本間的差異。0.4本書的體例<1>字符串使用雙引號,比如"literalstrings";單字符使用單引號,比如'a';模式串也是用單引號,比如'[%w_]*'。<2>符號-->表示語句的輸出或者表達式的結(jié)果:print<10>-->1013+3-->16<3>符號<-->表示等價,即對于Lua來說,用this與that沒有區(qū)別。this<-->that0.5關(guān)于本書開始打算寫這本書是1998年冬天<南半球>,那時候Lua版本是3.1;2000年v4.0;2003年v5.0。很明顯的是,這些變化給本書帶來很大的沖擊,有些內(nèi)容失去了它存在理由,比如關(guān)于超值〔upvalues的復雜的解釋。一些章節(jié)被重寫,比如CAPI,另外一些章節(jié)被增加進來,比如協(xié)同處理。不太明顯的是,Lua語言本身的發(fā)展對本書的完成也產(chǎn)生了很大的影響。一些語言的變化在本書中并沒有被涵蓋進來,這并非偶然的。在本書的創(chuàng)作過程中,有的時候在某個章節(jié)我會突然感覺很困惑,因為我不知道該從何開始或者怎樣去講問題闡述清楚。當你想盡力去解釋清楚如何使用的前提是你應該覺得使用這個東西很容易,這表明Lua某些地方需要被改進。還有的時候,我順利的寫完某個章節(jié),結(jié)果卻是沒有人能看得懂我寫的或者沒有人對我在這個章節(jié)內(nèi)表達的觀點達成一致。大部分情況下,這是我的過錯因為我是個作家,偶爾我也會因此發(fā)現(xiàn)語言本身的一些需要改進的缺陷〔舉例來說,從upvalues到lexicalscoping的轉(zhuǎn)變是由無意義的嘗試所帶來的抱怨所引發(fā)的,在此書的先前的草稿里,把upvalues形容成是lexicalscoping的一種。本書的完成必須服從語言的變化,本書在這個時候完成的原因:<1>Lua5.0是一個成熟的版本<2>語言變得越來越大,超出了最初本書的目標。此外一個原因是我迫切的想將Lua介紹給大家讓更多的人了解Lua。
0.6感謝在完成本書的過程中,很多人給了我極大的幫助:LuizHenriquedeFigueiredo和WaldemarCeles給了我很大的幫助,使得本書能夠更好完成,LuizHenrique也幫助設計了本書的內(nèi)部。NoemiRodriguez,AndréCarregal,DiegoNehab,以及GavinWraith閱讀了本書的草稿提出了很多有價值的建議。RenatoCerqueira,CarlosCassino,TomásGuisasola,JoeMyers和EdFerguson也提出了很多重要的建議。AlexandreNakonechnyj負責本書的封面和內(nèi)部設計。RosaneTeles負責CIP數(shù)據(jù)的準備。謝謝他們所有人。第1章起點寫一個最最簡單的程序——HelloWorld。print<"HelloWorld">假定你把上面這句保存在hello.lua文件中,你在命令行只需要:prompt>luahello.lua看到結(jié)果了嗎?讓我們來看一個稍微復雜點的例子:--definesafactorialfunctionfunctionfact<n>ifn==0thenreturn1elsereturnn*fact<n-1>endendprint<"enteranumber:">a=io.read<"*number">--readanumberprint<fact<a>>這個例子定義了一個函數(shù),計算輸入?yún)?shù)n的階乘;本例要求用戶輸入一個數(shù)字n,然后打印n的階乘。1.1ChunksChunk是一系列語句,Lua執(zhí)行的每一塊語句,比如一個文件或者交互模式下的每一行都是一個Chunk。每個語句結(jié)尾的分號〔;是可選的,但如果同一行有多個語句最好用;分開a=1b=a*2--ugly,butvalid一個Chunk可以是一個語句,也可以是一系列語句的組合,還可以是函數(shù),Chunk可以很大,在Lua中幾個MByte的Chunk是很常見的。你還可以以交互模式運行Lua,不帶參數(shù)運行Lua:Lua5.0Copyright?1994-2003Tecgraf,PUC-Rio>你鍵入的每個命令〔比如:"HelloWorld"在你鍵入回車之后立即被執(zhí)行,鍵入文件結(jié)束符可以退出交互模式〔Ctrl-DinUnix,Ctrl-ZinDOS/Windows,或者調(diào)用OS庫的os.exit<>函數(shù)也可以退出。在交互模式下,Lua通常把每一個行當作一個Chunk,但如果Lua一行不是一個完整的Chunk時,他會等待繼續(xù)輸入直到得到一個完整的Chunk.在Lua等待續(xù)行時,顯示不同的提示符〔一般是>>.可以通過指定參數(shù)讓Lua執(zhí)行一系列Chunk。例如:假定一個文件a內(nèi)有單個語句x=1;另一個文件b有語句print<x>prompt>lua-la-lb命令首先在一個Chunk內(nèi)先運行a然后運行b?!沧⒁猓?l選項會調(diào)用require,將會在指定的目錄下搜索文件,如果環(huán)境變量沒有設好,上面的命令可能不能正確運行。我們將在8.1節(jié)詳細更詳細的討論therequirefunction-i選項要求Lua運行指定Chunk后進入交互模式.prompt>lua-i-la-lb將在一個Chunk內(nèi)先運行a然后運行b,最后直接進入交互模式。另一個連接外部Chunk的方式是使用dofile函數(shù),dofile函數(shù)加載文件并執(zhí)行它.假設有一個文件:--file'lib1.lua'functionnorm<x,y>localn2=x^2+y^2returnmath.sqrt<n2>endfunctiontwice<x>return2*xend在交互模式下:>dofile<"lib1.lua">--loadyourlibrary>n=norm<3.4,1.0>>print<twice<n>>-->7.0880180586677-i和dofile在調(diào)試或者測試Lua代碼時是很方便的。1.2全局變量全局變量不需要聲明,給一個變量賦值后即創(chuàng)建了這個全局變量,訪問一個沒有初始化的全局變量也不會出錯,只不過得到的結(jié)果是:nil.print<b>-->nilb=10print<b>-->10如果你想刪除一個全局變量,只需要將變量負值為nilb=nilprint<b>-->nil這樣變量b就好像從沒被使用過一樣.換句話說,當且僅當一個變量不等于nil時,這個變量存在。1.3詞法約定標示符:字母<letter>或者下劃線開頭的字母、下劃線、數(shù)字序列.最好不要使用下劃線加大寫字母的標示符,因為Lua的保留字也是這樣的。Lua中,letter的含義是依賴于本地環(huán)境的。保留字:以下字符為Lua的保留字,不能當作標識符。andbreakdoelseelseifendfalseforfunctionifinlocalnilnotorrepeatreturnthentrueuntilwhile注意:Lua是大小寫敏感的.注釋:單行注釋:--多行注釋:--[[
--]]--[[print<10>--noaction<comment>--]]1.4命令行方式lua[options][script[args]]-e:直接將命令傳入Luaprompt>lua-e"print<math.sin<12>>"-->-0.53657291800043-l:加載一個文件.-i:進入交互模式._PROMPT內(nèi)置變量作為交互模式的提示符prompt>lua-i-e"_PROMPT='lua>'"lua>Lua的運行過程,在運行參數(shù)之前,Lua會查找環(huán)境變量LUA_INIT的值,如果變量存在并且值為@filename,Lua將加載指定文件。如果變量存在但不是以@開頭,Lua假定filename為Lua代碼文件并且運行他。利用這個特性,我們可以通過配置,靈活的設置交互模式的環(huán)境??梢约虞d包,修改提示符和路徑,定義自己的函數(shù),修改或者重命名函數(shù)等。全局變量arg存放Lua的命令行參數(shù)。prompt>luascriptabc在運行以前,Lua使用所有參數(shù)構(gòu)造arg表。腳本名索引為0,腳本的參數(shù)從1開始增加。腳本前面的參數(shù)從-1開始減少。prompt>lua-e"sin=math.sin"scriptabarg表如下:arg[-3]="lua"arg[-2]="-e"arg[-1]="sin=math.sin"arg[0]="script"arg[1]="a"arg[2]="b"第2章類型和值Lua是動態(tài)類型語言,變量不要類型定義。Lua中有8個基本類型分別為:nil、boolean、number、string、userdata、function、thread和table。函數(shù)type可以測試給定變量或者值的類型。print<type<"Helloworld">>-->stringprint<type<10.4*3>>-->numberprint<type<print>>-->functionprint<type<type>>-->functionprint<type<true>>-->booleanprint<type<nil>>-->nilprint<type<type<X>>>-->string變量沒有預定義的類型,每一個變量都可能包含任一種類型的值。print<type<a>>-->nil<'a'isnotinitialized>a=10print<type<a>>-->numbera="astring!!"print<type<a>>-->stringa=print--yes,thisisvalid!a<type<a>>-->function注意上面最后兩行,我們可以使用function像使用其他值一樣使用〔更多的介紹參考第六章。一般情況下同一變量代表不同類型的值會造成混亂,最好不要用,但是特殊情況下可以帶來便利,比如nil。2.1NilLua中特殊的類型,他只有一個值:nil;一個全局變量沒有被賦值以前默認值為nil;給全局變量負nil可以刪除該變量。2.2Booleans兩個取值false和true。但要注意Lua中所有的值都可以作為條件。在控制結(jié)構(gòu)的條件中除了false和nil為假,其他值都為真。所以Lua認為0和空串都是真。2.3Numbers表示實數(shù),Lua中沒有整數(shù)。一般有個錯誤的看法CPU運算浮點數(shù)比整數(shù)慢。事實不是如此,用實數(shù)代替整數(shù)不會有什么誤差〔除非數(shù)字大于100,000,000,000,000。Lua的numbers可以處理任何長整數(shù)不用擔心誤差。你也可以在編譯Lua的時候使用長整型或者單精度浮點型代替numbers,在一些平臺硬件不支持浮點數(shù)的情況下這個特性是非常有用的,具體的情況請參考Lua發(fā)布版所附的詳細說明。和其他語言類似,數(shù)字常量的小數(shù)部分和指數(shù)部分都是可選的,數(shù)字常量的例子:40.44.57e-30.3e125e+202.4Strings指字符的序列。lua是8位字節(jié),所以字符串可以包含任何數(shù)值字符,包括嵌入的0。這意味著你可以存儲任意的二進制數(shù)據(jù)在一個字符串里。Lua中字符串是不可以修改的,你可以創(chuàng)建一個新的變量存放你要的字符串,如下:a="onestring"b=string.gsub<a,"one","another">--changestringpartsprint<a>-->onestringprint<b>-->anotherstringstring和其他對象一樣,Lua自動進行內(nèi)存分配和釋放,一個string可以只包含一個字母也可以包含一本書,Lua可以高效的處理長字符串,1M的string在Lua中是很常見的??梢允褂脝我柣蛘唠p引號表示字符串a(chǎn)="aline"b='anotherline'為了風格統(tǒng)一,最好使用一種,除非兩種引號嵌套情況。對于字符串中含有引號的情況還可以使用轉(zhuǎn)義符\來表示。Lua中的轉(zhuǎn)義序列有:\abell\bbackspace--后退\fformfeed--換頁\nnewline--換行\(zhòng)rcarriagereturn--回車\thorizontaltab--制表\vverticaltab\\backslash--"\"\"doublequote--雙引號\'singlequote--單引號\[leftsquarebracket--左中括號\]rightsquarebracket--右中括號例子:>print<"oneline\nnextline\n\"inquotes\",'inquotes'">onelinenextline"inquotes",'inquotes'>print<'abackslashinsidequotes:\'\\\''>abackslashinsidequotes:'\'>print<"asimplerway:'\\'">asimplerway:'\'還可以在字符串中使用\ddd〔ddd為三位十進制數(shù)字方式表示字母。"alo\n123\""和'\97lo\10\04923"'是相同的。還可以使用[[...]]表示字符串。這種形式的字符串可以包含多行也,可以嵌套且不會解釋轉(zhuǎn)義序列,如果第一個字符是換行符會被自動忽略掉。這種形式的字符串用來包含一段代碼是非常方便的。page=[[<HTML><HEAD><TITLE>AnHTMLPage</TITLE></HEAD><BODY>Lua[[atextbetweendoublebrackets]]</BODY></HTML>]]io.write<page>運行時,Lua會自動在string和numbers之間自動進行類型轉(zhuǎn)換,當一個字符串使用算術(shù)操作符時,string就會被轉(zhuǎn)成數(shù)字。print<"10"+1>-->11print<"10+1">-->10+1print<"-5.3e-10"*"2">-->-1.06e-09print<"hello"+1>--ERROR<cannotconvert"hello">反過來,當Lua期望一個string而碰到數(shù)字時,會將數(shù)字轉(zhuǎn)成string。print<10..20>-->1020..在Lua中是字符串連接符,當在一個數(shù)字后面寫..時,必須加上空格以防止被解釋錯。盡管字符串和數(shù)字可以自動轉(zhuǎn)換,但兩者是不同的,像10=="10"這樣的比較永遠都是錯的。如果需要顯式將string轉(zhuǎn)成數(shù)字可以使用函數(shù)tonumber<>,如果string不是正確的數(shù)字該函數(shù)將返回nil。line=io.read<>--readalinen=tonumber<line>--trytoconvertittoanumberifn==nilthenerror<line.."isnotavalidnumber">elseprint<n*2>end反之,可以調(diào)用tostring<>將數(shù)字轉(zhuǎn)成字符串,這種轉(zhuǎn)換一直有效:print<tostring<10>=="10">-->trueprint<10..""=="10">-->true2.5Functions函數(shù)是第一類值〔和其他變量相同,意味著函數(shù)可以存儲在變量中,可以作為函數(shù)的參數(shù),也可以作為函數(shù)的返回值。這個特性給了語言很大的靈活性:一個程序可以重新定義函數(shù)增加新的功能或者為了避免運行不可靠代碼創(chuàng)建安全運行環(huán)境而隱藏函數(shù),此外這特性在Lua實現(xiàn)面向?qū)ο笾幸财鹆酥匾饔谩苍诘?6章詳細講述。Lua可以調(diào)用lua或者C實現(xiàn)的函數(shù),Lua所有標準庫都是用C實現(xiàn)的。標準庫包括string庫、table庫、I/O庫、OS庫、算術(shù)庫、debug庫。2.6UserdataandThreadsuserdata可以將C數(shù)據(jù)存放在Lua變量中,userdata在Lua中除了賦值和相等比較外沒有預定義的操作。userdata用來描述應用程序或者使用C實現(xiàn)的庫創(chuàng)建的新類型。例如:用標準I/O庫來描述文件。下面在CAPI章節(jié)中我們將詳細討論。在第九章討論協(xié)同操作的時候,我們介紹線程。第3章表達式Lua中的表達式包括數(shù)字常量、字符串常量、變量、一元和二元運算符、函數(shù)調(diào)用。還可以是非傳統(tǒng)的函數(shù)定義和表構(gòu)造。3.1算術(shù)運算符二元運算符:+-*/^
<加減乘除冪>一元運算符:-
<負值>這些運算符的操作數(shù)都是實數(shù)。3.2關(guān)系運算符<><=>===~=這些操作符返回結(jié)果為false或者true;==和~=比較兩個值,如果兩個值類型不同,Lua認為兩者不同;nil只和自己相等。Lua通過引用比較tables、userdata、functions。也就是說當且僅當兩者表示同一個對象時相等。a={};a.x=1;a.y=0b={};b.x=1;b.y=0c=aa==cbuta~=bLua比較數(shù)字按傳統(tǒng)的數(shù)字大小進行,比較字符串按字母的順序進行,但是字母順序依賴于本地環(huán)境。當比較不同類型的值的時候要特別注意:"0"==0--false2<15--true"2"<"15"--false<alphabeticalorder!>為了避免不一致的結(jié)果,混合比較數(shù)字和字符串,Lua會報錯,比如:2<"15"3.3邏輯運算符andornot邏輯運算符認為false和nil是假〔false,其他為真,0也是true.and和or的運算結(jié)果不是true和false,而是和它的兩個操作數(shù)相關(guān)。aandb--如果a為false,則返回a,否則返回baorb--如果a為true,則返回a,否則返回b例如:print<4and5>-->5print<niland13>-->nilprint<falseand13>-->falseprint<4or5>-->4print<falseor5>-->5一個很實用的技巧:如果x為false或者nil則給x賦初始值vx=xorv等價于ifnotxthenx=vendand的優(yōu)先級比or高。C語言中的三元運算符a?b:c在Lua中可以這樣實現(xiàn):<aandb>orcnot的結(jié)果只返回false或者trueprint<notnil>-->trueprint<notfalse>-->trueprint<not0>-->falseprint<notnotnil>-->false3.4連接運算符..--兩個點字符串連接,如果操作數(shù)為數(shù)字,Lua將數(shù)字轉(zhuǎn)成字符串。print<"Hello".."World">-->HelloWorldprint<0..1>-->013.5優(yōu)先級從高到低的順序:^not-<unary>*/+-..<><=>=~===andor除了^和..外所有的二元運算符都是左連接的。a+i<b/2+1<--><a+i><<<b/2>+1>5+x^2*8<-->5+<<x^2>*8>a<yandy<=z<--><a<y>and<y<=z>-x^2<-->-<x^2>x^y^z<-->x^<y^z>3.6表的構(gòu)造構(gòu)造器是創(chuàng)建和初始化表的表達式。表是Lua特有的功能強大的東西。最簡單的構(gòu)造函數(shù)是{},用來創(chuàng)建一個空表??梢灾苯映跏蓟瘮?shù)組:days={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}Lua將"Sunday"初始化days[1]〔第一個元素索引為1,用"Monday"初始化days[2]...print<days[4]>-->Wednesday構(gòu)造函數(shù)可以使用任何表達式初始化:tab={sin<1>,sin<2>,sin<3>,sin<4>,sin<5>,sin<6>,sin<7>,sin<8>}如果想初始化一個表作為record使用可以這樣:a={x=0,y=0}<-->a={};a.x=0;a.y=0不管用何種方式創(chuàng)建table,我們都可以向表中添加或者刪除任何類型的域,構(gòu)造函數(shù)僅僅影響表的初始化。w={x=0,y=0,label="console"}x={sin<0>,sin<1>,sin<2>}w[1]="anotherfield"x.f=wprint<w["x"]>-->0print<w[1]>-->anotherfieldprint<x.f[1]>-->anotherfieldw.x=nil--removefield"x"每次調(diào)用構(gòu)造函數(shù),Lua都會創(chuàng)建一個新的table,可以使用table構(gòu)造一個list:list=nilforlineinio.lines<>dolist={next=list,value=line}end這段代碼從標準輸入讀進每行,然后反序形成鏈表。下面的代碼打印鏈表的內(nèi)容:l=listwhileldoprint<l.value>l=l.nextend在同一個構(gòu)造函數(shù)中可以混合列表風格和record風格進行初始化,如:polyline={color="blue",thickness=2,npoints=4,{x=0,y=0},{x=-10,y=0},{x=-10,y=1},{x=0,y=1}}這個例子也表明我們可以嵌套構(gòu)造函數(shù)來表示復雜的數(shù)據(jù)結(jié)構(gòu).print<polyline[2].x>-->-10上面兩種構(gòu)造函數(shù)的初始化方式還有限制,比如你不能使用負索引初始化一個表中元素,字符串索引也不能被恰當?shù)谋硎?。下面介紹一種更一般的初始化方式,我們用[expression]顯示的表示將被初始化的索引:opnames={["+"]="add",["-"]="sub",["*"]="mul",["/"]="div"}i=20;s="-"a={[i+0]=s,[i+1]=s..s,[i+2]=s..s..s}print<opnames[s]>-->subprint<a[22]>-->list風格初始化和record風格初始化是這種一般初始化的特例:{x=0,y=0}<-->{["x"]=0,["y"]=0}{"red","green","blue"}<-->{[1]="red",[2]="green",[3]="blue"}如果真的想要數(shù)組下標從0開始:days={[0]="Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}注意:不推薦數(shù)組下標從0開始,否則很多標準庫不能使用。在構(gòu)造函數(shù)的最后的","是可選的,可以方便以后的擴展。a={[1]="red",[2]="green",[3]="blue",}在構(gòu)造函數(shù)中域分隔符逗號〔","可以用分號〔";"替代,通常我們使用分號用來分割不同類型的表元素。{x=10,y=45;"one","two","three"}第4章基本語法Lua像C和PASCAL幾乎支持所有的傳統(tǒng)語句:賦值語句、控制結(jié)構(gòu)語句、函數(shù)調(diào)用等,同時也支持非傳統(tǒng)的多變量賦值、局部變量聲明。4.1賦值語句賦值是改變一個變量的值和改變表域的最基本的方法。a="hello".."world"t.n=t.n+1Lua可以對多個變量同時賦值,變量列表和值列表的各個元素用逗號分開,賦值語句右邊的值會依次賦給左邊的變量。a,b=10,2*x<-->a=10;b=2*x遇到賦值語句Lua會先計算右邊所有的值然后再執(zhí)行賦值操作,所以我們可以這樣進行交換變量的值:x,y=y,x--swap'x'for'y'a[i],a[j]=a[j],a[i]--swap'a[i]'for'a[i]'當變量個數(shù)和值的個數(shù)不一致時,Lua會一直以變量個數(shù)為基礎(chǔ)采取以下策略:a.變量個數(shù)>值的個數(shù)按變量個數(shù)補足nilb.變量個數(shù)<值的個數(shù)多余的值會被忽略例如:a,b,c=0,1print<a,b,c>-->01nila,b=a+1,b+1,b+2--valueofb+2isignoredprint<a,b>-->12a,b,c=0print<a,b,c>-->0nilnil上面最后一個例子是一個常見的錯誤情況,注意:如果要對多個變量賦值必須依次對每個變量賦值。a,b,c=0,0,0print<a,b,c>-->000多值賦值經(jīng)常用來交換變量,或?qū)⒑瘮?shù)調(diào)用返回給變量:a,b=f<>f<>返回兩個值,第一個賦給a,第二個賦給b。4.2局部變量與代碼塊〔block使用local創(chuàng)建一個局部變量,與全局變量不同,局部變量只在被聲明的那個代碼塊內(nèi)有效。代碼塊:指一個控制結(jié)構(gòu)內(nèi),一個函數(shù)體,或者一個chunk〔變量被聲明的那個文件或者文本串。x=10locali=1--localtothechunkwhilei<=xdolocalx=i*2--localtothewhilebodyprint<x>-->2,4,6,8,...i=i+1endifi>20thenlocalx--localtothe"then"bodyx=20print<x+2>elseprint<x>-->10<theglobalone>endprint<x>-->10<theglobalone>注意,如果在交互模式下上面的例子可能不能輸出期望的結(jié)果,因為第二句locali=1是一個完整的chunk,在交互模式下執(zhí)行完這一句后,Lua將開始一個新的chunk,這樣第二句的i已經(jīng)超出了他的有效范圍。可以將這段代碼放在do..end〔相當于c/c++的{}塊中。應該盡可能的使用局部變量,有兩個好處:1.避免命名沖突2.訪問局部變量的速度比全局變量更快.我們給block劃定一個明確的界限:do..end內(nèi)的部分。當你想更好的控制局部變量的作用范圍的時候這是很有用的。dolocala2=2*alocald=sqrt<b^2-4*a*c>x1=<-b+d>/a2x2=<-b-d>/a2end--scopeof'a2'and'd'endshereprint<x1,x2>4.3控制結(jié)構(gòu)語句控制結(jié)構(gòu)的條件表達式結(jié)果可以是任何值,Lua認為false和nil為假,其他值為真。if語句,有三種形式:ifconditionsthenthen-partend;ifconditionsthenthen-partelseelse-partend;ifconditionsthenthen-partelseifconditionsthenelseif-part..>多個elseifelseelse-partend;while語句:whileconditiondostatements;end;repeat-until語句:repeatstatements;untilconditions;for語句有兩大類:第一,數(shù)值for循環(huán):forvar=exp1,exp2,exp3doloop-partendfor將用exp3作為step從exp1〔初始值到exp2〔終止值,執(zhí)行l(wèi)oop-part。其中exp3可以省略,默認step=1有幾點需要注意:1.三個表達式只會被計算一次,并且是在循環(huán)開始前。fori=1,f<x>doprint<i>endfori=10,1,-1doprint<i>end第一個例子f<x>只會在循環(huán)前被調(diào)用一次。2.控制變量var是局部變量自動被聲明,并且只在循環(huán)內(nèi)有效.fori=1,10doprint<i>endmax=i--probablywrong!'i'hereisglobal如果需要保留控制變量的值,需要在循環(huán)中將其保存--findavalueinalistlocalfound=nilfori=1,a.ndoifa[i]==valuethenfound=i--savevalueof'i'breakendendprint<found>3.循環(huán)過程中不要改變控制變量的值,那樣做的結(jié)果是不可預知的。如果要退出循環(huán),使用break語句。第二,范型for循環(huán):前面已經(jīng)見過一個例子:--printallvaluesofarray'a'fori,vinipairs<a>doprint<v>end范型for遍歷迭代子函數(shù)返回的每一個值。再看一個遍歷表key的例子:--printallkeysoftable't'forkinpairs<t>doprint<k>end范型for和數(shù)值for有兩點相同:1.控制變量是局部變量2.不要修改控制變量的值再看一個例子,假定有一個表:days={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}現(xiàn)在想把對應的名字轉(zhuǎn)換成星期幾,一個有效地解決問題的方式是構(gòu)造一個反向表:revDays={["Sunday"]=1,["Monday"]=2,
["Tuesday"]=3,["Wednesday"]=4,
["Thursday"]=5,["Friday"]=6,
["Saturday"]=7}下面就可以很容易獲取問題的答案了:x="Tuesday"print<revDays[x]>-->3我們不需要手工,可以自動構(gòu)造反向表revDays={}fori,vinipairs<days>dorevDays[v]=iend如果你對范型for還有些不清楚在后面的章節(jié)我們會繼續(xù)來學習。4.4break和return語句break語句用來退出當前循環(huán)〔for、repeat、while。在循環(huán)外部不可以使用。return用來從函數(shù)返回結(jié)果,當一個函數(shù)自然結(jié)束時,結(jié)尾會有一個默認的return?!策@種函數(shù)類似pascal的過程〔procedureLua語法要求break和return只能出現(xiàn)在block的結(jié)尾一句〔也就是說:作為chunk的最后一句,或者在end之前,或者else前,或者until前,例如:locali=1whilea[i]doifa[i]==vthenbreakendi=i+1end有時候為了調(diào)試或者其他目的需要在block的中間使用return或者break,可以顯式的使用do..end來實現(xiàn):functionfoo<>return--<<SYNTAXERROR--'return'isthelaststatementinthenextblockdoreturnend--OK...--statementsnotreachedend第5章函數(shù)函數(shù)有兩種用途:1.完成指定的任務,這種情況下函數(shù)作為調(diào)用語句使用;2.計算并返回值,這種情況下函數(shù)作為賦值語句的表達式使用。語法:functionfunc_name<arguments-list>statements-list;end;調(diào)用函數(shù)的時候,如果參數(shù)列表為空,必須使用<>表明是函數(shù)調(diào)用。print<8*9,9/8>a=math.sin<3>+math.cos<10>print<os.date<>>上述規(guī)則有一個例外,當函數(shù)只有一個參數(shù)并且這個參數(shù)是字符串或者表構(gòu)造的時候,<>可有可無:print"HelloWorld"<-->print<"HelloWorld">dofile'a.lua'<-->dofile<'a.lua'>print[[amulti-line<-->print<[[amulti-linemessage]]message]]>f{x=10,y=20}<-->f<{x=10,y=20}>type{}<-->type<{}>Lua也提供了面向?qū)ο蠓绞秸{(diào)用函數(shù)的語法,比如o:foo<x>與o.foo<o,x>是等價的,后面的章節(jié)會詳細介紹面向?qū)ο髢?nèi)容。Lua使用的函數(shù),既可是Lua編寫的,也可以是其他語言編寫的,對于Lua程序員,用什么語言實現(xiàn)的函數(shù)使用起來都一樣。Lua函數(shù)實參和形參的匹配與賦值語句類似,多余部分被忽略,缺少部分用nil補足。functionf<a,b>returnaorbendCALLPARAMETERSf<3>a=3,b=nilf<3,4>a=3,b=4f<3,4,5>a=3,b=4<5isdiscarded>5.1多返回值Lua函數(shù)可以返回多個結(jié)果值,比如string.find,其返回匹配串"開始和結(jié)束的下標"〔如果不存在匹配串返回nil。s,e=string.find<"helloLuausers","Lua">print<s,e>-->79Lua函數(shù)中,在return后列出要返回的值得列表即可返回多值,如:functionmaximum<a>localmi=1--maximumindexlocalm=a[mi]--maximumvaluefori,valinipairs<a>doifval>mthenmi=im=valendendreturnm,miendprint<maximum<{8,10,23,12,5}>>-->233Lua總是調(diào)整函數(shù)返回值的個數(shù)以適用調(diào)用環(huán)境,當作為獨立的語句調(diào)用函數(shù)時,所有返回值將被忽略。假設有如下三個函數(shù):functionfoo0<>end--returnsnoresultsfunctionfoo1<>return'a'end--returns1resultfunctionfoo2<>return'a','b'end--returns2results第一,當作為表達式調(diào)用函數(shù)時,有以下幾種情況:1.當調(diào)用作為表達式最后一個參數(shù)或者僅有一個參數(shù)時,根據(jù)變量個數(shù)函數(shù)盡可能多地返回多個值,不足補nil,超出舍去。2.其他情況下,函數(shù)調(diào)用僅返回第一個值〔如果沒有返回值為nilx,y=foo2<>--x='a',y='b'x=foo2<>--x='a','b'isdiscardedx,y,z=10,foo2<>--x=10,y='a',z='b'x,y=foo0<>--x=nil,y=nilx,y=foo1<>--x='a',y=nilx,y,z=foo2<>--x='a',y='b',z=nilx,y=foo2<>,20--x='a',y=20x,y=foo0<>,20,30--x='nil',y=20,30isdiscarded第二,函數(shù)調(diào)用作為函數(shù)參數(shù)被調(diào)用時,和多值賦值是相同。print<foo0<>>-->print<foo1<>>-->aprint<foo2<>>-->abprint<foo2<>,1>-->a1print<foo2<>.."x">-->ax第三,函數(shù)調(diào)用在表構(gòu)造函數(shù)中初始化時,和多值賦值時相同。a={foo0<>}--a={}<anemptytable>a={foo1<>}--a={'a'}a={foo2<>}--a={'a','b'}a={foo0<>,foo2<>,4}--a[1]=nil,a[2]='a',a[3]=4另外,returnf<>這種形式,則返回"f<>的返回值":functionfoo<i>ifi==0thenreturnfoo0<>elseifi==1thenreturnfoo1<>elseifi==2thenreturnfoo2<>endendprint<foo<1>>-->aprint<foo<2>>-->abprint<foo<0>>--<noresults>print<foo<3>>--<noresults>可以使用圓括號強制使調(diào)用返回一個值。print<<foo0<>>>-->nilprint<<foo1<>>>-->aprint<<foo2<>>>-->a一個return語句如果使用圓括號將返回值括起來也將導致返回一個值。函數(shù)多值返回的特殊函數(shù)unpack,接受一個數(shù)組作為輸入?yún)?shù),返回數(shù)組的所有元素。unpack被用來實現(xiàn)范型調(diào)用機制,在C語言中可以使用函數(shù)指針調(diào)用可變的函數(shù),可以聲明參數(shù)可變的函數(shù),但不能兩者同時可變。在Lua中如果你想調(diào)用可變參數(shù)的可變函數(shù)只需要這樣:f<unpack<a>>unpack返回a所有的元素作為f<>的參數(shù)f=string.finda={"hello","ll"}print<f<unpack<a>>>-->34預定義的unpack函數(shù)是用C語言實現(xiàn)的,我們也可以用Lua來完成:functionunpack<t,i>i=ior1ift[i]thenreturnt[i],unpack<t,i+1>endend5.2可變參數(shù)Lua函數(shù)可以接受可變數(shù)目的參數(shù),和C語言類似在函數(shù)參數(shù)列表中使用三點〔...表示函數(shù)有可變的參數(shù)。Lua將函數(shù)的參數(shù)放在一個叫arg的表中,除了參數(shù)以外,arg表中還有一個域n表示參數(shù)的個數(shù)。例如,我們可以重寫print函數(shù):printResult=""functionprint<...>fori,vinipairs<arg>doprintResult=printResult..tostring<v>.."\t"endprintResult=printResult.."\n"end有時候我們可能需要幾個固定參數(shù)加上可變參數(shù)functiong<a,b,...>endCALLPARAMETERSg<3>a=3,b=nil,arg={n=0}g<3,4>a=3,b=4,arg={n=0}g<3,4,5,8>a=3,b=4,arg={5,8;n=2}如上面所示,Lua會將前面的實參傳給函數(shù)的固定參數(shù),后面的實參放在arg表中。舉個具體的例子,如果我們只想要string.find返回的第二個值。一個典型的方法是使用啞元〔dummyvariable,下劃線:local_,x=string.find<s,p>--nowuse`x'...還可以利用可變參數(shù)聲明一個select函數(shù):functionselect<n,...>returnarg[n]endprint<string.find<"hellohello","hel">>-->69print<select<1,string.find<"hellohello","hel">>>-->6print<select<2,string.find<"hellohello","hel">>>-->9有時候需要將函數(shù)的可變參數(shù)傳遞給另外的函數(shù)調(diào)用,可以使用前面我們說過的unpack<arg>返回arg表所有的可變參數(shù),Lua提供了一個文本格式化的函數(shù)string.format〔類似C語言的sprintf函數(shù):functionfwrite<fmt,...>returnio.write<string.format<fmt,unpack<arg>>>end這個例子將文本格式化操作和寫操作組合為一個函數(shù)。5.3命名參數(shù)Lua的函數(shù)參數(shù)是和位置相關(guān)的,調(diào)用時實參會按順序依次傳給形參。有時候用名字指定參數(shù)是很有用的,比如rename函數(shù)用來給一個文件重命名,有時候我們我們記不清命名前后兩個參數(shù)的順序了:--invalidcoderename<old="temp.lua",new="temp1.lua">上面這段代碼是無效的,Lua可以通過將所有的參數(shù)放在一個表中,把表作為函數(shù)的唯一參數(shù)來實現(xiàn)上面這段偽代碼的功能。因為Lua語法支持函數(shù)調(diào)用時實參可以是表的構(gòu)造。rename{old="temp.lua",new="temp1.lua"}根據(jù)這個想法我們重定義了rename:functionrename<arg>returnos.rename<arg.old,arg.new>end當函數(shù)的參數(shù)很多的時候,這種函數(shù)參數(shù)的傳遞方式很方便的。例如GUI庫中創(chuàng)建窗體的函數(shù)有很多參數(shù)并且大部分參數(shù)是可選的,可以用下面這種方式:w=Window{x=0,y=0,width=300,height=200,title="Lua",background="blue",border=true}functionWindow<options>--checkmandatoryoptionsiftype<options.title>~="string"thenerror<"notitle">elseiftype<options.width>~="number"thenerror<"nowidth">elseiftype<options.height>~="number"thenerror<"noheight">end--everythingelseisoptional_Window<options.title,options.xor0,--defaultvalueoptions.yor0,--defaultvalueoptions.width,options.height,options.backgroundor"white",--defaultoptions.border--defaultisfalse<nil>>end第6章再論函數(shù)Lua中的函數(shù)是帶有詞法定界〔lexicalscoping的第一類值〔first-classvalues。第一類值指:在Lua中函數(shù)和其他值〔數(shù)值、字符串一樣,函數(shù)可以被存放在變量中,也可以存放在表中,可以作為函數(shù)的參數(shù),還可以作為函數(shù)的返回值。詞法定界指:嵌套的函數(shù)可以訪問他外部函數(shù)中的變量。這一特性給Lua提供了強大的編程能力。Lua中關(guān)于函數(shù)稍微難以理解的是函數(shù)也可以沒有名字,匿名的。當我們提到函數(shù)名〔比如print,實際上是說一個指向函數(shù)的變量,像持有其他類型值的變量一樣:a={p=print}a.p<"HelloWorld">-->HelloWorldprint=math.sin--`print'nowreferstothesinefunctiona.p<print<1>>-->0.841470sin=a.p--`sin'nowreferstotheprintfunctionsin<10,20>-->1020既然函數(shù)是值,那么表達式也可以創(chuàng)建函數(shù)了,Lua中我們經(jīng)常這樣寫:functionfoo<x>return2*xend這實際上是Lua語法的特例,下面是原本的函數(shù):foo=function<x>return2*xend函數(shù)定義實際上是一個賦值語句,將類型為function的變量賦給一個變量。我們使用function<x>...end來定義一個函數(shù)和使用{}創(chuàng)建一個表一樣。table標準庫提供一個排序函數(shù),接受一個表作為輸入?yún)?shù)并且排序表中的元素。這個函數(shù)必須能夠?qū)Σ煌愋偷闹怠沧址蛘邤?shù)值按升序或者降序進行排序。Lua不是盡可能多地提供參數(shù)來滿足這些情況的需要,而是接受一個排序函數(shù)作為參數(shù)〔類似C++的函數(shù)對象,排序函數(shù)接受兩個排序元素作為輸入?yún)?shù),并且返回兩者的大小關(guān)系,例如:network={{name="grauna",IP="4"},{name="arraial",IP="3"},{name="lua",IP="2"},{name="derain",IP="0"},}如果我們想通過表的name域排序:table.sort<network,function<a,b>return<>>end>以其他函數(shù)作為參數(shù)的函數(shù)在Lua中被稱作高級函數(shù)〔higher-orderfunction,如上面的sort。在Lua中,高級函數(shù)與普通函數(shù)沒有區(qū)別,它們只是把"作為參數(shù)的函數(shù)"當作第一類值〔first-classvalue處理而已。下面給出一個繪圖函數(shù)的例子:functioneraseTerminal<>io.write<"\27[2J">end--writesan'*'atcolumn'x','rowy'functionmark<x,y>io.write<string.format<"\27[%d;%dH*",y,x>>end--TerminalsizeTermSize={w=80,h=24}--plotafunction--<assumethatdomainandimageareintherange[-1,1]>functionplot<f>eraseTerminal<>fori=1,TermSize.wdolocalx=<i/TermSize.w>*2-1localy=<f<x>+1>/2*TermSize.hmark<i,y>endio.read<>--waitbeforespoilingthescreenend要想讓這個例子正確的運行,你必須調(diào)整你的終端類型和代碼中的控制符\o""[3]一致:plot<function<x>returnmath.sin<x*2*math.pi>end>將在屏幕上輸出一個正弦曲線。將第一類值函數(shù)應用在表中是Lua實現(xiàn)面向?qū)ο蠛桶鼨C制的關(guān)鍵,這部分內(nèi)容在后面章節(jié)介紹。
6.1閉包當一個函數(shù)內(nèi)部嵌套另一個函數(shù)定義時,內(nèi)部的函數(shù)體可以訪問外部的函數(shù)的局部變量,這種特征我們稱作詞法定界。雖然這看起來很清楚,事實并非如此,詞法定界加上第一類函數(shù)在編程語言里是一個功能強大的概念,很少語言提供這種支持。下面看一個簡單的例子,假定有一個學生姓名的列表和一個學生名和成績對應的表;現(xiàn)在想根據(jù)學生的成績從高到低對學生進行排序,可以這樣做:names={"Peter","Paul","Mary"}grades={Mary=10,Paul=7,Peter=8}table.sort<names,function<n1,n2>returngrades[n1]>grades[n2]--comparethegradesend>假定創(chuàng)建一個函數(shù)實現(xiàn)此功能:functionsortbygrade<names,grades>table.sort<names,function<n1,n2>returngrades[n1]>grades[n2]--comparethegradesend>end例子中包含在sortbygrade函數(shù)內(nèi)部的sort中的匿名函數(shù)可以訪問sortbygrade的參數(shù)grades,在匿名函數(shù)內(nèi)部grades不是全局變量也不是局部變量,我們稱作外部的局部變量〔externallocalvariable或者upvalue?!瞮pvalue意思有些誤導,然而在Lua中他的存在有歷史的根源,還有他比起externallocalvariable簡短??聪旅娴拇a:functionnewCounter<>locali=0returnfunction<>--anonymousfunctioni=i+1returniendendc1=newCounter<>print<c1<>>-->1print<c1<>>-->2匿名函數(shù)使用upvaluei保存他的計數(shù),當我們調(diào)用匿名函數(shù)的時候i已經(jīng)超出了作用范圍,因為創(chuàng)建i的函數(shù)newCounter已經(jīng)返回了。然而Lua用閉包的思想正確處理了這種情況。簡單的說,閉包是一個函數(shù)以及它的upvalues。如果我們再次調(diào)用newCounter,將創(chuàng)建一個新的局部變量i,因此我們得到了一個作用在新的變量i上的新閉包。c2=newCounter<>print<c2<>>-->1print<c1<>>-->3print<c2<>>-->2c1、c2是建立在同一個函數(shù)上,但作用在同一個局部變量的不同實例上的兩個不同的閉包。技術(shù)上來講,閉包指值而不是指函數(shù),函數(shù)僅僅是閉包的一個原型聲明;盡管如此,在不會導致混淆的情況下我們繼續(xù)使用術(shù)語函數(shù)代指閉包。閉包在上下文環(huán)境中提供很有用的功能,如前面我們見到的可以作為高級函數(shù)〔sort的參數(shù);作為函數(shù)嵌套的函數(shù)〔newCounter。這一機制使得我們可以在Lua的函數(shù)世界里組合出奇幻的編程技術(shù)。閉包也可用在回調(diào)函數(shù)中,比如在GUI環(huán)境中你需要創(chuàng)建一系列button,但用戶按下button時回調(diào)函數(shù)被調(diào)用,可能不同的按鈕被按下時需要處理的任務有點區(qū)別。具體來講,一個十進制計算器需要10個相似的按鈕,每個按鈕對應一個數(shù)字,可以使用下面的函數(shù)創(chuàng)建他們:functiondigitButton<digit>returnButton{label=digit,action=function<>add_to_display<digit>end}end這個例子中我們假定Button是一個用來創(chuàng)建新按鈕的工具,label是按鈕的標簽,action是按鈕被按下時調(diào)用的回調(diào)函數(shù)?!矊嶋H上是一個閉包,因為他訪問upvaluedigit。digitButton完成任務返回后,局部變量digit超出范圍,回調(diào)函數(shù)仍然可以被調(diào)用并且可以訪問局部變量digit。閉包在完全不同的上下文中也是很有用途的。因為函數(shù)被存儲在普通的變量內(nèi)我們可以很方便的重定義或者預定義函數(shù)。通常當你需要原始函數(shù)有一個新的實現(xiàn)時可以重定義函數(shù)。例如你可以重定義sin使其接受一個度數(shù)而不是弧度作為參數(shù):oldSin=math.sinmath.sin=function<x>returnoldSin<x*math.pi/180>end更清楚的方式:dolocaloldSin=math.sinlocalk=math.pi/180math.sin=function<x>returnoldSin<x*k>endend這樣我們把原始版本放在一個局部變量內(nèi),訪問sin的唯一方式是通過新版本的函數(shù)。利用同樣的特征我們可以創(chuàng)建一個安全的環(huán)境〔也稱作沙箱,和java里的沙箱一樣,當我們運行一段不信任的代碼〔比如我們運行網(wǎng)絡服務器上獲取的代碼時安全的環(huán)境是需要的,比如我們可以使用閉包重定義io庫的open函數(shù)來限制程序打開的文件。dolocaloldOpen=io.openio.open=function<filename,mode>ifaccess_OK<filename,mode>thenreturnoldOpen<filename,mode>elsereturnnil,"accessdenied"endendend6.2非全局函數(shù)Lua中函數(shù)可以作為全局變量也可以作為局部變量,我們已經(jīng)看到一些例子:函數(shù)作為table的域〔大部分Lua標準庫使用這種機制來實現(xiàn)的比如io.read、math.sin。這種情況下,必須注意函數(shù)和表語法:1.表和函數(shù)放在一起Lib={}Lib.foo=function<x,y>returnx+yendLib.goo=function<x,y>returnx-yend2.使用表構(gòu)造函數(shù)Lib={foo=function<x,y>returnx+yend,goo=function<x,y>returnx-yend}3.Lua提供另一種語法方式Lib={}functionLib.foo<x,y>returnx+yendfunctionLib.goo<x,y>returnx-yend當我們將函數(shù)保存在一個局部變量內(nèi)時,我們得到一個局部函數(shù),也就是說局部函數(shù)像局部變量一樣在一定范圍內(nèi)有效。這種定義在包中是非常有用的:因為Lua把chunk當作函數(shù)處理,在chunk內(nèi)可以聲明局部函數(shù)〔僅僅在chunk內(nèi)可見,詞法定界保證了包內(nèi)的其他函數(shù)可以調(diào)用此函數(shù)。下面是聲明局部函數(shù)的兩種方式:1.方式一localf=function<...>...endlocalg=function<...>...f<>--externallocal`f'isvisiblehere...end2.方式二localfunctionf<...>...end有一點需要注意的是在聲明遞歸局部函數(shù)的方式:localfact=function<n>ifn==0thenreturn1elsereturnn*fact<n-1>--buggyendend上面這種方式導致Lua編譯時遇到fact<n-1>并不知道他是局部函數(shù)fact,Lua會去查找是否有這樣的全局函數(shù)fact。為了解決這個問題我們必須在定義函數(shù)以前先聲明:localfactfact=function<n>ifn==0thenreturn1elseretur
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 未來五年亞麻紡粗紗機行業(yè)跨境出海戰(zhàn)略分析研究報告
- 未來五年壓捆機行業(yè)跨境出海戰(zhàn)略分析研究報告
- 未來五年剔須刷行業(yè)直播電商戰(zhàn)略分析研究報告
- 浙江國企招聘2025臺州市椒江區(qū)社會事業(yè)發(fā)展集團有限公司招聘3人筆試參考題庫附帶答案詳解(3卷)
- 2025河北省青年美術(shù)家協(xié)會運營服務中心社會招聘21人筆試參考題庫附帶答案詳解(3卷)
- 2025年湖北交投巴楚建設管理有限公司社會招聘2人筆試參考題庫附帶答案詳解(3卷)
- 培訓師培訓技能提升
- 2025年四川石化公司秋季高校畢業(yè)生招聘55人筆試參考題庫附帶答案詳解(3卷)
- 2025年中國長安汽車集團校園招聘筆試參考題庫附帶答案詳解(3卷)
- 2025中國鋼研秋季校園招聘火熱進行中筆試參考題庫附帶答案詳解(3卷)
- 機電產(chǎn)品三維設計 課件 項目4.14.2.1~3扭尾機械手
- 2025考評員培訓考試題(含答案)
- 醫(yī)院黨建與醫(yī)療質(zhì)量提升的融合策略
- 2025年聊城交運集團汽車站招聘工作人員(3人)參考筆試試題及答案解析
- 2025西部機場集團航空物流有限公司招聘參考考點題庫及答案解析
- 2025海南三亞市直屬學校赴高校面向2026年應屆畢業(yè)生招聘教師111人(第5號)考試筆試參考題庫附答案解析
- 2025中央廣播電視總臺招聘144人(公共基礎(chǔ)知識)綜合能力測試題附答案解析
- 嚴格執(zhí)行管理制度(3篇)
- 支氣管哮喘常見癥狀及護理技術(shù)培訓
- 2025年廣東省常用非金屬材料檢測技術(shù)培訓考核考前沖刺必會500題-含答案
- 船舶航次安全風險評估管理須知制度
評論
0/150
提交評論