Lua的"主要"功能?

22 lua

在python中,通常会定义一个main函数,以便允许脚本用作模块(如果需要):

def main():
    print("Hello world")
    return 0

if __name__ == "__main__":
    sys.exit(main())
Run Code Online (Sandbox Code Playgroud)

在Lua中,这样的成语if __name__ == "__main__"是不可能的(这意味着,我认为不是这样).

这就是我为了在Lua中有类似行为而通常所做的事情:

os.exit((function(args)
    print("Hello world")
    return 0
end)(arg))
Run Code Online (Sandbox Code Playgroud)

......但这种方法似乎相当"重要的括号":-)

是否有更常见的方法(除了定义一个看似多余的全局主函数)?

Mat*_*ttJ 13

没有"正确"的方法来做到这一点,因为Lua并没有真正区分代码来自哪里,它们都只是函数.也就是说,这至少似乎在Lua 5.1中有效:

matthew@silver:~$ cat hybrid.lua 
if pcall(getfenv, 4) then
    print("Library")
else
    print("Main file")
end
matthew@silver:~$ lua hybrid.lua 
Main file
matthew@silver:~$ lua -lhybrid
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid"
Library
> ^C
matthew@silver:~$
Run Code Online (Sandbox Code Playgroud)

它的工作原理是检查堆栈深度是否大于3(库存Lua解释器中文件的正常深度).此测试可能会破坏Lua版本,甚至在任何嵌入式/自定义Lua版本中也是如此.

我还将包括这个(稍微更便携)的替代方案,虽然它在启发式方面取得了更大的飞跃,并且有一个失败案例(见下文):

matthew@silver:~$ cat hybrid2.lua 
function is_main(_arg, ...)
    local n_arg = _arg and #_arg or 0;
    if n_arg == select("#", ...) then
        for i=1,n_arg do
            if _arg[i] ~= select(i, ...) then
                print(_arg[i], "does not match", (select(i, ...)))
                return false;
            end
        end
        return true;
    end
    return false;
end

if is_main(arg, ...) then
    print("Main file");
else
    print("Library");
end
matthew@silver:~$ lua hybrid2.lua 
Main file
matthew@silver:~$ lua -lhybrid2
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua 
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid2"
Library
>
Run Code Online (Sandbox Code Playgroud)

这个通过比较_G.arg的内容和'...'的内容来工作.在主要块中,它们将始终相同.在模块中,_G.arg仍将包含命令行参数,但"..."将包含传递给require()的模块名称.我怀疑这是更接近更好的解决方案,因为你知道你的模块名称.此代码中的错误在于用户使用1参数执行主脚本时,这是模块的确切名称:

matthew@silver:~$ lua -i hybrid2.lua hybrid2
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
Main file
> require "hybrid2"
Main file
> 
Run Code Online (Sandbox Code Playgroud)

鉴于上述情况,我希望至少你知道你的立场,即使它不是你想到的:)

更新:对于在Lua 5.1和5.2中运行的hybrid.lua版本,可以使用debug.getlocal替换getfenv:

if pcall(debug.getlocal, 4, 1) then
    print("Library")
else
    print("Main file")
end
Run Code Online (Sandbox Code Playgroud)


Stu*_*ley 7

当Lua requiresa模块时,它将它require与varargs(...)一起传递给它.

因此,如果您的脚本不打算采用任何参数(从命令行或其他方式),您可以使用类似的东西

if ... then
  return this_mod --module case
else
  main() --main case
end
Run Code Online (Sandbox Code Playgroud)

但是,请注意,在您采用参数的(完全)可能的情况下,这并非万无一失.但是,在这一点上,你可以将它与Lukasz的答案结合起来得到:

if not package.loaded[...] then
  --main case
else --module case
end
Run Code Online (Sandbox Code Playgroud)

仍然不完美(例如,如果使用第一个参数string或其他已经加载的模块的名称调用脚本),但可能已经足够好了.在其他情况下,我会按照MattJ的回答.


Łuk*_*ner 6

您可以尝试检查是否需要该模块.

来自文件:

package.loaded require用来控制已加载哪些模块的表.当您需要模块modname且package.loaded [modname]不为false时,require只返回存储在那里的值.

有了这个你可以写:

if not package.loaded['modulename'] then
    main()
end
Run Code Online (Sandbox Code Playgroud)