退休的 Lua 常见问题解答

lua-users home
wiki

[以下内容中的一部分可能仍然可以被利用]

Lua 编程

如何声明变量?

你不需要:Lua 没有也不需要变量声明。

但是为什么你需要声明变量?

有些人想要声明变量来保护自己免受打字错误的影响。例如,代码

color="red"
print(colour)

将打印 nil 因为第 2 行中 color 被误写为 colour,而 colour 没有被定义。

为了防止这些错误,以及为了发现未定义的变量,你可以使用 getglobal 标签方法

function safe_getglobal(x)
  local v=rawgetglobal(x)
  if v then return v else error("undefined global variable "..x) end
end

settagmethod(tag(nil),"getglobal",safe_getglobal)

有了这段代码,任何尝试使用未定义变量(其值为 nil)的行为都会产生错误消息。

如何在 Lua 中进行类型检查?

Lua 是一种动态类型语言。这意味着变量没有类型,只有值有类型。由于 Lua 提供了访问值类型的方法(通过 type 函数),你可以编写自己的函数来进行动态类型检查。以下是一种可能的方案。

要检查简单类型,只需使用 type。有趣的是检查表,因为我们必须检查所有字段是否存在并根据预定义的“模板”正确填充。因此,我们需要一种方法来描述表的“类型”。如上所述,简单类型由其名称描述,如 type 函数报告的那样。表由“类型模板”描述,这些模板是将每个字段映射到其所需类型的表。以下是一些对用户界面工具包有用的示例

TNumber="number"
TPoint={x=TNumber, y=TNumber}
TColor={red=TNumber, blue=TNumber, green=TNumber}
TRectangle={topleft=TPoint, botright=TPoint}
TWindow={title="string", bounds=TRectangle, color=TColor}

有了这些描述,以下函数检查一个值是否具有给定的类型

function checkType(d, t)
  if type(t) == "string" then
    -- t is the name of a type
    return (type(d) == t)
  else
    -- t is a table, so d must also be a table
    if type(d) ~= "table" then
      return nil
    else
      -- d is also a table; check its fields
      local i,v = next(t,nil)
      while i do
        if not checkType(d[i],v) then
          return nil
        end
        i,v = next(t,i)
      end
    end
  end
  return 1
end

如何创建只读变量?

在大型项目中,以及在库中,有时需要保护某些变量,以防止它们被重新定义。这在加载多个库时特别有用,你希望确保它们不会意外地重新定义彼此的函数。

你可以使用 setglobal 标签方法来保护 Lua 中的函数

function protect(x)
  error("cannot redefine "..x)
end

settagmethod(tag(protect),"setglobal",protect)

这段代码应该在所有库加载完毕后运行。

有了这段代码,任何尝试重新定义函数的行为都会产生错误消息。你仍然可以重新定义函数,但你必须显式地进行操作

rawsetglobal("print",nil)
function print (x) ... end

要将此方案扩展到其他类型的值,你需要用表包装值,并使用 setglobalgetglobal 标签方法

RO=newtag()				-- tag for read-only values

function ROvalue(value)			-- make a read-only value
  local t={value=value}
  settag(t,RO)
  return t

end

function ROsetglobal(x)			-- protects assignment
  error(x.." is read-only")
end

function ROgetglobal(x,value)		-- get the actual value
  return value.value
end

settagmethod(RO,"getglobal",ROgetglobal)
settagmethod(RO,"setglobal",ROsetglobal)

tolerance=ROvalue(0.12)			-- make some read-only variables
color=ROvalue("red")
myprint=ROvalue(print)

此方案仅影响使用ROvalue创建的变量,因为只有它们具有RO标记。

Lua 中如何进行块注释?

Lua 5.0 现在具有块注释语法。

--Double-dashes begin a single-line comment

--[[Double-dashes immediately followed by double left square bracket
    Begin a multi-line comment
    That continues until the __matching__ double right square bracket,
    so you can do [[this]] too. ]]
以下关于块注释的信息适用于 5.0 之前的版本。

Lua 没有专门的块注释语法。一种解决方案是将注释包含在字符串中。

comment = [[
    this is my
    multi-line comment
]]

此解决方案的一种变体,对于临时禁用代码块很方便,如下所示。

comment = [[
    disabled code
--]]

您可以通过在赋值中添加--来启用代码。

--comment = [[
    enabled code
--]]

如果块很大,您可能希望避免让它在内存中漂浮,直到垃圾收集器将其删除。然后,您可以编写。

local comment = [[
    disabled code
--]] ; comment = nil

Luiz Henrique de Figueiredo 还建议。

do local C=[[
--]] end

"C 立即超出范围。解析器可以利用这一点(但没有)。还有:"

local C= 1 or [[
<syntaxly correct code to comment out>
--]]

"代码生成器可以利用这一点(但没有)。"

另一种解决方案是使用编辑器宏将选定行加上前缀--;或--~,例如,使注释块在周围的常规注释中脱颖而出。

对于注释掉代码块,可以使用if nil then ... end。(参见下一个问题。)

如何克隆对象?

在 Lua 中,表是通过引用操作的。因此,如果x的值是一个表,那么赋值y=x不会复制该表:xy包含相同的表。(换句话说,xy的值是同一个表的引用。)

如果您真的想复制一个表,您可以使用内置函数next,如下面的代码所示。

function clone(t)            -- return a copy of the table t
  local new = {}             -- create a new table
  local i, v = next(t, nil)  -- i is an index of t, v = t[i]
  while i do
    new[i] = v
    i, v = next(t, i)        -- get next index
  end
  return new
end

如果您想要深层复制,请在new[i] = v之前添加if type(v)=="table" then v=clone(v) end

如何使用表实现集合?

在 Lua 中实现集合的最佳方法是将集合的元素存储为表中的键。当表中相应的键值不为nil时,元素存在于集合中。

以下是集合的一些代码片段。

s={}			-- create an empty set
s[x]=1			-- insert element x into set s
s[x]=nil		-- remove element x from set s
x_is_present=s[x]	-- does s contain x?

包类似于集合,只是元素可能出现多次。包的实现方式与集合类似,但使用与元素关联的值作为其计数器。以下是包的代码片段。

-- insert element x into bag s
if s[x] then s[x] = s[x]+1 else s[x] = 1 end

-- remove element x from set s
if s[x] then
  s[x] = s[x]-1
  if s[x] == 0 then s[x] = nil end
end

如何将数字转换为字符?

从 Lua 5.0 开始,使用string.char(n)或甚至string.format("%c", n)。在 Lua 5.0 之前,使用strchar(n)format("%c",n)

如何读取单词、数字、带引号的字符串等?

[需要更新!]

read的模式匹配功能很强大,但编写模式并非适合所有人。以下是一些有用的模式。

Word (sequence of letters and digits)
        {{"%w%w*"}}
Word (sequence of non-white space characters)
        {{"%S%S*"}}
Integer
        {{"[+-]?%d%d*"}}
Real
        {{"[+-]?%d%d*[.]%d%d*"}}
Double quoted string
        {{'"[^"]*"'}}
Single quoted string
        {{"'[^']*'"}}

如果你想在读取项目之前跳过空格,只需在你的模式前加上"{%s*}"。例如,"{%s*}%S%S*" 跳过空格,然后读取下一个单词。

3.2 版本使这变得更加简单:你可以使用"*w"来读取一个单词,使用"*n"来读取一个数字。有关预定义模式的完整列表,请参阅参考手册。

如何在 Lua 中编写一个自我复制程序?

在任何语言中编写一个自我复制程序都是很有趣的,但并不总是容易的,因为你必须小心处理引号。在 Lua 中,这很容易,因为有替代的引号。

y = [[ print("y = [[" .. y .. "]]\ndostring(y)") ]]
dostring(y)

版本说明:以上是 Lua 4.0。

有关其他语言中的自我复制程序,请参阅 Quine 页面 [1]。还可以阅读关于自我复制程序工作原理的良好解释 [2]

如何保存/恢复全局环境?

[需要更新!]

试试这段代码

function save()
  local g={}
  foreachvar(function (n,v) %g[n]=v end)
  return g
end

function restore(g)
  foreach(g,setglobal)
end

从 3.1 版本开始,你可以在 C 中使用lua_openlua_closelua_setstate来完成这些操作,以及更多操作。

C 与 Lua 的接口

如何编写一个从 Lua 调用的 C 函数?

[需要更新!]

你必须遵循一个简单的协议:使用lua_getparam从 Lua 获取任何参数,使用相应的lua_is... 函数确保它们是正确的类型,使用相应的lua_get... 函数转换它们,对转换后的值进行一些操作,并使用相应的lua_push... 函数将结果推回 Lua。

让我们看一个实际的例子,并将getenv函数导出到 Lua。有关更多示例,请参阅实现标准库的代码。

C 中的getenv函数接受一个字符串并返回另一个字符串。它的原型是

char* getenv(char*);

因此,要调用的适当函数是lua_isstringlua_getstringlua_pushstring

void wrap_getenv(void) {
 lua_Object o=lua_getparam(1);
 if (lua_isstring(o))
  lua_pushstring(getenv(lua_getstring(o)));
 else
  lua_error("string expected in argument #1 to getenv");
}

从 3.0 版本开始,Lua 包含辅助函数,这些函数简化了编写包装器的过程。使用这些函数,我们可以将wrap_getenv编写为

void wrap_getenv(void) {
 lua_pushstring(getenv(luaL_check_string(1)));
}

编写完包装器后,你必须通过调用lua_register("getenv",wrap_getenv)使其对 Lua 可见。事实上,这几乎与标准库所做的完全相同。

如何使用我喜欢的 C 库与 Lua?

只需为你的库中的每个函数编写一个包装器,如上所述。

提供了多种解决方案来自动执行此过程。请参阅 LuaAddons 上的“代码包装器”。

什么是“堆栈大小溢出”?

版本说明:这是 Lua5 之前的版本。

除了琐碎的情况(例如无限递归)之外,当你从 C 中循环调用 Lua 时,你会收到此消息,因为参数和返回值会在 Lua 的堆栈中累积。例如,这段代码将产生“堆栈大小溢出”

 for (i=0;;i++) {
  lua_pushnumber(i);
  lua_call("print");
 }

解决方法是在与 Lua 交互的代码周围加上lua_beginblocklua_endblock的调用

 for (i=0;;i++) {
  lua_beginblock();
  lua_pushnumber(i);
  lua_call("print");
  lua_endblock();
 }

引用参考手册:强烈建议使用显式嵌套块。


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2009 年 6 月 3 日下午 1:28 GMT (差异)