--元表:元表是用来存放对table操作方式的表,当要对一个表进行各种操作(查询,加减等)就会到它的元
--  表找到对应的元方法,元方法存放要获取数据和操作方法

--"元方法":  __add, __sub,__mul,__div,__eq(等于),__lt(小于),__le(小于等于)
	-- __unm(相反数)、__mod(取模)、__pow(乘幂)、__concat(连接操作符)
		-- __tostring(print时调用) ,__metatable(设置后不可修改元表)

print("----------------")
--第1步: 定义“原始表”
tab_1={10,20,50}
tab_2={35,70,80}

tab_3={num1=10,num2=20,num3=50}
tab_4={num1=50,num2=60,num3=8}

--测试
--tabRes=tab_1+tab_2                    --两个表直接做算数运算,会导致报错。

--第2步: 定义“元表”(核心计算)

setTable={}                             --定义一个“元表”
--定义“元表”的加法函数
-- 参数:
--  tab1 是原始表1, tab2 是原始表2
function setTable.Adding(tab1,tab2)
    local addTableRes={}                --返回结果表

    for i, v in pairs(tab1) do
        if(v==nil) then
            break
        end
        addTableRes[i]=tab1[i]+tab2[i]
    end

    return addTableRes
end

--定义“元表”的减法函数
-- 参数:
--  tab1 是原始表1, tab2 是原始表2
function setTable.Sub(tab1,tab2)
    local subTableRes={}                --返回结果表

    for i, v in pairs(tab1) do
        if(v==nil) then
            break
        end
        subTableRes[i]=tab1[i]-tab2[i]
    end

    return subTableRes
end

----第3步: 设置元方法
setTable.__add=setTable.Adding          --Adding函数,作为“__add” 的实际执行的元方法
setTable.__sub=setTable.Sub

--第4步: 设置元表
setmetatable(tab_1,setTable)
--setmetatable(tab_2,setTable)
setmetatable(tab_3,setTable)            --测试“键值对”类型的表
--setmetatable(tab_4,setTable)

--第5步: 测试输出
tabResult1=tab_1+tab_2                 --测试表之间的“加法”
tabResult2=tab_1-tab_2                 --测试表之间的“减法”
tabResult3=tab_3+tab_4                   --测试表之间的“加法”(属于键值对类型的table)
----输出结果
for i = 1, #tabResult1 do
    print(tabResult1[i])
  end

  for i = 1, #tabResult2 do
    print(tabResult2[i])
  end

----输出结果(表是键值对类型)
for i, v in pairs(tabResult3) do
    print(i,v)
end

print("----------------")





--[[-
--第1步: 定义“原始表”  ]]
--tab_1={10,20,50,300,200}
tab_2={str1="Unity脚本",str2="C#语言",str3="Lua语言"}

--测试
--print(tab_1)                            --没有定义“元方法”无法直接打印
--print(tab_2)

----第2步: 定义“元表”(核心计算)
local setTable={}

----设置直接打印的元方法
----参数:tab
----     表示需要进行处理的“原始表”
function setTable.ToString(tab)
    local tabResult={}                  --返回结果表

    for i, v in pairs(tab) do
    	 --开始#tabResult=0,循环中长度慢慢增加
        tabResult[#tabResult+1]=v
    end
    return table.concat(tabResult, ",")
end

----第3步: 设置元方法
setTable.__tostring=setTable.ToString
--规定tab_2的元表不允许被修改成其他元表和获取(不能使用getmetatable获得元表 )
setTable.__metatable="不能修改受保护的元表('Don't modify protected metatable !')"

----第4步: 设置元表
setmetatable(tab_2,setTable)

------第5步: 测试输出
print(tab_2)
--测试, 再次做设置元表
 --setmetatable(tab_2,{})
--print(getmetatable(tab_2))
--print("测试修改元表后的输出结果: ")
 --print(tab_2)



--Table访问元方法
--__index(查询table):当访问一个table中不存在的字段时,会促使解释器去元表查找一个叫__index的元方法.
--如果没有这个元方法,那么访问结果就是nil,否则就由这个元方法来提供最终的结果。

--__newindex(给table新字段赋值): 
--当对一个table中不存在的索引赋值时,解释器就会查找元表的__newindex元方法。
--如果有这个元方法,解释器就调用它,而不是执行赋值。

------__index
tabTable={str1="师长",str2="军长"}

--定义元表核心函数
local setTable={}
--提示信息
function setTable.tipNotFinding()
    return "您查询Table中没有对应的记录!"
end
function tipNotFinding()
    return "您查询Table中没有对应的记录!"
end

--设置元方法
setTable.__index=setTable.tipNotFinding
---设置原始表对应的元表
--setmetatable(tabTable,setTable)
--(简化形式)
setmetatable(tabTable,{__index=tipNotFinding})
--
setmetatable(tabTable,{ __index={ str3="abcd" }  })

----测试
print(tabTable["str1"])   --output: “师长”
print(tabTable["str5"])   --output: nil
print(tabTable["str3"])   --output: abcd

-------__newindex
--[[--学习Table访问的元方法: “__newindex”元方法(作用: 给一个表新字段赋值,触发本元方法)]]
tabTable={str1="科长",str2="处长"}

--定义元表的实现函数
function AddValue(tab,k,v)
    print("检测到你给Table添加了新的数据 k="..k.." v="..v)
end

--定义元表元方法
setmetatable(tabTable,{__newindex=AddValue})
----测试
print(tabTable["str1"])   --output: “科长”
tabTable["str3"]="厅长"    --添加了元方法 ,添加元素没成功
print(tabTable["str3"])   --output:  "厅长"



--[[--学习Table访问的元方法: “__newindex”元方法
      增加学习 "rawset()"  函数]]
tabTable={str1="科长",str2="处长"}

--定义元表的实现函数
function AddValue(tab,k,v)
    --rawset(t,k,v)方法可以绕过元方法直接对table进行赋值。配合“__newindex”元方法一起使用
    --元表函数继续执行。
    rawset(tab,k,v)
    print("检测到你给Table添加了新的数据 k="..k.." v="..v)
end

--定义元表元方法
setmetatable(tabTable,{__newindex=AddValue})

--测试
print(tabTable["str1"])   --output: “科长”
tabTable["str3"]="厅长"    --给一个Table 新字段赋值,是成功的。
print(tabTable["str3"])   --output:  "厅长"



--[[--学习Table访问的元方法: “__newindex”元方法, 再次举例]]
myTable=setmetatable(
    {key1="Value"},
    {
        __newindex=function(mytable,key,value)
            rawset(myTable,key,"---"..value.."----")
            print("__newindex 核心处理函数被执行了")
        end
    }
)
myTable.key1="new Value"
myTable.key2=88

----测试输出
print(myTable.key1,myTable.key2)   --output: new value    88





--[[--学习Table 访问元方法:'__call']]
---- __call (调用表的时候使用):
--原始表
tab1={}
--元表核心处理函数
function InvokeMethod()
    print("本元表被调用")
end
----设置元表元方法
setmetatable(tab1,{__call=InvokeMethod})
--测试
--表加括号
print(tab1())    --相当于一个Table 被执行。即: 就是元方法核心处理函数,成为了表中的一个函数。


--原始表
tab1={10,20}
--元表核心处理函数
function InvokeMethod(tab,num1,num2)
    print("本元表被调用")
    return num1+num2
end
--设置元表元方法
setmetatable(tab1,{__call=InvokeMethod})
--测试
-- 表被当做方法来调用
print("调用的结果=",tab1(tab1[1],tab1[2]))