<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 變量聲明與 C 語言的不同 Lua 中有一個常見的用法,不論變量、函數都可以用下面這種方法保存到局部變量中(同時加快訪問速度): ~~~ local foo = foo ~~~ 書里加了個括號來解釋這種寫法: > The local foo becomes visible only after its declaration. 這一點需要瞎扯的是 C 語言里相應的東西。 ~~~ int foo = 12; int bar = 6; void foobar(void) { int foo = foo; int bar[bar]; } ~~~ 與 Lua 不同,在 C 語言中初始賦值是聲明之后的事情。所以這里函數 foobar 中的 foo 會被初始化為自己(而不是全局的 foo,所以值不確定),bar 卻被合法地定義為一個含有 6 個元素的數組。 ## 看似多余的限制 另一個有趣的現象是在 4.4 節中說到: > For syntactic reasons, a break or return can appear only as the last statement of a block; in other words, as the last statement in your chunk or just before an end, an else, or an until. 乍一看覺得加上這個限制真是麻煩,但想想這不正是 break/return 的正確用法么?因為其后的語句都永遠不會被執行到,所以如果不是在塊的最后寫 break/return 是毫無意義的(調試除外)。雖然看上去是挺多余的一段話,但也算是說出了事物的本源。 ## 函數的本質 第六章 More About Functions 中說到我們平時在 Lua 中寫的函數聲明 ~~~ function foo (x) return 2*x end ~~~ 其實是一種語法糖,本質上我們可以把它寫成如下代碼: ~~~ foo = function (x) return 2*x end ~~~ 于是也就可以說 * Lua 中的所有函數都是匿名函數,之前所謂「具名函數」只是保存了某個匿名函數的變量罷了。 * Lua 中的函數聲明其實只是一個語句而已。 ## 終于有用的知識 在第 47 頁看到了一段令人淚流滿面的代碼和運行結果: ~~~ function derivative (f, delta) delta = delta or 1e-4 return function (x) return (f(x + delta) - f(x))/delta end end c = derivative(math.sin) print(math.cos(10), c(10)) --> -0.83907152907645 -0.83904432662041 ~~~ 最初我并不知道 derivative 是什么意思,但看了示例代碼和運行結果,頓時恍然大悟:這貨不就是導數嗎? ## 沙盒 ### 背景知識 Lua 給我的感覺是:各種內置函數和標準庫的存在感都是比較強的。如果執行這句: ~~~ for name in pairs(_G) do print(_G) end ~~~ 就會把各種環境中已存在名稱的打印出來: * 全局變量:比如字符串 _VERSION。 * 內置函數:比如 print、tonumber、dofile 之類。 * 模塊名稱:比如 string、io、coroutine 之類。 這里的全局變量 _G 就是存放環境的表(于是會有 _G 中存在著 _G._G 的遞歸)。 于是,平時對于全局變量的訪問就可以等同于對 _G 表進行索引: ~~~ value = _G[varname] --> value = varname _G[varname] = value --> varname = value ~~~ ### 改變函數的環境 函數的上下文環境可以通過 setfenv(f, table) 函數改變,其中 table 是新的環境表,f 表示需要被改變環境的函數。如果 f 是數字,則將其視為堆棧層級(Stack Level),從而指明函數(1 為當前函數,2 為上一級函數): ~~~ a = 3 -- 全局變量 a setfenv(1, {}) -- 將當前函數的環境表改為空表 print(a) -- 出錯,因為當前環境表中 print 已經不存在了 ~~~ 沒錯,不僅是 a 不存在,連 print 都一塊兒不存在了。如果需要引用以前的 print 則需要在新的環境表中放入線索: ~~~ a = 3 setfenv(1, { g = _G }) g.print(a) -- 輸出 nil g.print(g.a) -- 輸出 3 ~~~ ### 沙盒 于是,出于安全或者改變一些內置函數行為的目的,需要在執行 Lua 代碼時改變其環境時便可以使用 setfenv 函數。僅將你認為安全的函數或者新的實現加入新環境表中: ~~~ local env = {} -- 沙盒環境表,按需要添入允許的函數 function run_sandbox(code) local func, message = loadstring(code) if not func then return nil, message end -- 傳入代碼本身錯誤 setfenv(func, env) return pcall(func) end ~~~ ### Lua 5.2 的 _ENV 變量 Lua 5.2 中所有對全局變量 var 的訪問都會在語法上翻譯為 _ENV.var。而 _ENV 本身被認為是處于當前塊外的一個局部變量。(于是只要你自己定義一個名為 _ENV 的變量,就自動成為了其后代碼所處的「環境」(enviroment)。另有一個「全局環境」(global enviroment)的概念,指初始的 _G 表。) Lua 的作者之一 Roberto Ierusalimschy 同志在介紹 Lua 5.2 時說: > the new scheme, with _ENV, allows the main benefit of setfenv with a little more than syntactic sugar. 就我的理解來說,優點就是原先虛無縹緲只能通過 setfenv、getfenv 訪問的所謂「環境」終于實體化為一個始終存在的變量 _ENV 了。 于是以下兩個函數內容大致是一樣的: ~~~ -- Lua 5.1 function foobar() setfenv(1, {}) -- code here end -- Lua 5.2 function foobar() local _ENV = {} -- code here end ~~~ 而更進一步的是,5.2 中對 load 函數作出了修改。(包括但不限于 :))合并了 loadstring 功能,并可以在參數中指定所使用的環境表: ~~~ local func, message = load(code, nil, "t", env) ~~~ ## 面向對象 沒錯,Lua 中只存在表(Table)這么唯一一種數據結構,但依舊可以玩出面向對象的概念。 ### 添加成員函數 好吧,如果熟悉 C++ 還是很好理解類似的進化過程的:如果說 struct 里可以添加函數是從 C 過渡到 C++ 的第一認識的話,為 Table 添加函數也可以算是認識 Lua 是如何面向對象的第一步吧。 ~~~ player = { health = 200 } --> 一個普通的 player 表,這里看作是一個對象 function takeDamage(self, amount) self.health = self.health - amount end takeDamage(player, 20) --> 調用 ~~~ 如何將獨立的 takeDamage 塞進 player 中咧?答案是直接定義進去: ~~~ player = { health = 200 } function player.takeDamage(self, amount) self.health = self.health - amount end player.takeDamage(player, 20) --> 調用 ~~~ 這樣就相當于在 player 表中添加了一個叫做 takeDamage 的字段,和下面的代碼是一樣的: ~~~ player = { health = 200, takeDamage = function(self, amount) --> Lua 中的函數是 first-class value self.health = self.health - amount end } player.takeDamage(player, 20) --> 調用 ~~~ 調用時的 player.takeDamage(player, 20) 稍顯不和諧(據說用術語叫做 DRY),于是就要出動「冒號操作符」這個專門為此而生的語法糖了: ~~~ player:takeDamage(20) --> 等同于 player.takeDamage(player, 20) function player:takeDamage(amount) --> 等同于 function player.takeDamage(self, amount) ~~~ ### 從對象升華到類 類的意義在于提取一類對象的共同點從而實現量產(我瞎扯的 >_<)。同樣木有 Class 概念的 Javascript 使用 prototype 實現面向對象,Lua 則通過 Metatable 實現與 prototype 類似的功能。 ~~~ Player = {} function Player:create(o) --> 參數 o 可以暫時不管 o = o or { health = 200 } --> Lua 的 or 與一般的 || 不同,如果非 nil 則返回該非 nil 值 setmetatable(o, self) self.__index = self return o end function Player:takeDamage(amount) self.health = self.health - amount end playerA = Player:create() --> 參數 o 為 nil playerB = Player:create() playerA:takeDamage(20) playerB:takeDamage(40) ~~~ 顧名思義 Metatable 也是一個 Table,可以通過在其中存放一些函數(稱作 metamethod)從而修改一些默認的求值行為(如何顯示為字符串、如何相加、如何連接、如何進行索引)。Metatable 的 __index 域設置了「如何進行索引」的方法。例如調用 foo.bar 時,如果在 foo 中沒有找到名為 bar 的域時,則會調用 Metatable:__index(foo, bar)。于是: ~~~ playerA:takeDamage(20) ~~~ 因為在 playerA 中并不存在 takeDamge 函數,于是求助于 Metatable: ~~~ getmetatable(playerA).__index.takeDamage(playerA, 20) ~~~ 帶入 Metatable 后: ~~~ Player.__index.takeDamage(playerA, 20) ~~~ 因為 Player 的 __index 在 create 時被指定為 self,所以最終變為: ~~~ Player.takeDamage(playerA, 20) ~~~ 于是 takeDamage 的 self 得到了正確的對象 playerA。 ### 繼承 繼承是面向對象的一大特性,明白了如何創建「類」,那么繼承也就比較明了了,還記得大明湖畔的參數 o 么? ~~~ RMBPlayer = Player:create() function RMBPlayer:broadcast(message) --> 為子類添加新的方法 print(message) end function RMBPlayer:takeDamage(amount) --> 子類重載父類方法 self.health = self.health - amount / (self.money / 100) end vip = RMBPlayer:create { money = 200 } --> 子類添加新成員(單個 Table 作為參數可以省略括號) vip:takeDamage(20) vip:broadcast("F*ck") ~~~ 以上便是 Lua 中實現面向對象的基本方法。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看