软件开发中的原表是啥呢?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件开发中的原表是啥呢?相关的知识,希望对你有一定的参考价值。

软件开发中的原表是什么呢?

参考技术A

给指定表设置元表。 (你不能在 L ua 中改变其它类型值的元表,那些只能在 C 里做。) 如果 metatable 是 nil, 将指定表的元表移除。 如果原来那张元表有 "__metatable" 域,抛出一个错误。
这个函数返回 table。
如果调用的时候没有 base, tonumber 尝试把参数转换为一个数字。 如果参数已经是一个数字,或是一个可以转换为数字的字符串, tonumber 就返回这个数字; 否则返回 nil。
字符串的转换结果可能是整数也可能是浮点数, 这取决于 Lua 的转换文法。 (字符串可以有前置和后置的空格,可以带符号。)
当传入 base 调用它时, e 必须是一个以该进制表示的整数字符串。 进制可以是 2 到 36 (包含 2 和 36)之间的任何整数。 大于 10 进制时,字母 'A' (大小写均可)表示 10 , 'B' 表示 11,依次到 'Z' 表示 35 。 如果字符串 e 不是该进制下的合法数字, 函数返回 nil。
可以接收任何类型,它将其转换为人可阅读的字符串形式。 浮点数总被转换为浮点数的表现形式(小数点形式或是指数形式)。 (如果想完全控制数字如何被转换,可以使用 string.format。)
如果 v 有 "__tostring" 域的元表, tostring 会以 v 为参数调用它。 并用它的结果作为返回值。
开始或继续协程 co 的运行。 当你第一次延续一个协程,它会从主体函数处开始运行。 val1, ... 这些值会以参数形式传入主体函数。 如果该协程被让出,resume 会重新启动它; val1, ... 这些参数会作为让出点的返回值。
如果协程运行起来没有错误, resume 返回 true 加上传给 yield 的所有值 (当协程让出), 或是主体函数的所有返回值(当协程中止)。 如果有任何错误发生, resume 返回 false 加错误消息。
返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
以字符串形式返回协程 co 的状态: 当协程正在运行(它就是调用 status 的那个) ,返回 "running"; 如果协程调用 yield 挂起或是还没有开始运行,返回 "suspended"; 如果协程是活动的,都并不在运行(即它正在延续其它协程),返回 "normal"; 如果协程运行完主体函数或因错误停止,返回 "dead"。
创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。 传给这个函数的参数都会作为 resume 的额外参数。 和 resume 返回相同的值, 只是没有第一个布尔量。 如果发生任何错误,抛出这个错误。

Lua中的元表与元方法

前言

Lua中每个值都可具有元表。元表是普通的Lua表,定义了原始值在某些特定操作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征。
例如,当数字值作为加法的操作数时,Lua检查其元表中的"__add"字段是否有个函数。如果有,Lua调用它执行加法。
我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。
可通过函数getmetatable查询任何值的元表。
在table中,我可以重新定义的元方法有以下几个:
 
   
   
 
__add(a, b) --加法
__sub(a, b) --减法
__mul(a, b) --乘法
__div(a, b) --除法
__mod(a, b) --取模
__pow(a, b) --乘幂
__unm(a) --相反数
__concat(a, b) --连接
__len(a) --长度
__eq(a, b) --相等
__lt(a, b) --小于
__le(a, b) --小于等于
__index(a, b) --索引查询
__newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
__call(a, ...) --执行方法调用
__tostring(a) --字符串输出
__metatable --保护元表
Lua中的每一个表都有其Metatable。Lua默认创建一个不带metatable的新表
 
   
   
 
t = {}
print(getmetatable(t)) --> nil
可以使用setmetatable函数设置或者改变一个表的metatable
 
   
   
 
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1)
任何一个表都可以是其他一个表的metatable,一组相关的表可以共享一个metatable(描述他们共同的行为)。一个表也可以是自身的metatable(描述其私有行为)。
接下来就介绍介绍如果去重新定义这些方法。

算术类的元方法

现在我使用完整的实例代码来详细的说明算术类元方法的使用。
Set = {}local mt = {} -- 集合的元表
-- 根据参数列表中的值创建一个新的集合function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return setend
-- 并集操作function Set.union(a, b) local retSet = Set.new{} -- 此处相当于Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSetend
-- 交集操作function Set.intersection(a, b) local retSet = Set.new{} for v in pairs(a) do retSet[v] = b[v] end return retSetend
-- 打印集合的操作function Set.toString(set) local tb = {} for e in pairs(set) do tb[#tb + 1] = e end return "{" .. table.concat(tb, ", ") .. "}"end
function Set.print(s) print(Set.toString(s))end
现在,我定义“+”来计算两个集合的并集,那么就需要让所有用于表示集合的table共享一个元表,并且在该元表中定义如何执行一个加法操作。首先创建一个常规的table,准备用作集合的元表,然后修改Set.new函数,在每次创建集合的时候,都为新的集合设置一个元表。代码如下:
Set = {}local mt = {} -- 集合的元表
-- 根据参数列表中的值创建一个新的集合function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return setend
在此之后,所有由Set.new创建的集合都具有一个相同的元表,例如:
local set1 = Set.new({10, 20, 30})local set2 = Set.new({1, 2})print(getmetatable(set1))print(getmetatable(set2))assert(getmetatable(set1) == getmetatable(set2))
最后,我们需要把元方法加入元表中,代码如下:
 
   
   
 
mt.__add = Set.union
这以后,只要我们使用“+”符号求两个集合的并集,它就会自动的调用Set.union函数,并将两个操作数作为参数传入。比如以下代码:
local set1 = Set.new({10, 20, 30})local set2 = Set.new({1, 2})local set3 = set1 + set2Set.print(set3)
在上面列举的那些可以重定义的元方法都可以使用上面的方法进行重定义。现在就出现了一个新的问题,set1和set2都有元表,那我们要用谁的元表啊?虽然我们这里的示例代码使用的都是一个元表,但是实际coding中,会遇到我这里说的问题,对于这种问题,Lua是按照以下步骤进行解决的:
  1. 对于二元操作符,如果第一个操作数有元表,并且元表中有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就以这个字段为元方法,而与第二个值无关;
  2. 对于二元操作符,如果第一个操作数有元表,但是元表中没有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就去查找第二个操作数的元表;
  3. 如果两个操作数都没有元表,或者都没有对应的元方法定义,Lua就引发一个错误。
以上就是Lua处理这个问题的规则,那么我们在实际编程中该如何做呢?
比如set3 = set1 + 8这样的代码,就会打印出以下的错误提示:
 
   
   
 
lua: test.lua:16: bad argument #1 to 'pairs' (table expected, got number)
但是,我们在实际编码中,可以按照以下方法,弹出我们定义的错误消息,代码如下:
function Set.union(a, b) if getmetatable(a) ~= mt or getmetatable(b) ~= mt then error("metatable error.") end
local retSet = Set.new{} -- 此处相当于Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSetend
当两个操作数的元表不是同一个元表时,就表示二者进行并集操作时就会出现问题,那么我们就可以打印出我们需要的错误消息。
上面总结了算术类的元方法的定义,关系类的元方法和算术类的元方法的定义是类似的,这里不做累述。

__tostring元方法

写过Java或者C#的人都知道,Object类中都有一个tostring的方法,程序员可以重写该方法,以实现自己的需求。在Lua中,也是这样的,当我们直接print(a)(a是一个table)时,是不可以的。那怎么办,这个时候,我们就需要自己重新定义tostring元方法,让print可以格式化打印出table类型的数据。
函数print总是调用tostring来进行格式化输出,当格式化任意值时,tostring会检查该值是否有一个tostring的元方法,如果有这个元方法,tostring就用该值作为参数来调用这个元方法,剩下实际的格式化操作就由__tostring元方法引用的函数去完成,该函数最终返回一个格式化完成的字符串。例如以下代码:
 
   
   
 
mt.__tostring = Set.toString

如何保护我们的“奶酪”——元表

我们会发现,使用getmetatable就可以很轻易的得到元表,使用setmetatable就可以很容易的修改元表,那这样做的风险是不是太大了,那么如何保护我们的元表不被篡改呢?在Lua中,函数setmetatable和getmetatable函数会用到元表中的一个字段,用于保护元表,该字段是metatable。当我们想要保护集合的元表,是用户既不能看也不能修改集合的元表,那么就需要使用metatable字段了;当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable则会引发一个错误;如以下演示代码:
function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end mt.__metatable = "You cannot get the metatable" -- 设置完我的元表以后,不让其他人再设置 return setend
local tb = Set.new({1, 2})print(tb)
print(getmetatable(tb))setmetatable(tb, {})
上述代码就会打印以下内容:
 
   
   
 
{1, 2}
You cannot get the metatable
lua: test.lua:56: cannot change a protected metatable

__index元方法

是否还记得当我们访问一个table中不存在的字段时,会返回什么值?默认情况下,当我们访问一个table中不存在的字段时,得到的结果是nil。但是这种状况很容易被改变;Lua是按照以下的步骤决定是返回nil还是其它值得:
  1. 当访问一个table的字段时,如果table有这个字段,则直接返回对应的值;
  2. 当table没有这个字段,则会促使解释器去查找一个叫__index的元方法,接下来就就会调用对应的元方法,返回元方法返回的值;
  3. 如果没有这个元方法,那么就返回nil结果。
下面通过一个实际的例子来说明__index的使用。假设要创建一些描述窗口,每个table中都必须描述一些窗口参数,例如颜色,位置和大小等,这些参数都是有默认值得,因此,我们在创建窗口对象时可以指定那些不同于默认值得参数。
Windows = {} -- 创建一个命名空间
-- 创建默认值表Windows.default = {x = 0, y = 0, width = 100, height = 100, color = {r = 255, g = 255, b = 255}}
Windows.mt = {} -- 创建元表
-- 声明构造函数function Windows.new(o) setmetatable(o, Windows.mt) return oend
-- 定义__index元方法Windows.mt.__index = function (table, key) return Windows.default[key]end
local win = Windows.new({x = 10, y = 10})print(win.x) -- >10 访问自身已经拥有的值print(win.width) -- >100 访问default表中的值print(win.color.r) -- >255 访问default表中的值
根据上面代码的输出,结合上面说的那三步,我们再来看看,print(win.x)时,由于win变量本身就拥有x字段,所以就直接打印了其自身拥有的字段的值;print(win.width),由于win变量本身没有width字段,那么就去查找是否拥有元表,元表中是否有index对应的元方法,由于存在index元方法,返回了default表中的width字段的值,print(win.color.r)也是同样的道理。
在实际编程中,__index元方法不必一定是一个函数,它还可以是一个table。当它是一个函数时,Lua以table和不存在key作为参数来调用该函数,这就和上面的代码一样;当它是一个table时,Lua就以相同的方式来重新访问这个table,所以上面的代码也可以是这样的:
 
   
   
 
-- 定义__index元方法
Windows.mt.__index = Windows.default

__newindex元方法

newindex元方法与index类似,newindex用于更新table中的数据,而index用于查询table中的数据。当对一个table中不存在的索引赋值时,在Lua中是按照以下步骤进行的:
Lua解释器先判断这个table是否有元表;
  1. 如果有了元表,就查找元表中是否有__newindex元方法;如果没有元表,就直接添加这个索引,然后对应的赋值;
  2. 如果有这个__newindex元方法,Lua解释器就执行它,而不是执行赋值;
  3. 如果这个__newindex对应的不是一个函数,而是一个table时,Lua解释器就在这个table中执行赋值,而不是对原来的table。
那么这里就出现了一个问题,看以下代码:
 
   
   
 
local tb1 = {}
local tb2 = {}

tb1.__newindex = tb2
tb2.__newindex = tb1

setmetatable(tb1, tb2)
setmetatable(tb2, tb1)

tb1.x = 10
发现什么问题了么?是不是循环了,在Lua解释器中,对这个问题,就会弹出错误消息,错误消息如下:
 
   
   
 
loop in settable


以上是关于软件开发中的原表是啥呢?的主要内容,如果未能解决你的问题,请参考以下文章

设计增强现实移动应用程序的最佳工具是啥呢?

PLM软件主要的作用是啥呢?

程序开发中遇到的lua语言概念是啥呢?

在JavaScript中 top为上边 那么它相对的下边是啥呢

sql server 有很多相关版本 请问具体区别是啥呢?

计算机开发中常说的加载器是啥呢?