Goto in LuaJIT

7.6k 词

Lua 在 5.2 之后的版本,加入了 goto 这个关键字,用来控制程序跳转到指定 label。我们可以利用这个特性,来模拟 continue 的实现。需要注意的是 goto 只能跳转到 label,而 ::name:: 的格式就可以设置一个 label

for i=1,5 do
    if i == 3 then
        goto continue
    end
    print(i)
    ::continue::
end

这样就简单实现了 continue。但是有些同学可能会有疑问,这是 Lua 5.2 的特性,我们都知道 OR 中使用的 Lua 5.1,那如何使用?

其实我们只需要使用 LuaJIT 就可以解决这个问题了。在 LuaJIT 的主页上有这个介绍:

LuaJIT supports some language and library extensions from Lua 5.2. Features that are unlikely to break existing code are unconditionally enabled:

  • goto and ::labels::.

也就是说 LuaJIT 把 5.2 的这个特性拿过来了。那我们来验证下:

[root@master:~]# cd /usr/local/openresty/luajit/bin/
[root@master:bin]# ./luajit
LuaJIT 2.1.0-beta1 -- Copyright (C) 2005-2015 Mike Pall. http://luajit.org/
JIT: ON SSE2 SSE3 SSE4.1 fold cse dce fwd dse narrow loop abc sink fuse
>
> for i=1,5 do
    if i == 3 then
        goto continue
    end
    print(i)
    ::continue::
end>> >> >> >> >> >>
1
2
4
5
>

继续验证 OR :

[root@master:demo]# nl app/app.lua
     1	local lor = require("lor.index")
     2	local app = lor()
 <span class="mi">3</span>	<span class="n">app</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;/index&#34;</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">req</span><span class="p">,</span><span class="n">res</span><span class="p">,</span><span class="nb">next</span><span class="p">)</span>
 <span class="mi">4</span>	    <span class="n">res</span><span class="p">:</span><span class="n">send</span><span class="p">(</span><span class="s2">&#34;hello&#34;</span><span class="p">)</span>
 <span class="mi">5</span>	<span class="k">end</span><span class="p">)</span>

 <span class="mi">6</span>	<span class="n">app</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;goto&#34;</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">req</span><span class="p">,</span><span class="n">res</span><span class="p">,</span><span class="nb">next</span><span class="p">)</span>
 <span class="mi">7</span>	    <span class="k">for</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="k">do</span>
 <span class="mi">8</span>	        <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">3</span> <span class="k">then</span>
 <span class="mi">9</span>	            <span class="n">goto</span> <span class="n">continue</span>
<span class="mi">10</span>	        <span class="k">end</span>
<span class="mi">11</span>	        <span class="n">ngx</span><span class="p">.</span><span class="n">say</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="mi">12</span>	        <span class="p">::</span><span class="n">continue</span><span class="p">::</span>
<span class="mi">13</span>	    <span class="k">end</span>
<span class="mi">14</span>	<span class="k">end</span><span class="p">)</span>

<span class="mi">15</span>	<span class="k">return</span> <span class="n">app</span>

[root@master:demo]#
[root@master:demo]# http :8888/goto
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain
Date: Fri, 02 Dec 2016 02:55:43 GMT
Server: openresty/1.9.7.4
Transfer-Encoding: chunked
X-Powered-By: Lor Framework

1
2
4
5

[root@master:demo]#

那么 goto 还有哪些玩法?尾递归优化使用 goto 来实现:

do
    local function fact(n)
        return fact_iter(n, 1)
    end
<span class="k">function</span> <span class="nf">fact_iter</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">now</span><span class="p">)</span>
    <span class="p">::</span><span class="n">call</span><span class="p">::</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="k">then</span>
        <span class="k">return</span> <span class="n">now</span>
    <span class="k">else</span>
        <span class="c1">-- return fact_iter(n-1, now*n)</span>
        <span class="n">n</span><span class="p">,</span> <span class="n">now</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">now</span><span class="o">*</span><span class="n">n</span>
        <span class="n">goto</span> <span class="n">call</span>
    <span class="k">end</span>
<span class="k">end</span>

<span class="nb">print</span><span class="p">(</span><span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">fact</span><span class="p">(</span><span class="mi">10000</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">fact</span><span class="p">(</span><span class="mi">10000000</span><span class="p">))</span>

end

然而感觉并没什么卵用。其他玩法需要各位同学自己去挖掘了。。不过需要注意的是 Lua 也有对 goto 的滥用限制:

  • A label is visible in the entire block where it is defined (including nested blocks, but not nested functions).
  • A goto may jump to any visible label as long as it does not enter into the scope of a local variable.
            <hr style="visibility: hidden;"/>