乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      理解 Lua 的那些坑爹特性

       quasiceo 2014-01-16

      理解 Lua 的那些坑爹特性

       

      按:最近看到了依云的文章,一方面,為L(zhǎng)ua被人誤解而感到十分難過,另一方面,也為我的好友,依云沒有能夠體會(huì)到Lua的絕妙和優(yōu)雅之處而感到很遺憾,因此我寫了這篇文章,逐條款地說明了依云理解中出現(xiàn)的一些問題。希望能夠幫助到大家!

      1. 協(xié)程只能在Lua代碼中使用

          是的,協(xié)程在當(dāng)你需要掛起一個(gè)C函數(shù)的時(shí)候無法使用。但是,在提出這個(gè)缺陷的時(shí)
      候,是不是應(yīng)該想一想:為什么Lua會(huì)有這個(gè)缺陷?
          原因很簡(jiǎn)單:這一點(diǎn)完全避不開,這是C的問題,C無法保存函數(shù)執(zhí)行的某個(gè)現(xiàn)場(chǎng)用于
      返回這個(gè)現(xiàn)場(chǎng)繼續(xù)執(zhí)行,因此完全沒有辦法在Lua的協(xié)程被喚醒的時(shí)候,回到這個(gè)現(xiàn)場(chǎng)。
          那么怎么辦呢?Lua5.2做出了很優(yōu)秀的設(shè)計(jì)。既然無法回到C的現(xiàn)場(chǎng),那么我們不回
      去了,而是采取“事件通知”的方式告訴你,“hey哥們,你前面的邏輯被切了,想辦法
      補(bǔ)救吧”,這就是所謂的CPS——繼續(xù)風(fēng)格的編程。繼續(xù)在這里是一個(gè)Scheme/Lisp術(shù)
      語,意思是“當(dāng)前的操作執(zhí)行完了以后,下面該做什么?”這種風(fēng)格是Lua能支持任意
      Yield 的必要條件。在C的限制下,只有這一種方法能突破這個(gè)限制。
          至于你說的“比異步回調(diào)更復(fù)雜”,我想你弄混了兩點(diǎn):1.這只是C API層面的修改
      ,完全不影響到Lua代碼層面,你的Lua代碼完全不必做出任何修改,而且,你對(duì)
      coroutine的用法完全錯(cuò)了!等會(huì)兒我會(huì)教你coroutine到底怎么用。2.上面提到了,
      這是唯一一種能支持coroutine的方式,既然是唯一一種,就無所謂復(fù)雜與否了。3.
      我下面會(huì)演示給你,為什么說coroutine完全解放了程序員,使用coroutine的代碼會(huì)帶來
      革命性的簡(jiǎn)化。
          我們分兩步來說明這個(gè)問題:第一步,我們先來看你的例子:你想做的事情是,在執(zhí)
      行 c.callback的時(shí)候,能夠yield出來,繼續(xù)其他的流程。這里必須要說明,你的API設(shè)
      計(jì)本身就是callback式的,因此這種API本身就犯不著coroutine,Lua本身能完全地處理
      。這里我會(huì)給出一個(gè)支持coroutine的C模塊設(shè)計(jì),讓這個(gè)模塊能支持coroutine,第二步
      ,我會(huì)告訴你coroutine實(shí)際上是用在什么方面的,是如何取代事件回調(diào)機(jī)制的。在完成
      這個(gè)說明后,我們來說明coroutine到底有什么好處,為什么說coroutine比事件回調(diào)機(jī)制
      有著 革命性的優(yōu)秀之處。
          你的例子是這樣的:
      co.lua
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      c = require('c')                
                                        
      co = coroutine.create(function()
        print('coroutine yielding')   
        c.callback(function()         
          coroutine.yield()           
        end)                          
        print('coroutine resumed')    
      end)                            
                                        
      coroutine.resume(co)            
      coroutine.resume(co)            
                                        
      print('the end')                
          先說一下,將模塊放到全局變量里通常不是一個(gè)好主意。所以第一行如果寫成
      1
      local c = require 'c'
          就更好了。
          其他的地方倒是沒什么需要修改的了。
          再看看你的C模塊代碼:
      c.c
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      #include<stdio.h>                                       
      #include<stdlib.h>                                      
      #include<lua.h>                                         
      #include<lualib.h>                                      
      #include<lauxlib.h>                                     
                                                                
      static int c_callback(lua_State *L){                    
        int ret = lua_pcall(L, 0, 0, 0);                      
        if(ret){                                              
          fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
          lua_pop(L, 1);                                      
          exit(1);                                            
        }                                                     
        return 0;                                             
      }                                                       
                                                                
      static const luaL_Reg c[] = {                           
        {"callback", c_callback},                             
        {NULL, NULL}                                          
      };                                                      
                                                                
      LUALIB_API int luaopen_c (lua_State *L) {               
        luaL_register(L, "c", c);                             
        return 1;                                             
      }                                                       
          首先,因?yàn)檫@是Lua的C模塊,所以你得聲明這的確是一個(gè)C模塊,應(yīng)該在
              #include <lua.h>
          之前加入這一行:
              #define LUA_LIB
          編譯的時(shí)候就可以用下面的命令行了:
              gcc -mdll -DLUA_BUILD_AS_DLL c.c -oc.dll
          然后,Lua5.2已經(jīng)沒有l(wèi)uaL_register函數(shù)了,因?yàn)長(zhǎng)ua不鼓勵(lì)將模塊設(shè)置到全局域,
      而luaL_register會(huì)做這件事。所以將這行改為:
              luaL_newlib(L, c);
          最后一點(diǎn)不是問題,只是一個(gè)小建議:Lua只是會(huì)用luaL_Reg里的內(nèi)容,但是卻不會(huì)
      保留里面的任何內(nèi)容,所以你可以直接將其放在luaopen_c里面,并去掉static,這樣可
      以節(jié)省一點(diǎn)內(nèi)存。
          我們來看看一個(gè)支持coroutine的C模塊應(yīng)該怎么寫:
      c.c
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      #include<stdio.h>                                                    
      #include<stdlib.h>                                                   
                                                                             
      #define LUA_LIB /* 告訴Lua,這是一個(gè)LIB文件 */                       
      #include<lua.h>                                                      
      #include<lualib.h>                                                   
      #include<lauxlib.h>                                                  
                                                                             
      static int c_cont(lua_State *L) {                                    
        /* 這里什么都不用做:因?yàn)槟愕脑瘮?shù)里面就沒做什么 */               
        return 0;                                                          
      }                                                                    
                                                                             
      static int c_callback(lua_State *L){                                 
        /* 使用 lua_pcallk,而不是lua_pcall */                             
        int ret = lua_pcallk(L, 0, 0, 0, 0, c_cont);                       
        if(ret) {                                                          
          fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));             
          lua_pop(L, 1);                                                   
          exit(1);                                                         
        }                                                                  
        /* 因?yàn)槟氵@里什么都沒做,所以c_cont里面才什么都沒有。如果這里需要做
         * 什么東西,將所有內(nèi)容挪到c_cont里面去,然后在這里簡(jiǎn)單地調(diào)用      
         * return c_cont(L);                                               
         * 即可。                                                          
         */                                                                
        return 0;                                                          
      }                                                                    
                                                                             
      static const luaL_Reg c[] = {                                        
        {"callback", c_callback},                                          
        {NULL, NULL}                                                       
      };                                                                   
                                                                             
      LUALIB_API int luaopen_c (lua_State *L) {                            
        /* 使用新的 luaL_newlib 函數(shù) */                                    
        luaL_newlib(L, c);                                                 
        return 1;                                                          
      }
          現(xiàn)在,你的例子可以完美運(yùn)行了:
      1
      2
      3
      4
      lua  -- co.lua    
      coroutine yielding
      coroutine resumed 
      the end           
          我們看到,讓C模塊支持yield是非常簡(jiǎn)單的:首先,你需要將lua_call/lua_pcall改
      成對(duì)應(yīng)的k版本,將函數(shù)其后的所有內(nèi)容剪切到對(duì)應(yīng)的cont函數(shù)里去,然后將原先的內(nèi)容
      改為return func_cont(L);即可。
          為什么要這么設(shè)計(jì)API?上面說了,這是為了解決C自身的問題,如是而已。
          現(xiàn)在我們來討論第二個(gè)問題:Lua的coroutine用在什么地方呢?
          假設(shè)我們要書寫游戲的登陸邏輯,我們需要干這樣的事情:
              1. 登陸游戲
              2. 獲取玩家角色數(shù)據(jù)
              3. 讓玩家移動(dòng)到上次退出前的坐標(biāo)
          如果是事件回調(diào)引擎,你會(huì)怎么設(shè)計(jì)API呢?可能是這樣的:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      function do_login(server)                                                
          server:login(function(data)                                          
              -- 錯(cuò)誤處理先不管,假設(shè)有一個(gè)全局處理錯(cuò)誤的機(jī)制(后面會(huì)提到,實(shí)際
              -- 上就是newtry/protect機(jī)制)                                    
              server:get_player_info(function(data)                            
                  player:move_to(data.x, data.y)                               
              end)                                                             
          end, "username", "password")                                         
      end
          看到了么?因?yàn)榈顷懶枰却W(wǎng)絡(luò)請(qǐng)求,而等待的時(shí)候你不能把事件循環(huán)給阻塞了,
      所以你不得不用回調(diào)機(jī)制,但是,一旦你一次要做幾件事情,回調(diào)立即就會(huì)讓你的代碼狼
      狽不堪。這還只是簡(jiǎn)單的順序代碼。如果是判斷或者是循環(huán)呢?我告訴你,上面的代碼是
      一個(gè)真實(shí)的例子,是我以前設(shè)計(jì)的手機(jī)網(wǎng)游里面關(guān)于登陸部分的實(shí)際例子,而另一個(gè)例子
      是在客戶端批量購(gòu)買N個(gè)道具!可以想象這會(huì)是一個(gè)很復(fù)雜的遞歸代碼了,而實(shí)際上你僅
      僅是想做for在做的事情而已!
          那么怎么辦呢?coroutine提供了解決這個(gè)問題的一個(gè)極端優(yōu)雅的辦法。我們想想最
      優(yōu)雅的設(shè)計(jì)會(huì)是什么樣子的:
      1
      2
      3
      4
      5
      function d_login(server)                 
          server:login("username", "password"
          local data = server:get_player_info()
          player:move_to(data.x, data.y)       
      end                                      
          是不是簡(jiǎn)單多了?慢著!看起來login等函數(shù)是阻塞的,這樣的阻塞難道不會(huì)阻塞事
      件循環(huán),導(dǎo)致界面僵死么?好!現(xiàn)在coroutine上場(chǎng)了!看看我們是如何實(shí)現(xiàn)login的!
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      local current                                         
      function server:login(name, password)                 
          assert(not current, "already send login message!")
          server:callback_login(function(data)              
              local cur = current                           
              current = nil                                 
              coroutine.resume(cur, data)                   
          end, name, password)                              
          current = coroutine.running()                     
          coroutine.yield()                                 
      end                                                   
          看到了嗎?login先用正常的方式調(diào)用了基于回調(diào)的callback_login,然后設(shè)置當(dāng)前
      在等待的coroutine為自身,最后yield掉自己。在回調(diào)執(zhí)行的時(shí)候,回調(diào)會(huì)resume那個(gè)上
      次被yield掉的coroutine,這樣就完美的支持了阻塞的語法并且還能夠滿足事件循環(huán)的約
      束!能夠重新整理程序的執(zhí)行流程,這就是coroutine的強(qiáng)大之處。最奇妙的是,在
      這個(gè)設(shè)計(jì)之中,回調(diào)中唯一會(huì)做的事情只有resume,而不是yield,這意味著**即使不修
      改一行代碼,現(xiàn)有的模型也可以完美支持這個(gè)模式**!
          可以看出將回調(diào)模式的函數(shù)改造成協(xié)程模式的函數(shù)是很簡(jiǎn)單的,我們甚至可以寫一個(gè)
      高階函數(shù)來做這件事:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      function coroutinize(f, reenter_errmsg)    
          local current                          
          return function(...)                   
              assert(not current, reenter_errmsg)
              f(function(...)                    
                  local cur = current            
                  current = nil                  
                  coroutine.resume(cur, ...)     
              end, ...)                          
              current = coroutine.running()      
              coroutine.yield()                  
          end                                    
      end                                        
          這樣,上面的login函數(shù)就很簡(jiǎn)單了:
              server.login = coroutinize(server.login)
          看到Lua在表達(dá)復(fù)雜邏輯時(shí)的巨大優(yōu)勢(shì)了嗎?coroutine機(jī)制同樣也是可以支持函數(shù)重
      入的:如果一個(gè)函數(shù)被調(diào)用多次,那么對(duì)應(yīng)被調(diào)用的回調(diào)調(diào)用時(shí),對(duì)應(yīng)的那個(gè)coroutine
      會(huì)被resume。至于如何實(shí)現(xiàn),就交給讀者作為練習(xí)了。提示:Programming in Lua這本書
      已經(jīng)說明了該如何去做。
          我們總結(jié)一下:
              1. coroutine無法穿越C邊界是C語言的固有缺陷,Lua無法在保持其代碼是Clean
                 C的前提下完成這個(gè)impossible的任務(wù)。
              2. 那么,要支持這個(gè)特性,就只有要求C模塊的編寫者能采用CPS的方式編程了
                 。當(dāng)然Lua的代碼可以完全不做任何修改。
              3. 而,coroutine很少需要在C函數(shù)內(nèi)部yield(可能有實(shí)際場(chǎng)景會(huì)需要,但事實(shí)
                 是在我所書寫的上萬行的Lua富coroutine的代碼中,完全沒有用到過這種策
                 略)。
              4. 如果你能深入了解coroutine,你會(huì)發(fā)現(xiàn)即使coroutine無法在C內(nèi)部yield,
                 coroutine依然可以展現(xiàn)其絕大多數(shù)的威力。
              5. Lua本身的設(shè)計(jì)可以讓Lua在表現(xiàn)極端復(fù)雜的邏輯關(guān)系時(shí)游刃有余。

      2. 幽靈一般的 nil

          我不否認(rèn),在我剛剛學(xué)習(xí)Lua的時(shí)候,我的確被nil坑過很多遍。我們先拋棄掉luaJIT
      關(guān)于NULL設(shè)計(jì)的問題(這個(gè)設(shè)計(jì)本身也是一種無奈,而且LuaJIT畢竟并不能完全繼承Lua
      作者對(duì)Lua的理念),先來看看nil究竟是什么——從nil中,我學(xué)習(xí)到了,在遇到坑爹特
      性之前,先不要急著抱怨,想想為什么作者會(huì)設(shè)計(jì)這么坑爹的特性。要么作者是比你低能
      的傻逼,要么這么設(shè)計(jì)就的確是有充分的考慮和不得已的苦衷的。這點(diǎn)你想到過嗎?
          nil是一個(gè)表示“沒有”的值。是的,就是真的“沒有”,因此nil本身就是一個(gè)幽靈
      ——它除了表示“這里沒有東西”以外,沒有其他的任何含義!它不是None(None是一個(gè)
      表示“空”的對(duì)象),它也不是NULL(NULL表示沒指向任何地方的指針——總所周知指針
      本身必定是有值的,哪怕那個(gè)值是NULL)。Lua的作者十分聰明的將“沒有”這個(gè)概念也
      引入了語言,并且還保持了語言的一致性:請(qǐng)問,將“沒有”存入一個(gè)表里面,它如果不
      消失,還能發(fā)生什么事呢?
          那么如何表示“空”或者“沒有指向任何地方的引用”呢??jī)蓚€(gè)辦法,你可以存入
      false,或者可以用下面這個(gè)巧妙的方法:
      1
      2
      3
      4
      5
      6
      7
      undefined = {}                             
      -- 指定一個(gè)全局變量存在,但不指向任何地方:
      a = undefined                              
      -- 判斷這個(gè)全局變量是否不指向任何地方:    
      if a == undefine then ... end              
      -- 徹底刪除這個(gè)變量:                      
      a = nil                                    
          看!Lua可以靈活的控制一個(gè)變量的實(shí)際作用域!讓一個(gè)變量真正的憑空消失掉!這
      一點(diǎn)即使是C或者C++都是做不到的(變量只有在作用域外才會(huì)消失),這也是Lua強(qiáng)大的
      靈活性的一個(gè)佐證。
          千萬不要弄錯(cuò)了,nil代表“沒有”,它不代表“空”,也不代表“沒有被初始化的
      值”,它只是沒有而已。因此它就應(yīng)該是幽靈般的。這是Lua的語言設(shè)計(jì)一致性帶來的優(yōu)
      勢(shì),而不是坑爹的特性。如果說學(xué)不會(huì)的特性就是坑爹的特性,那么是不是C語言的指針
      也是坑爹的特性呢?
          其次,各種庫定義的null值,本質(zhì)上是代表微妙但不同的東西。仔細(xì)地體會(huì)其中的不
      同,能讓你更得心應(yīng)手的使用那些庫。如果你在這些庫的互操作上感到困擾,請(qǐng)給庫的作
      者寫郵件抱怨:Lua有一個(gè)很熱情友好的社區(qū)!

      3. 沒有continue

          是的,Lua一直不肯加入continue。為什么呢?因?yàn)閞epeat until。而為什么強(qiáng)調(diào)“
      不添加不必要的特性”的Lua作者會(huì)舍棄掉“那么常見”的continue,卻保留不那么常見
      的repeat呢?是Lua的作者傻么?
          不是。這是經(jīng)過仔細(xì)設(shè)計(jì)的。我先告訴你答案,再仔細(xì)地分析:事實(shí)上,在加入
      repeat以后,continue是邏輯上不可能實(shí)現(xiàn)的,而repeat語句實(shí)現(xiàn)了一個(gè)用其他的特性完
      全無法取代的特性。
          注意看repeat的結(jié)構(gòu):
              repeat <block> until <exp>
          問題就在<exp>上了。Lua規(guī)定,<exp>是在<block>的作用域下計(jì)算的!這也就意味著
      1
      2
      local a = true          
      repeat a = false until a
          會(huì)是一個(gè)死循環(huán)!看到了么?這種“表達(dá)式在一個(gè)block內(nèi)的上下文計(jì)算”的循環(huán)特
      性,是無法被其他任何特性模擬的!這種特性導(dǎo)致Lua作者為了語言的完整性,不得不將
      repeat語句添加入Lua語言。
          那么continue呢?花開兩朵,各表一枝,我們先介紹一下Lua5.2的新特性:goto語句
      。lua5.2支持跳轉(zhuǎn)語句了!看看你之前的那個(gè)例子吧,在沒有continue的情況下,我改如
      何寫那個(gè)循環(huán)呢?答案是這樣:
      1
      2
      3
      4
      5
      6
      7
      for line in configfile do                
          if string.sub(line, 1, 1) == '#' then
              goto next                        
          end                                  
          parse_config(line)                   
          ::next::                             
      end                                      
          看上去有點(diǎn)小題大做,連goto都用上了!呵呵,其實(shí)還有一個(gè)細(xì)節(jié)你不知道哦,在
      Lua5.2里,甚至連break都沒有了!break語句只是goto break的一個(gè)語法糖而已。而
      break標(biāo)簽會(huì)被自動(dòng)插入到你的源代碼中。
          那么,你可能會(huì)問了,事已至此,為什么不也加個(gè)continue的語法糖呢?畢竟break
      都是語法糖了!好,我們?cè)囍趓epeat里面用一下我們手寫的“continue”:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      local i                          
      repeat                           
          if i == 5 then               
              goto next                
          end                          
          local j = i * i              
          print("i = "..i..", j = "..j)
          i = i + 1                    
          ::next::                     
      until i == 10                    
          這個(gè)例子造的有點(diǎn)刻意了,但是至少也有continue的意思了吧!好,現(xiàn)在執(zhí)行一下—
      —出錯(cuò)了……
      1
      2
      3
      4
      lua  -- "noname\2013-01-03-1.lua"                                                       
      lua: noname\2013-01-03-1.lua:10: <goto next> at line 4 jumps into the scope of local 'j'
      shell returned 1                                                                        
      Hit any key to close this window...
          這是怎么回事??
          我們知道,在C里面,goto可以跳入任何地方,包括跳過變量的初始化,這會(huì)導(dǎo)致一
      個(gè)變量可能在用的時(shí)候,是未初始化的。Lua為了避免這樣的錯(cuò)誤,要求goto不允許跳入
      一個(gè)變量的作用域內(nèi)部!而看看我們的代碼,goto跳入了變量j的內(nèi)部!
          在這種情況下,是根本沒有辦法continue的!
          這就是Lua作者不提供continue的真實(shí)意思。continue是根本不可能實(shí)現(xiàn)的。因?yàn)閺?/div>
      完整性考慮,必須提供支持作用域擴(kuò)展的repeat語句(記得C/C++對(duì)for的三個(gè)子語句中的
      作用域規(guī)定么?),而continue可能會(huì)在repeat的條件表達(dá)式中用到的變量還沒有初始化
      的時(shí)候,就開始條件表達(dá)式的計(jì)算!這是不允許的!
          現(xiàn)在我們知道了:
              1. 不是Lua作者故意不提供continue,而是continue和當(dāng)前的一個(gè)關(guān)鍵特性沖突
                 了,導(dǎo)致continue語句完全無法實(shí)現(xiàn)。
              2. 為了彌補(bǔ)這個(gè)問題,Lua作者為L(zhǎng)ua加入了goto語句。然而該有的限制仍然存
                 在,但是編譯器會(huì)為你檢查這個(gè)限制!
              3. 所以,現(xiàn)在在Lua里的大多數(shù)情況下,你仍然能使用你自己手工打造的
                 continue,而且功能更為強(qiáng)大(labeled break,labeled continue都是可以
                 模擬出來的)。
          關(guān)于goto語言可能的壞處以及作者的考慮,請(qǐng)參考Lua作者的novelties-5.2.pdf文件。
          對(duì)了,還要說一句:有讀者可能為問:既然Lua已經(jīng)把break做成語法糖了,為什么不
      把continue也做成語法糖呢?如果遇到不合法的情況,直接出錯(cuò)不行么?
          這個(gè)問題我也沒想明白。也許會(huì)有自己的原因吧,不過如果把這個(gè)想法當(dāng)作Lua的坑
      爹設(shè)計(jì)也未嘗不可以,不過其“坑爹指數(shù)”已經(jīng)大為降低了。

      4. 錯(cuò)誤信息的表達(dá)

          我只想說一句話:其實(shí)大多數(shù)在預(yù)見到會(huì)對(duì)錯(cuò)誤進(jìn)行處理的場(chǎng)合里面,錯(cuò)誤的返回方
      式其實(shí)并不是nil, errmsg,而是nil, errmsg, errno。別的你懂了。

      5. 下標(biāo)

          參看 novelties-5.2.pdf,說的非常明白了。

      6. 提前返回

          這是一個(gè)語法問題,事實(shí)上return語句不跟著end的話,那么編譯器就根本無法編譯
      return語句了。這是Lua“行無關(guān)語法”的一個(gè)必然折衷,我開始也不爽,但事實(shí)是,在
      我數(shù)萬行的Lua開發(fā)中,除了測(cè)試必要要注釋一部分代碼以外,我根本沒用過do return
      end這種表達(dá)——至于為什么,你實(shí)際開發(fā)一下就知道了:因?yàn)檫@種代碼一定會(huì)導(dǎo)致完全
      無法被執(zhí)行到的死代碼。

      7. 方法調(diào)用

      8. 面向?qū)ο?/h4>
          這兩點(diǎn)恰好就是Lua的優(yōu)勢(shì)?。?!有時(shí)間我會(huì)寫一篇文章來討論。這實(shí)際上是Lua能以
      比其他語言小巧靈活得多地去處理復(fù)雜邏輯的一個(gè)必然原因了。這里只說一點(diǎn):Lua5.2中
      ,表所具有的元方法已經(jīng)和C API能處理的完全一樣多了。純Lua已經(jīng)不必對(duì)著__len和
      __gc而望洋興嘆了。
          關(guān)于這一點(diǎn),MikePall(LuaJIT實(shí)現(xiàn)者)還專門和Lua作者吵了一架,因?yàn)樽尡碇С?/div>
      __gc會(huì)導(dǎo)致luaJIT的jit編譯非常難寫= =||||

      9. 結(jié)論

          我開始學(xué)習(xí)Lua的時(shí)候,也幾乎得到了跟你一樣的結(jié)論。然而,在長(zhǎng)達(dá)兩年的Lua開發(fā)
      中,我逐漸認(rèn)識(shí)到了Lua的美,認(rèn)識(shí)到了Lua實(shí)現(xiàn)的優(yōu)雅和嚴(yán)謹(jǐn)?,F(xiàn)在如果有新手想學(xué)習(xí)C
      語言開發(fā)的訣竅和技巧,我通常會(huì)建議他去拜讀Lua的C實(shí)現(xiàn)源代碼。Lua的實(shí)現(xiàn)太優(yōu)雅了
      。而Lua的設(shè)計(jì)也凝聚著作者的一點(diǎn)一滴的心血。Lua精準(zhǔn)絕妙的設(shè)計(jì)是Lua強(qiáng)大的表達(dá)能
      力的表現(xiàn)。繼續(xù)學(xué)習(xí)下去吧,我向你保證,你一定會(huì)發(fā)現(xiàn),Lua實(shí)際上腳本語言里面表達(dá)
      能力最強(qiáng),概念最統(tǒng)一,設(shè)計(jì)最優(yōu)雅的語言了。Lua無愧腳本語言之王!

      Category: 未分類 | Tags: Lua | Read Count: 4979
      Small_feed 評(píng)論 (9)
      Avatar_small
      依云 說:
      大約 1 年前

      關(guān)于協(xié)程,我當(dāng)然知道協(xié)程該怎么用。Lua C API 確實(shí)有些細(xì)節(jié)上不太清楚,文檔太簡(jiǎn)略了。pcallk 只能解決一次 yield 吧?如果 yield 的次數(shù)不定該怎么辦?我有個(gè)庫的函數(shù),在運(yùn)行過程中可能需要調(diào)用一個(gè)回調(diào)函數(shù)來取某些數(shù)據(jù)。在 Lua 綁定中,這個(gè)回調(diào)函數(shù)就是調(diào)用一個(gè) Lua 函數(shù),然后由于涉及網(wǎng)絡(luò)操作,它是會(huì) yield 不定次數(shù)的。

      你說的所有這些,要么是 LuaJIT 2.0.0 還沒實(shí)現(xiàn)的特性(LuaJIT 比 Lua 快太多了),要么是要求作者對(duì) Lua 該怎么編程很熟悉(如果我接手的那些代碼是你寫的就好了)。至于表達(dá)能力,還是不要太強(qiáng)的好,不然每個(gè)人的錯(cuò)誤返回方式和面向?qū)ο蟮膶?shí)現(xiàn)都不一樣,概念是統(tǒng)一了,實(shí)現(xiàn)千差萬別、各不相容。

      沒錯(cuò),「do return end」就是調(diào)試時(shí)用的。

      Avatar_small
      亞彌 說:
      大約 1 年前

      @依云: 恩,說句實(shí)話,只看reference的確很難搞明白k系列函數(shù)內(nèi)部的核心思想。我是一開始就跟著郵件列表的討論才比較清楚的。不過你真的可以看看novelties-5.2.pdf,這里面有很詳細(xì)的說明。
      另外不明白“一次yield”和“多次yield”有什么區(qū)別。只要用了k系列函數(shù),你多少yield都沒問題的,因?yàn)長(zhǎng)ua自己會(huì)幫你維護(hù)Lua內(nèi)部yield時(shí)候的狀態(tài)。無論你如何yield,回到C層面(即從內(nèi)部的coroutine返回)只會(huì)有一次,因此k系列函數(shù)一定能做到你想要的,而且并不需要特別的設(shè)計(jì)。
      你仔細(xì)看看LuaJIT,很多特性已經(jīng)實(shí)現(xiàn)了,包括goto。k系列函數(shù)沒實(shí)現(xiàn)是基于兩個(gè)原因:1.LuaJIT關(guān)注純Lua應(yīng)用,甚至用ffi庫取代了C API的必要性;2.LuaJIT因?yàn)榕cLua作者的巨大分歧(郵件里面吵了好幾架),所以不打算實(shí)現(xiàn)5.2兼容了。至少短期內(nèi)是不想的。sigh……快的話,其實(shí)快不了多少,只是科學(xué)計(jì)算方面的確快了很多,如果你的代碼是C模塊密集的,那么LuaJIT很難提高效率,其次是如果你用了NYI的特性,那么也是不會(huì)快的(比如字符串模式匹配和coroutine),從我的經(jīng)驗(yàn)看,網(wǎng)游邏輯書寫用luaJIT對(duì)效率的提升不大,甚至可能比原Lua更慢。
      表達(dá)能力問題的確是個(gè)雙刃劍,但有個(gè)朋友說得好“做得到總比做不到好”,這個(gè)就看怎么解讀了。
      錯(cuò)誤返回是有標(biāo)準(zhǔn)模式的,文章里面提到了newtry/protect模式,不過寫到后來寫忘了= =有時(shí)間補(bǔ)上吧,OO的話也是有標(biāo)準(zhǔn)模式的,而且是兩套。關(guān)鍵是,因?yàn)榈讓痈拍罱y(tǒng)一,所以即使是千差萬別的實(shí)現(xiàn),最終也一定是兼容的。你如果處理過實(shí)現(xiàn)之間的糾葛就會(huì)體會(huì)到底層概念統(tǒng)一帶來的巨大好處。
      do return end的話也就是一個(gè)詞和三個(gè)詞的區(qū)別吧……sigh……就當(dāng)多打字了,實(shí)在不行做個(gè)imap或者iab唄……

      Avatar_small
      Wayne 說:
      12 個(gè)月前

      哇,偶像你也在這里?。?/p>

      Avatar_small
      Larry Xu 說:
      12 個(gè)月前

      newtry/protect??
      luasocket中的那套,我當(dāng)時(shí)看了也覺得蠻有意思的
      可否補(bǔ)充介紹下實(shí)際過程的使用方式

      Avatar_small
      4T-Shirt 說:
      10 個(gè)月前

      你好,能給我lua郵件列表的郵箱嗎?謝謝~~

      Avatar_small
      wtyqm 說:
      7 個(gè)月前

      您好,想問下,如果想在pcall里使用coroutine,有什么辦法嘛? 文中的protect,指的就是luaSocket里那種封裝pcall的方式吧

      Avatar_small
      太陽神上 說:
      5 個(gè)月前

      我覺得 lua 還有一個(gè)坑爹特性,table 當(dāng)哈希表時(shí),無法以 O(1) 的時(shí)間復(fù)雜度取得其元素個(gè)數(shù)。

      Avatar_small
      荒野無燈 說:
      2 個(gè)月前

      從lily那里過來的。看了你這文章,受益頗多。

      Avatar_small
      skyblue 說:
      大約 1 個(gè)月前

      求解釋coroutinize中這部分的必要性

      local cur = current
      current = nil

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多