【问题标题】:Luaj: How to Import or Require a Lua Function LibraryLuaj:如何导入或需要 Lua 函数库
【发布时间】:2015-12-12 09:29:10
【问题描述】:

在 Java 中 LuaJ library 我想知道如何通过 Java 的 lua 闭包调用另一个 lua 脚本中的函数的 lua 脚本。例如这不起作用:

public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
    Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
    Globals luaScriptStandardGlobals = JsePlatform.standardGlobals();
    luaScriptStandardGlobals.loadfile("mycoolmathfunctions.lua");
    LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, luaScriptStandardGlobals);
    return luaClosure.call();
}

而且这里的输入流是指另一个lua的内容:

import 'mycoolmathfunctions'
-- or maybe require mycoolmathfunctions ?

return sum({1, 2, 3})
-- or maybe mycoolmathfunctions.sum({1, 2, 3}) ?

我该怎么做?

【问题讨论】:

    标签: java lua luaj


    【解决方案1】:

    在 Java LuaJ 库中,我想知道如何在另一个由 lua 闭包通过 Java 调用的 lua 脚本中要求或导入函数的 lua 脚本。

    您可以将 Lua 库作为资源放入 Java 包中。然后在需要另一个 lua 脚本的 lua 脚本上,您 require 它们相对于您的包路径。

    这是一个例子:

    这是我们的import-me.lua:

    -- make our sample module table global
    my_imported = {}
    
    function my_imported.printHello()
        print "Hello!"
    end
    
    return my_imported
    

    然后将其导入我们的sample-that-imports.lua:

    require "com.example.import-me"
    
    my_imported.printHello()
    

    然后我们在 SampleMain Java 类中运行 sample-that-imports.lua:

    package com.example;
    ...
    public class SampleMain {
    
        public static void main(String[] args) {
            Globals globals = JsePlatform.standardGlobals();
    
            // Again, we load the lua scripts relative to our package path
            LuaValue chunk = globals.loadfile("com/example/sample-that-imports.lua");
            chunk.call();
    
            // We could even use our imported library here
            chunk = globals.load("my_imported.printHello()");
            chunk.call();
        }
    }
    

    现在回答你的其他问题,

    例如这不起作用……

    我在您的 Java 代码中注意到您假设调用 loadfile() 会自动运行您的 lua 脚本。此外,您假设loadfile() 用于加载您的lua 模块。但是,这不是它应该使用的方式。

    您的loadfile() 应该能够返回一个LuaValue,您需要call() 来运行脚本本身。您甚至可以安全地将其转换为 LuaClosure,因为这是 loadfile() 实际返回的内容。

    要修复上面的 Java 代码,您可以使用它,

    public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
        Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
        Globals globals = JsePlatform.standardGlobals();
        LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, globals);
        return luaClosure.call();
    }
    

    我将在上面的代码中假设您已经在使用上述方法传递的InputStream(包含一个lua 脚本)中使用了require。如果没有,您可以进行以下更改:

    public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
        Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
        Globals globals = JsePlatform.standardGlobals();
    
        LuaValue chunk = globals.load("require 'com.example.import-me';");
        chunk.call();
    
        LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, globals);
        return luaClosure.call();
    }
    

    在上述更改中,我假设您的 lua 模块(在我们的示例中为 import-me.lua)自动为自己创建一个全局空间(在我们的示例中为 my_imported 表)。如果没有,你可以做最后的润色:

    ...
    LuaValue chunk = globals.load("my_imported = require 'com.example.import-me';");
    ...
    


    您还应该重用您的Globals(由JsePlatform.standardGlobals() 返回),除非您真的想在每次调用您的方法时创建一个新的Globals 表。此外,如果您真的不需要InputStream,而只是想从文件路径(或 Java 包路径中的资源路径)加载文件本身,则可以将所有内容简化为:

    public static LuaValue runLuaFile(Globals globals, String luafile) {
        return globals.loadfile(luafile).call();
    }
    

    或者保证我们的lua模块总是被我们的lua脚本导入(或者已经require'd),

    public static LuaValue runLuaFile(Globals globals, String luafile) {
        LuaValue chunk = globals.load("require 'com.example.import-me';");
        chunk.call();
        chunk = globals.loadfile(luafile);
        return chunk.call();
    }
    

    同样,您必须为我们的 lua 文件指定完整的资源路径。这是使用我们上面的简化方法的示例 Java sn-p:

    Globals globals = JsePlatform.standardGlobals();
    runLuaFile(globals, "com/example/sample-that-imports.lua");
    

    我希望这会有所帮助!


    编辑:

    您在 cmets 中提到需要从 InputStreams 导入 lua 模块。有两种方法可以实现:

    1. 第一个是加载和运行你需要的lua模块,比如简单的lua脚本——如果你需要的lua模块只兼容lua的require机制,你会有很多面临的问题。
    2. 第二种、最简单、最有效的方法是简单地加载模块,将其放在 lua 表 package.preload 中,并以键作为名称进行映射(供 require 使用)。

    我们将使用上面的第二种方法,因为这正是 lua 的require 机制真正想要的。以下是使用 LuaJ 实现它的方法:

    public static void preloadLuaModule(Globals globals, String modname, InputStream is) {
        LuaValue module = globals.load(is, modname, "bt", globals);
        globals.get("package").get("preload").set(modname, module);
    }
    

    上述实用程序方法预加载了一个InputStream 以供require 使用。这是一个示例用法:

    在一切开始的地方,我们初始化东西:

    ...
    preloadLuaModule(globals, "sample_module", sampleModuleInputStream);
    ...
    

    而我们上面的sampleModuleInputStream是一个lua模块,内容如下:

    -- make our sample module table global
    sample_module = {}
    
    function sample_module.printHi()
        print "Hi!"
    end
    
    return sample_module
    

    然后我们可以简单地在任何我们喜欢的地方使用require "sample_module",无论是在 Lua 脚本中还是在使用 LuaJ 的 Java 中:

    globals.get("require").call("sample_module");
    

    【讨论】:

    • 谢谢杰森!问题,这些 lua 脚本不在我们的 Java 代码库中,而是我想将它们作为文本数据从数据库中导入。我可以手动将脚本源代码传递给全局变量以使其可用于其他脚本(作为输入流传递吗?)
    • @DavidWilliams 当然可以。但是你必须确保你有一个只加载一次的机制。 require 的优点是它只加载和运行 lua 模块一次。你可以阅读 require 的 lua 文档,关于在 package.preload[] 表中安装自定义加载器。你也可以在 LuaJ 中做同样的事情;请参阅 PackageLib 类的 LuaJ 文档。
    • 嗨 Jason,"bt" 在 preloadLuaModule 函数中是什么意思?
    • 哦,我看到了二进制文本。
    猜你喜欢
    • 2012-02-15
    • 2012-06-30
    • 2019-10-12
    • 2016-04-29
    • 2014-01-31
    • 2013-01-08
    • 2016-01-09
    • 2014-10-22
    • 2013-04-06
    相关资源
    最近更新 更多