Lua 中的引用问题

我正在尝试在 Lua 中创建一个引用函数,以便我可以将参数用作字符串,但不需要用引号引起来,或在某些环境中访问它们。就像这个问题的第二个评论中所述 (https://stackoverflow.com/questions/2747445/quasiquote-quote-in-lua)

w = print
function test()
   local function _ix( _ , k )
w( "              _ix \\ " , _ , k )
            local v = rawget( _G , k )
w( "              <-- " , k )
         return k
         end
  local _ = setmetatable( {} , { __index = _ix } )
  local function q( _ )    return _  end
        q = setfenv( q , _ )
return q
end

所以,当我运行它时:

q = test()
w( "q( uno )" , q( uno ) )

它甚至没有调用 __index 元方法:

---------- Capture Output ----------

q( uno )    nil

> Terminated with exit code 0.

那么,我做错了什么?

点赞
用户2505965
用户2505965

如果我理解正确的话,那么你尝试做的事情没有太多意义。uno会在调用q的环境中被查找,而不是与其调用的环境。在你的例子中,这类似于调用q(nil)。问题中的例子可以正常工作,因为它们在同一全局环境中工作。

你可以编写一个帮助函数来拦截当前环境中的nil查找,但必须在任何想要使用这些nilstring查找的环境中预先调用它。

local function intercept (tab)
    setfenv(2, setmetatable(tab or {}, {
        __index = function (_, key)
            return key
        end
    }))
end

你需要一个环境克隆函数,除非你想手动创建每个沙盒,否则你可能会搞砸父环境(例如_G)。你可以将此逻辑移动到intercept中,以获得更清晰的函数调用,但灵活性会减少。

local function clone_current_env ()
    local env = {}

    for key, value in pairs(getfenv(2)) do
        env[key] = value
    end

    return env
end

将它们组合在一起,你可以使任何环境中的nil查找变为字符串。

intercept(clone_current_env())
print(type(foo), type(bar)) --> string string

这是一些丑陋的元编程,我真的不知道为什么你想写这样的代码,除了作为一个概念验证。


一个完整的示例。

DEMO

local function clone (tab)
    local new = {}

    for key, value in pairs(tab) do
        new[key] = value
    end

    return new
end

local function enable_nil_strings ()
    setfenv(2, setmetatable(clone(getfenv(2)), {
        __index = function (env, key)
            return key
        end
    }))
end

local function disable_nil_strings()
    setmetatable(getfenv(2), nil)
end

-----------------------------------------------------

print(type(foo), type(bar)) --> nil nil
enable_nil_strings()
print(type(foo), type(bar)) --> string string
disable_nil_strings()
print(type(foo), type(bar)) --> nil nil

最后,实现这个的最好方法可能是简单地在执行上下文中包装一下:

local function with_nil_strings (context, ...)
    local env = {}

    for key, value in pairs(getfenv(2)) do
        env[key] = value
    end

    setfenv(
        context,
        setmetatable(env, {
            __index = function (_, key) return key end
        })
    )

    context(...)
end

print(type(foo)) --> nil

with_nil_strings(function ()
    print(type(foo)) --> string
end)

print(type(foo)) --> nil
2016-10-27 09:25:40