Lua
Lua语法
Lua 数据类型
Lua 是,变量不需要类型定义,只需要赋值变量。 该值可以作为参数传输或结果返回存储在变量中。
Lua 中有 8 基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
数据类型 | 描述 |
---|---|
nil | 这是最简单的,只有值nil属于这一类,表示无效值(相当于条件表达式false)。 |
boolean | 包括两个值:false和true。 |
number | 实浮点数表示双精度类型 |
string | 字符串由一对双引号或单引号表示。 2 个方括号 [[ ]] "来表示"一块"字符串。字符串连接使用 .. |
function | 由 C 或 Lua 编写的函数 |
userdata | 变量中任意存储的C数据结构 |
thread | 用于执行协同程序的独立线路 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。 Lua 里,table 创建是通过的"构造表达式"完成最简单的结构表达式{} ,用来创建一个空表。 |
通过type()
该方法可获得变量类型,返回值为的。
type(X) print("a" .. 'b') --字符串连接
Lua字符串
字符串或串(String)它是一串由数字、字母和下划线组成的字符。
Lua 以下三种方符串可以用以下三种方式串:
-
单引号之间的一串字符。
-
双引号之间的一串字符。
-
与 一串字符。
字符串转义
和其他语言一样,使用它\
字符串函数
-- 所有字符串都转换为大写字母。 string.upper(argument) -- 所有字符串都转换成小写字母。 string.lower(argument) --替换字符串。 ----mainString 要操作的字符串, findString 替换字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换) string.gsub(mainString,findString,replaceString,num) --在指定的目标字符串中搜索指定的内容(第三个参数为索引),并返回其具体位置。如果不存在,则返回 nil。 string.find (str, substr, [init, [end]]) --字符串反转 string.reverse(arg) --返回类似printf的格式化字符串 string.format(...) --char 将整形数字转换成字符并连接, byte 转换字符为整数值(可指定一个字符,默认为第一个字符)。 string.char(...) 和 string.byte(arg[,int])
--计算字符串长度。
string.len(arg)
--返回字符串string的n个拷贝(重复n次)
string.rep(string, n)
--链接两个字符串
..
--返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
string.gmatch(str, pattern)
--只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
--在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
string.match(str, pattern, init)
--截取字符串。j:截取结束位置,默认为 -1,最后一个字符。
string.sub(s, i [, j])
Lua 变量
变量在使用前,需要在代码中进行声明,即创建该变量。
Lua 变量有三种类型:、、。
Lua 中的变量(与JavaScript类似)
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的 nil
。
在Lua 语言中,,使用未经初始化的全局变量不会导致错误。当使用未经初始化的全局变量时,得到的结果为 。
当把nil赋值给全局变量时,Lua会回收该全局变量。
赋值语句
a = "hello" .. "world"
-- Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
a, b = 10, 2*x --> a=10; b=2*x
-- 遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
*索引
对 table 的索引使用方括号 []
。Lua 也提供了 .
操作。
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
语句控制
循环语句
--
while(condition)
do
statements
end
-- 数值循环:var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。
for var=exp1,exp2,exp3 do
<执行体>
end
--泛型for 循环。i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
a = {
"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
-- 类似其他语言 do ... while。
repeat
statements
until( condition )
--嵌套循环。前面几个循环的互相嵌套
while(condition)
do
while(condition)
do
statements
end
statements
end
if
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
--------------------------
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
------------------------
if( 布尔表达式 1)
then
--[ 布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2)
then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
end
函数
通过关键字 function
声明一个函数。return
语句返回结果。
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
说明:
optional_function_scope
: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字local
。function_name
: 指定函数名称。argument1, argument2, argument3..., argumentn
: 函数参数,多个参数以隔开,函数也可以不带参数。function_body
: 函数体,函数中需要执行的代码语句块。result_params_comma_separated
: 函数返回值,。
*多值返回
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({
8,10,23,12,5}))
*可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 表示函数有可变的参数。
function add(...)
local s = 0
for i, v in ipairs{
...} do --> {...} 表示一个由所有变长参数构成的数组 。inpairs没有使用括号,应该是语法编译。
s = s + v
end
return s
end
print(add(3,4,5,6,7)) --->25
-
通常在遍历变长参数的时候只需要使用 ,然而变长参数可能会包含一些 ,那么就可以用 函数来访问变长参数了: 或者
-
- 返回可变参数的长度。
- 用于返回从起点 开始到结束位置的所有参数列表。
- 返回可变参数的长度。
-
调用 select 时,必须传入一个固定实参 selector(开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 开始到结束位置的所有参数列表,否则只能为字符串 ,这样 select 返回变长参数的总数。
Lua运算符
算术运算符
设定 A 的值为10,B 的值为 20:
操作符 | 描述 | 实例 |
---|---|---|
+ | 加法 | A + B 输出结果 30 |
- | 减法 | A - B 输出结果 -10 |
* | 乘法 | A * B 输出结果 200 |
/ | 除法 | B / A 输出结果 2 |
% | 取余 | B % A 输出结果 0 |
^ |
乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果 -10 |
关系运算符
设定 A 的值为10,B 的值为 20:
操作符 | 描述 | 实例 |
---|---|---|
== | 等于,检测两个值是否相等,相等返回 true,否则返回 false | (A == B) 为 false。 |
~= | 不等于,检测两个值是否相等,不相等返回 true,否则返回 false | (A ~= B) 为 true。 |
> | 大于,如果左边的值大于右边的值,返回 true,否则返回 false | (A > B) 为 false。 |
< | 小于,如果左边的值大于右边的值,返回 false,否则返回 true | (A < B) 为 true。 |
>= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false | (A >= B) 返回 false。 |
<= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false | (A <= B) 返回 true。 |
逻辑运算符
设定 A 的值为 true,B 的值为 false:
操作符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
*其他运算符
操作符 | 描述 | 实例 |
---|---|---|
连接两个字符串 | a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。 | |
一元运算符,返回字符串或表的长度。 | #“Hello” 返回 5 |
运算符优先级
从高到低的顺序:
^
not - (unary)
* / %
+ -
.. ####################
< > <= >= ~= ==
and
or
*
a+i < b/2+1 --> (a+i) < ((b/2)+1)
5+x^2*8 --> 5+((x^2)*8)
a < y and y <= z --> (a < y) and (y <= z)
-x^2 --> -(x^2)
x^y^z --> x^(y^z) ::::右结合的:::,注意顺序
Lua数组
Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
* 一维数组
array = {
"Lua", "Tutorial"}
在 Lua 索引值是以 1
为起始,但你指定 0
开始。
还可以以为数组索引值。。
array = {
}
for i= -2, 2 do
array[i] = i *2
end
for i = -2,2 do
print(array[i])
end
--OUTPUT:
-4
-2
0
2
4
多维数组
就是类似于value是一个map的map。
-- 初始化数组
array = {
}
for i=1,3 do
array[i] = {
}
for j=1,3 do
array[i][j] = i*j
end
end
Lua迭代器
泛型 for 迭代器
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
for k, v in pairs(t) do
print(k, v)
end
泛型 for 的执行过程:
- 首先,初始化,计算 in 后面的值,表达式应该返回泛型 for 需要的三个值:、、;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
- 第二,将状态常量和控制变量作为调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
- 第三,将迭代函数返回的值赋给。
- 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
- 第五,回到第二步再次调用迭代函数
在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:
- 无状态的迭代器
- 多状态的迭代器
无状态的迭代器
无状态的迭代器是指的迭代器,因此在循环中我们可以利用无状态迭代器花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子是 ipairs
,它遍历数组的每一个元素,元素的索引需要是数值。
--迭代函数
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,3,0
do
print(i,n)
end
--OUTPUT:
1 1
2 4
3 9
迭代的状态包括被遍历的表(循环过程中不会改变的)和当前的索引下标(),ipairs 和都很简单,我们在 Lua 中可以这样实现:
--迭代函数。参数a:是状态常量,i:控制变量
function iter (a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
function ipairs (a)
return iter, a, 0
end
当 Lua 调用 ipairs(a)
开始循环时,他获取三个值:、、;然后 Lua 调用 iter(a,0) 返回 1
, a[1]
(除非 a[1]=nil);第二次迭代调用 iter(a,1) 返回 2, a[2]……直到第一个 nil 元素。
多状态的迭代器
迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table 作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函数通常不需要第二个参数。
array = {
"Google", "Runoob"}
function elementIterator (collection)
local index = 0
local count = #collection --collectioin 长度
-- 闭包函数
return function ()
index = index + 1
if index <= count
then
-- 返回迭代器的当前元素
return collection[index]
end
end
end
for element in elementIterator(array) -- elementIterator(array) 调用的返回值(闭包) ,作为一个迭代函数。
do
print(element)
end
--OUTPUT:
Google
Runoob
Lua table(表)
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,你可以用的值来作数组的索引,但这个值。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
table(表)的构造
-- 初始化表
mytable = {
}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
Table 操作
序号 | 方法 | 用途 |
---|---|---|
1 | table.concat (table [, sep [, start [, end]]]): | concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。 |
2 | table.insert (table, [pos,] value): | 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. |
3 | table.maxn (table) | 指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。() |
4 | table.remove (table [, pos]) | 返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 |
5 | table.sort (table [, comp]) | 对给定的table进行升序排序。 |
Lua其他
其他 Redis用不不涉及,就不整理了。
参考
https://www.runoob.com/lua/lua-strings.html
Redis
Redis中使用Lua的好处
- 减少网络开销。可以将多个请求通过脚本的形式,减少网络时延
- 。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
- 。客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
Redis Lua脚本与事务
从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。
使用事务时可能会遇上以下两种错误:
- 事务在执行 EXEC 之前,。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
- 命令可能在 失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。
对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。
经过测试lua中发生异常处理方式和redis 事务一致,可以说这两个东西是一样的,但是lua支持缓存,可以复用脚本,这个是原来的事务所没有的
Redis 相关命令
EVAL
EVAL script numkeys key [key …] arg [arg …]
script
参数是一段 Lua 5.1 脚本程序,它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。
numkeys
参数用于指定键名参数的个数。
键名参数 key [key ...]
从 EVAL
的参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS
数组,用 1
为基址的形式访问( KEYS[1]
, KEYS[2]
,以此类推)。
在命令的最后,那些不是键名参数的附加参数 arg [arg ...]
,可以在 Lua
中通过全局变量 ARGV
数组访问,访问的形式和 KEYS
变量类似( ARGV[1]
、 ARGV[2]
,诸如此类)。
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
EVALSHA
EVAL 命令要求在每次执行脚本的时候脚本主体(script body)。Redis 有一个内部的缓存机制,因此它不会每次都重新编译脚本,不过在很多场合,付出无谓的带宽来传送脚本主体并不是最佳选择。
为了减少带宽的消耗, Redis 实现了 EVALSHA
命令,它的作用和 EVAL 一样,都用于对脚本求值,但它接受的(sum)。
EVALSHA
命令的表现如下:
- 如果服务器还缓存了给定的 SHA1 校验和所指定的脚本,那么执行这个脚本
- 如果服务器未缓存给定的 SHA1 校验和所指定的脚本,那么它返回一个特殊的错误,提醒用户使用 EVAL 代替 EVALSHA
以下是示例:
> set foo bar
OK
> eval "return redis.call('get','foo')" 0
"bar"
> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"
> evalsha ffffffffffffffffffffffffffffffffffffffff 0
(error) `NOSCRIPT` No matching script. Please use [EVAL](/commands/eval).
客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ,并期望着要使用的脚本已经保存在服务器上了,只有当 NOSCRIPT
错误发生时,才使用 EVAL 命令重新发送脚本,这样就可以最大限度地节省带宽。
这也说明了执行 EVAL 命令时,使用正确的格式来传递键名参数和附加参数的重要性:因为如果将参数硬写在脚本中,那么每次当参数改变的时候,都要重新发送脚本,即使脚本的主体并没有改变,相反,通过使用正确的格式来传递键名参数和附加参数,就可以在脚本主体不变的情况下,直接使用 EVALSHA 命令对脚本进行复用,免去了无谓的带宽消耗。
SCRIPT FLUSH
清除Redis服务端所有 Lua 脚本缓存
script flush
SCRIPT EXISTS
给定一个或多个脚本的 SHA1 校验和,返回一个包含 0或 1 的列表,表示校验和所指定的脚本是否已经被保存在缓存当中
SCRIPT EXISTS sha1 [sha1 …]
SCRIPT LOAD
将脚本 script 添加到Redis服务器的脚本缓存中,并不立即执行这个脚本,而是会立即对输入的脚本进行求值。并返回给定脚本的 SHA1 校验和。如果给定的脚本已经在缓存里面了,那么不执行任何操作。
SCRIPT LOAD script
SCRIPT KILL
杀死当前的脚本
SCRIPT KILL
杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效。 这个命令主要用于终止运行时间过长的脚本,比如一个因为 BUG 而发生无限 loop 的脚本,诸如此类。
假如当前正在运行的脚本已经执行过写操作,那么即使执行SCRIPT KILL
,也无法将它杀死,因为这是违反 Lua 脚本的原子性执行原则的。在这种情况下,唯一可行的办法是使用SHUTDOWN NOSAVE
命令,通过停止整个 Redis 进程来停止脚本的运行,并防止不完整(half-written)的信息被写入数据库中。
Lua中执行redis命令
在 Lua 脚本中,可以使用两个不同函数来执行 Redis 命令,它们分别是:
redis.call()
redis.pcall()
这两个函数的在于它们使用不同的方式。
redis.call()
和 redis.pcall()
两个函数的参数可以是任何格式良好(well formed)的 Redis 命令:
> eval "return redis.call('set','foo','bar')" 0
OK
上面这段脚本的确实现了将键 foo
的值设为 bar
的目的,但是,它违反了 EVAL 命令的语义,因为脚本里使用的所有键都应该由 KEYS
数组来传递,就像这样:
> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK
要求使用正确的形式来传递键(key)是有原因的,因为不仅仅是 EVAL 这个命令,所有的 Redis 命令,,籍此来确定命令会对哪些键进行操作。
因此,对于 EVAL 命令来说,必须使用,才能确保分析工作正确地执行。除此之外,使用正确的形式来传递键还有很多其他好处,它的一个特别重要的用途就是确保 Redis 集群可以将你的请求发送到。(对 Redis 集群的工作还在进行当中,但是脚本功能被设计成可以与集群功能保持兼容。)不过,这条规矩并不是强制性的,从而使得用户有机会滥用(abuse) Redis 单实例配置(single instance configuration),代价是这样写出的脚本不能被 Redis 集群所兼容。
在 Lua 数据类型和 Redis 数据类型之间转换
当 Lua 通过 call()
或 pcall()
函数执行 Redis 命令的时候,命令的返回值会被转换成 Lua 数据结构。同样地,当 Lua 脚本在 Redis 内置的解释器里运行时,Lua 脚本的返回值也会被转换成 Redis 协议(protocol),然后由 EVAL 将值返回给客户端。
数据类型之间的转换遵循这样一个设计原则:如果将一个 Redis 值转换成 Lua 值,之后再将转换所得的 Lua 值转换回 Redis 值,那么这个转换所得的 Redis 值应该和最初时的 Redis 值一样。
换句话说, 。
以下列出的是详细的转换规则:
从 Redis 转换到 Lua :
Redis integer reply -> Lua number # Redis 整数转换成 Lua 数字
Redis bulk reply -> Lua string # Redis bulk 回复转换成 Lua 字符串
Redis multi bulk reply -> Lua table (may have other Redis data types nested) #Redis 多条 bulk 回复转换成 Lua 表,表内可能有其他别的 Redis 数据类型
Redis status reply -> Lua table with a single ok field containing the status #Redis 状态回复转换成 Lua 表,表内的 `ok` 域包含了状态信息
Redis error reply -> Lua table with a single err field containing the error #Redis 错误回复转换成 Lua 表,表内的 `err` 域包含了错误信息
Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type # Redis 的 Nil 回复和 Nil 多条回复转换成 Lua 的布尔值 `false`
从 Lua 转换到 Redis:
Lua number -> Redis integer reply # Lua 数字转换成 Redis 整数
Lua string -> Redis bulk reply # Lua 字符串转换成 Redis bulk 回复
Lua table (array) -> Redis multi bulk reply # Lua 表(数组)转换成 Redis 多条 bulk 回复
Lua table with a single ok field -> Redis status reply # 一个带单个 `ok` 域的 Lua 表,转换成 Redis 状态回复
Lua table with a single err field -> Redis error reply # 一个带单个 `err` 域的 Lua 表,转换成 Redis 错误回复
Lua boolean false -> Redis Nil bulk reply # Lua 的布尔值 `false` 转换成 Redis 的 Nil bulk 回复
从 Lua 转换到 Redis 有一条额外的规则,这条规则没有和它对应的从 Redis 转换到 Lua 的规则:
Lua boolean true -> Redis integer reply with value of 1 # Lua 布尔值 `true` 转换成 Redis 整数回复中的 `1`
示例:
#将 Lua 值转换成 Redis 值
> eval "return 10" 0
(integer) 10
> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
2) "Hello World!"
#将 Redis 值转换成 Lua 值,然后再将 Lua 值转换成 Redis 值的类型转过程。
> eval "return redis.call('get','foo')" 0
"bar"
错误处理