<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                協程(coroutine)并不是 Lua 獨有的概念,如果讓我用一句話概括,那么大概就是:一種能夠在運行途中主動中斷,并且能夠從中斷處恢復運行的特殊函數。(嗯,其實不是函數。) ### [](https://github.com/andycai/luaprimer/blob/master/05.md#舉個最原始的例子)舉個最原始的例子: 下面給出一個最簡單的 Lua 中 coroutine 的用法演示: ~~~ function greet() print "hello world" end co = coroutine.create(greet) -- 創建 coroutine print(coroutine.status(co)) -- 輸出 suspended print(coroutine.resume(co)) -- 輸出 hello world -- 輸出 true (resume 的返回值) print(coroutine.status(co)) -- 輸出 dead print(coroutine.resume(co)) -- 輸出 false cannot resume dead coroutine (resume 的返回值) print(type(co)) -- 輸出 thread ~~~ 協程在創建時,需要把協程體函數傳遞給創建函數 create。新創建的協程處于 suspended 狀態,可以使用 resume 讓其運行,全部執行完成后協程處于 dead 狀態。如果嘗試 resume 一個 dead 狀態的,則可以從 resume 返回值上看出執行失敗。另外你還可以注意到 Lua 中協程(coroutine)的變量類型其實叫做「thread」Orz... 乍一看可能感覺和線程沒什么兩樣,但需要注意的是 resume 函數只有在 greet 函數「返回」后才會返回(所以說協程像函數)。 ### [](https://github.com/andycai/luaprimer/blob/master/05.md#函數執行的中斷與再開) 函數執行的中斷與再開 單從上面這個例子,我們似乎可以得出結論:協程果然就是某種坑爹的函數調用方式啊。然而,協程的真正魅力來自于 resume 和 yield 這對好基友之間的羈絆。 ### [](https://github.com/andycai/luaprimer/blob/master/05.md#函數-coroutineresumeco-val1-)函數 coroutine.resume(co[, val1, ...]) 開始或恢復執行協程 co。 如果是開始執行,val1 及之后的值都作為參數傳遞給協程體函數;如果是恢復執行,val1 及之后的值都作為 yield 的返回值傳遞。 第一個返回值(還記得 Lua 可以返回多個值嗎?)為表示執行成功與否的布爾值。如果成功,之后的返回值是 yield 的參數;如果失敗,第二個返回值為失敗的原因(Lua 的很多函數都采用這種錯誤處理方式)。 當然,如果是協程體函數執行完畢 return 而不是 yield,那么 resume 第一個返回值后跟著的就是其返回值。 ### [](https://github.com/andycai/luaprimer/blob/master/05.md#函數-coroutineyield)函數 coroutine.yield(...) 中斷協程的執行,使得開啟該協程的 coroutine.resume 返回。再度調用 coroutine.resume 時,會從該 yield 處恢復執行。 當然,yield 的所有參數都會作為 resume 第一個返回值后的返回值返回。 OK,總結一下:當 co = coroutine.create(f) 時,yield 和 resume 的關系如下圖: ### [](https://github.com/andycai/luaprimer/blob/master/05.md#how-coroutine-makes-life-easier)How coroutine makes life easier 如果要求給某個怪寫一個 AI:先向右走 30 幀,然后只要玩家進入視野就往反方向逃 15 幀。該怎么寫? #### [](https://github.com/andycai/luaprimer/blob/master/05.md#傳統做法)傳統做法 經典的純狀態機做法。 ~~~ -- 每幀的邏輯 function Monster:frame() self:state_func() self.state_frame_count = self.state_frame_count + 1 end -- 切換狀態 function Monster:set_next_state(state) self.state_func = state self.state_frame_count = 0 end -- 首先向右走 30 幀 function Monster:state_walk_1() local frame = self.state_frame_count self:walk(DIRECTION_RIGHT) if frame > 30 then self:set_next_state(state_wait_for_player) end end -- 等待玩家進入視野 function Monster:state_wait_for_player() if self:get_distance(player) < self.range then self.direction = -self:get_direction_to(player) self:set_next_state(state_walk_2) end end -- 向反方向走 15 幀 function Monster:state_walk_2() local frame = self.state_frame_count; self:walk(self.direction) if frame > 15 then self:set_next_state(state_wait_for_player) end end ~~~ #### [](https://github.com/andycai/luaprimer/blob/master/05.md#協程做法)協程做法 ~~~ -- 每幀的邏輯 function Monster:frame() -- 首先向右走 30 幀 for i = 1, 30 do self:walk(DIRECTION_RIGHT) self:wait() end while true do -- 等待玩家進入視野 while self:get_distance(player) >= self.range do self:wait() end -- 向反方向走 15 幀 self.direction = -self:get_direction_to(player) for i = 1, 15 do self:walk(self.direction) self:wait() end end end -- 該幀結束 function Monster:wait() coroutine.yield() end ~~~ 額外說一句,從 wait 函數可以看出,Lua 的協程并不要求一定要從協程體函數中調用 yield,這是和 Python 的一個區別。 協同程序(coroutine,這里簡稱協程)是一種類似于線程(thread)的東西,它擁有自己獨立的棧、局部變量和指令指針,可以跟其他協程共享全局變量和其他一些數據,并且具有一種掛起(yield)中斷協程主函數運行,下一次激活恢復協程會在上一次中斷的地方繼續執行(resume)協程主函數的控制機制。 Lua 把關于協程的所有函數放在一個名為 “coroutine” 的 table 里,coroutine 里具有以下幾個內置函數: ~~~ -coroutine-yield [function: builtin#34] | -wrap [function: builtin#37] | -status [function: builtin#31] | -resume [function: builtin#35] | -running [function: builtin#32] | -create [function: builtin#33] ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#coroutinecreate---創建協程)coroutine.create - 創建協程 函數 coroutine.create 用于創建一個新的協程,它只有一個以函數形式傳入的參數,該函數是協程的主函數,它的代碼是協程所需執行的內容 ~~~ co = coroutine.create(function() io.write("coroutine create!\n") end) print(co) ~~~ 當創建完一個協程后,會返回一個類型為 thread 的對象,但并不會馬上啟動運行協程主函數,協程的初始狀態是處于掛起狀態 ### [](https://github.com/andycai/luaprimer/blob/master/05.md#coroutinestatus---查看協程狀態)coroutine.status - 查看協程狀態 協程有 4 種狀態,分別是:掛起(suspended)、運行(running)、死亡(dead)和正常(normal),可以通過 coroutine.status 來輸出查看協程當前的狀態。 ~~~ print(coroutine.status(co)) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#coroutineresume---執行協程)coroutine.resume - 執行協程 函數 coroutine.resume 用于啟動或再次啟動一個協程的執行 ~~~ coroutine.resume(co) ~~~ 協程被調用執行后,其狀態會由掛起(suspended)改為運行(running)。不過當協程主函數全部運行完之后,它就變為死亡(dead)狀態。 傳遞給 resume 的額外參數都被看作是協程主函數的參數 ~~~ co = coroutine.create(function(a, b, c) print("co", a, b, c) end) coroutine.resume(co, 1, 2, 3) ~~~ 協程主函數執行完時,它的主函數所返回的值都將作為對應 resume 的返回值 ~~~ co = coroutine.create(function() return 3, 4 end) print(coroutine.resume(co)) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#coroutineyield---中斷協程運行)coroutine.yield - 中斷協程運行 coroutine.yield 函數可以讓一個運行中的協程中斷掛起 ~~~ co = coroutine.create(function() for i = 1, 3 do print("before coroutine yield", i) coroutine.yield() print("after coroutine yield", i) end end) coroutine.resume(co) ~~~ coroutine.resume(co) 上面第一個 resume 喚醒執行協程主函數代碼,直到第一個 yield。第二個 resume 激活被掛起的協程,并從上一次協程被中斷 yield 的位置繼續執行協程主函數代碼,直到再次遇到 yield 或程序結束。 resume 執行完協程主函數或者中途被掛起(yield)時,會有返回值返回,第一個值是 true,表示執行沒有錯誤。如果是被 yield 掛起暫停,yield 函數有參數傳入的話,這些參數會接著第一個值后面一并返回 ~~~ co = coroutine.create(function(a, b, c) coroutine.yield(a, b, c) end) print(coroutine.resume(co, 1, 2, 3)) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#以-coroutinewrap-的方式創建協程)以 coroutine.wrap 的方式創建協程 跟 coroutine.create 一樣,函數 coroutine.wrap 也是創建一個協程,但是它并不返回一個類型為 thread 的對象,而是返回一個函數。每當調用這個返回函數,都會執行協程主函數運行。所有傳入這個函數的參數等同于傳入 coroutine.resume 的參數。 coroutine.wrap 會返回所有應該由除第一個(錯誤代碼的那個布爾量) 之外的由 coroutine.resume 返回的值。 和 coroutine.resume 不同之處在于, coroutine.wrap 不會返回錯誤代碼,無法檢測出運行時的錯誤,也無法檢查 wrap 所創建的協程的狀態 ~~~ function wrap(param) print("Before yield", param) obtain = coroutine.yield() print("After yield", obtain) return 3 end resumer = coroutine.wrap(wrap) print(resumer(1)) print(resumer(2)) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#coroutinerunning---返回正在運行中的協程)coroutine.running - 返回正在運行中的協程 函數 coroutine.running 用于返回正在運行中的協程,如果沒有協程運行,則返回 nil ~~~ print(coroutine.running()) co = coroutine.create(function() print(coroutine.running()) print(coroutine.running() == co) end) coroutine.resume(co) print(coroutine.running()) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/05.md#resume-yield-交互)resume-yield 交互 下面代碼放在一個 lua 文件里運行,隨便輸入一些字符后按回車,則會返回輸出剛才輸入的內容 ~~~ function receive(prod) local status, value = coroutine.resume(prod) return value end function send(x) coroutine.yield(x) end function producer() return coroutine.create(function() while true do local x = io.read() send(x) end end) end function filter(prod) return coroutine.create(function() -- for line = 1, math.huge do for line = 1, 5 do local x = receive(prod) x = string.format("%5d Enter is %s", line, x) send(x) end end) end function consumer(prod) -- repeat -- local x = receive(prod) -- print(type(x)) -- if x then -- io.write(x, "\n") -- end -- until x == nil while true do local obtain = receive(prod) if obtain then io.write(obtain, "\n\n") else break end end end p = producer() f = filter(p) consumer(f) ~~~
                  <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>

                              哎呀哎呀视频在线观看