可讓步迴圈

lua-users home
wiki

變更 Lua,讓「任何事物皆可讓步」這件事(可能有)需要,但卻是一個長期計畫。但在這同時,我覺得for迴圈中的反覆運算函數不能讓步是一件令人煩躁的事,這使得撰寫簡單的回應器迴圈變得雜亂無章,例如反覆運算器可能是非同步輸入函數。

不需要撰寫

for msg in eachmsg() do
  -- handle msg
end
-- end of messages, clean up

你需要

repeat
  local msg = getmsg()
  if msg == nil then break end
  -- handle msg
until false
-- end of messages, clean up

但要讓第一個程式碼範例運作非常簡單。只需將TFORLOOP VM 操作分解成兩個操作碼。第一個操作設定一個一般的 Lua 呼叫,然後進入 OP_CALL 實作。接下來的操作碼執行條件移動並根據第一個操作碼傳回的第一個值進行分支。

一些粗糙的測試似乎顯示這樣的變更實質上稍微改進了效能,雖然結果並不明確。我猜測這是因為 VM 可以無遞迴地處理呼叫,彌補了額外操作碼的額外負擔。

無論如何,修補程式在[失效連結]

(也可在此取得[1],取自 Google Code Search 快取[2]。)

([更新為 Lua 5.1.5])

範例

下面是一個測試程式。當中的關鍵函數是responder,它顯示了可讓步for運作的方式。測試輸出來在程式碼之後

local yield, resume, create, costatus =
  coroutine.yield, coroutine.resume, coroutine.create, coroutine.status
 
local function input(prompt)
  local inf, outf = io.stdin, io.stderr
  return function()
    outf:write(prompt," ")
    return inf:read()
  end
end
 
-- These could be quite a bit more complex
function eachmsg()
  return yield
end
 
-- This isn't actually used in this demo, but it could be :)
getmsg = coroutine.yield

-- This would probably be more complicated in a real app, too. 
function responder(name)
 local n = 0 
 print(name.." is born!")
 for msg in eachmsg() do
   n = n + 1
   if msg == "goodbye" then break
   else print(name.." heard "..msg)
   end
 end
 print(name.." departs this vale of tears, after listening to "..n.." utterances")
end
 
function driver()
 local cmd = {}
 local kids = {}
 -- the commands we understand
 function cmd.quit()   
   print "Exiting!"
   for _, kid in pairs(kids) do
     resume(kid)
   end
   return false
 end
 function cmd.kill(arg)
   local _, _, who = string.find(arg, "(%w+)")
   if not who then
     return "Kill who?"
   elseif not kids[who] then
     return who.."? I don't know any "..who
   else
     local status, result = resume(kids[who])
     kids[who] = nil
     if status then
       return
     else
       return result
     end
   end
 end
 function cmd.spawn(arg)
   local _, _, who = string.find(arg, "(%w+)")
   if not who then
     return "Spawn who?"
   elseif kids[who] then
     return who .. " already exists"
   else
     kids[who] = create(responder)
     local status, result = resume(kids[who], who)
     if not status then
       kids[who] = nil
       return result
     end    
   end
 end
 function cmd.list()
   print"Currently active:"
   for k in pairs(kids) do print("  "..k) end
 end
 
 -- main loop starts here --
 for msg in input("->") do
   local _, _, verb, rest = string.find(msg, "%s*(%w+)%s*(.*)")
   if cmd[verb] then
     local res = cmd[verb](rest)
     if res then print(res)
     elseif res == false then return
     end
   elseif kids[verb] then
     local status, result = coroutine.resume(kids[verb], rest)
     if not status then
       print(verb.." exited with error "..result)
       kids[verb] = nil
     elseif coroutine.status(kids[verb]) ~= "suspended" then
       print(verb.." decided to go away")
       kids[verb] = nil
     end
   else
     print "I don't understand what you're talking about"
   end
 end
end

範例執行

  
> driver()
-> list
Currently active:
-> spawn bob
bob is born!
-> spawn sally
sally is born!
-> bob hi
bob heard hi
-> sally hi
sally heard hi
-> bob meet sally
bob heard meet sally
-> fred hi
I don't understand what you're talking about
-> spawn fred
fred is born!
-> list
Currently active:
  sally
  fred
  bob
-> fred how are you
fred heard how are you
-> fred goodbye
fred departs this vale of tears, after listening to 2 utterances
fred decided to go away
-> kill bob
bob departs this vale of tears, after listening to 2 utterances
-> sally ?
sally heard ?
-> spawn sue
sue is born!
-> quit
Exiting!
sally departs this vale of tears, after listening to 2 utterances
sue departs this vale of tears, after listening to 0 utterances

-- RiciLake


RecentChanges · preferences
edit · history
最後編輯於 2012 年 5 月 20 日,凌晨 3:10 GMT (diff)