2019独角兽企业重金招聘Python工程师标准>>>
Lua 环境安装
Linux环境安装
选择你需要的Lua版本:http://www.lua.org/ftp/
curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz
tar zxf lua-5.3.4.tar.gz
cd lua-5.3.4
make linux test
make install
测试安装环境错误:
cd src && make linux
make[1]: Entering directory `/home/webapps/lua/lua-5.3.4/src'
make all SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl -lreadline"
make[2]: Entering directory `/home/webapps/lua/lua-5.3.4/src'
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_USE_LINUX -c -o lua.o lua.c
lua.c:82:31: 错误:readline/readline.h:没有那个文件或目录
lua.c:83:30: 错误:readline/history.h:没有那个文件或目录
lua.c: 在函数‘pushline’中:
lua.c:312: 警告:隐式声明函数‘readline’
lua.c:312: 警告:赋值时将整数赋给指针,未作类型转换
lua.c: 在函数‘addreturn’中:
lua.c:339: 警告:隐式声明函数‘add_history’
make[2]: *** [lua.o] 错误 1
make[2]: Leaving directory `/home/webapps/lua/lua-5.3.4/src'
make[1]: *** [linux] 错误 2
make[1]: Leaving directory `/home/webapps/lua/lua-5.3.4/src'
make: *** [linux] 错误 2
解决方案:
yum install libtermcap-devel ncurses-devel libevent-devel readline-devel
安装成功:
lua -v
Lua 5.3.4 Copyright (C) 1994-2017 Lua.org, PUC-Rio
编写测试代码:
vi lua_coding.lua --新建文件
print("Hello World Keep Coding!!!") --执行代码
lua lua_coding.lua --执行
Windows 环境安装
Window 系统上安装 Lua window下你可以使用一个叫"SciTE"的IDE环境来执行lua程序,下载地址为:
- 本站下载地址:LuaForWindows_v5.1.4-46.exe
- Github 下载地址:https://github.com/rjpcomputing/luaforwindows/releases
- Google Code下载地址 : https://code.google.com/p/luaforwindows/downloads/list 双击安装后即可在该环境下编写 Lua 程序并运行。
你也可以使用 Lua 官方推荐的方法使用 LuaDist:http://luadist.org/
国人开发IDEA Lua插件:https://github.com/tangzx/IntelliJ-EmmyLua
Lua语法
以下代码保存文件可以直接运行: 新建2个模块测试文件:TestMod.lua, TestLoad.lua
TestMod.lua 代码
local TestMod = {}
local function getname()
return "无忌"
end
function TestMod.Greeting()
print("Hello, My name is "..getname())
end
return TestMod
TestLoad.lua 代码
print('load...')
测试代码:
print("Hello World Keep Coding!!!")
--注释
--[[
多行注释
--]]
--[[
变量
NULL在Lua中是nil
lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。变量前加local关键字的是局部变量
--]]
theGlobalVar = 50 --全局变量
local theLocalVar = "local variable" --本地变量
-- 5种方式定义的变量,字符串相等
a1 = 'alo\n123"'
a2 = "alo\n123\""
a3 = '\97lo\10\04923"'
a4 = [[alo
123"]] -- 多行变量定义的方式
a5 = [===[
alo
123"]===] -- [=[之间多少个==都没关系
nullvar = null
print('---- 变量 ----')
print(a1 == a2)
print(a2 == a3)
print(a3 == a4)
print(a4 == a5)
print('nullvar == nil is',nullvar == nil)
print('---- 变量 ----')
print('---- 控制语句 ----')
print('---- if-else分支 ----')
io.write('请输入年龄:')
local age = io.read("*number")
io.read() -- 回车符
io.write('请输入性别(男|女):')
local sex = io.read()
if age == 40 and sex =="男" then
print("男人四十一枝花")
elseif age > 60 and sex ~="女" then
print("old man without country!")
elseif age <20 then
io.write("too young, too naive!\n")
else
print("Your age is "..age)
end
print(&#39;---- for循环 ----&#39;)
sum &#61; 0
for i &#61; 100, 1, -2 do
sum &#61; sum &#43; i
end
print("sum &#61;",sum)
print(&#39;---- while循环 ----&#39;)
-- Lua没有&#43;&#43;或是&#43;&#61;这样的操作
sum &#61; 0
num &#61; 1
while num <&#61; 100 do
sum &#61; sum &#43; num
num &#61; num &#43; 1
end
print("sum &#61;",sum)
print(&#39;---- until循环 ----&#39;)
sum &#61; 2
repeat
sum &#61; sum ^ 2 --幂操作
print(&#39;until循环 sum&#61;&#39;,sum)
until sum > 1000
print(&#39;---- 控制语句 ----&#39;)
print(&#39;\n---- 函数 与Javascript 写法类似 ----&#39;)
print(&#39;---- 递归 ----&#39;)
function fib(n)
if n <2 then return 1 end
return fib(n - 2) &#43; fib(n - 1)
end
print(fib(2))
print(&#39;---- 闭包 ----&#39;)
function newCounter()
local i &#61; 0
return function() -- anonymous function
i &#61; i &#43; 1
return i
end
end
c1 &#61; newCounter()
print(c1()) --> 1
print(c1()) --> 2
function myPower(x)
return function(y) return y^x end
end
power2 &#61; myPower(2)
power3 &#61; myPower(3)
print(&#39;4的2次方&#61;&#39;,power2(4)) --4的2次方
print(&#39;5的3次方&#61;&#39;,power3(5)) --5的3次方
--函数的返回值&#xff0c;可以一次返回多个值
function returnMultiParam()
return &#39;无忌&#39;, &#39;nassir.wen&#64;gmail.com&#39;, 40
end
print(&#39;方法返回多个参数&#xff1a;&#39;,returnMultiParam())
--局部函数 Javascript类似
function foo(x) return x^2 end
foo &#61; function(x) return x^2 end
print(&#39;---- Table对象(支持Array 和 Map结构) ----&#39;)
--所谓Table其实就是一个Key Value的数据结构&#xff0c;它很像Javascript中的Object&#xff0c;或是PHP中的数组&#xff0c;在别的语言里叫Dict或Map
arr &#61; {name&#61;&#39;无忌&#39;,age&#61;37,email&#61;&#39;nassir.wen&#64;gmail.com&#39;}
arr2 &#61; {10,20,30} -- 等价于 {[1]&#61;10,[2]&#61;20,[3]&#61;30}
arr3 &#61; {&#39;无忌&#39;,function(x) return x &#43; 1 end} -- 数组中可以定义不同类型&#xff0c;也可以定义方
-- 遍历数组
for k, v in pairs(arr) do
print(k, v)
end
for i &#61; 1, #arr2 do -- 数组下标从0开始, #arr2代表arr2长度
print(arr2[i])
end
print(&#39;调用数组中的方法:&#39;,arr3[2](1))
print(&#39;---- MetaTable 和 MetaMethod ----&#39;)
-- MetaTable和MetaMethod是Lua中的重要的语法&#xff0c;MetaTable主要是用来做一些类似于C&#43;&#43;重载操作符式的功能
fraction_a &#61; {numerator&#61;2, denominator&#61;3} --分数 2/3
fraction_b &#61; {numerator&#61;4, denominator&#61;7} --分数 4/7
-- 如果直接执行 fraction_a &#43; fraction_b 会报错&#xff0c;我们需要通过MetaTable处理
-- 使用MetaTable
fraction_op&#61;{}
function fraction_op.__add(f1, f2)
ret &#61; {}
ret.numerator &#61; f1.numerator * f2.denominator &#43; f2.numerator * f1.denominator
ret.denominator &#61; f1.denominator * f2.denominator
return ret
end
-- 为之前定义的两个table设置MetaTable&#xff1a;&#xff08;其中的setmetatble是库函数&#xff09;
setmetatable(fraction_a, fraction_op)
setmetatable(fraction_b, fraction_op)
fraction_s &#61; fraction_a &#43; fraction_b
print(&#39;MateTable 实现分数对象相加:&#39;, fraction_s.numerator, &#39;/&#39; , fraction_s.denominator)
print(&#39;---- 面向对象(有点像Javascript的prototype)----&#39;)
--[[ 面向对象的实现&#xff0c;主要是使用MetaMethod的__index重载&#xff0c;所谓__index&#xff0c;说得明确一点&#xff0c;如果我们有两个对象a和b&#xff0c;我们想让b作为a的prototype只需要
setmetatable(a, {__index &#61; b})
--]]
Person&#61;{}
function Person:new(p)
local obj &#61; p
if (obj &#61;&#61; nil) then
obj &#61; {name&#61;"无忌", age&#61;18, handsome&#61;true}
end
self.__index &#61; self
return setmetatable(obj, self)
end
function Person:toString()
return self.name .." : ".. self.age .." : ".. (self.handsome and "handsome" or "ugly")
end
--[[
1&#xff09;self 就是 Person&#xff0c;Person:new(p)&#xff0c;相当于Person.new(self, p)
2&#xff09;new方法的self.__index &#61; self 的意图是怕self被扩展后改写&#xff0c;所以&#xff0c;让其保持原样
3&#xff09;setmetatable这个函数返回的是第一个参数的值。
--]]
-- 测试
me &#61; Person:new()
print(me:toString())
kf &#61; Person:new{name&#61;"King&#39;s fucking", age&#61;70, handsome&#61;false}
print(kf:toString())
-- 继承&#xff0c;同样使用setmetatable
Student &#61; Person:new()
function Student:new()
newObj &#61; {year &#61; 2013}
self.__index &#61; self
return setmetatable(newObj, self)
end
function Student:toString()
return "Student : ".. self.year.." : " .. self.name
end
stu &#61; Student:new{name&#61;&#39;无忌&#39;}
print(&#39;Student 继承:&#39;,stu:toString())
print(&#39;---- 模块 ---- &#39;)
--[[
加载文件几种方式区别:
require("model_name") 载入相同文件只会执行一次
dofile("model_name") 载入相同文件每次都会执行
loadfile("model_name") 载入文件不执行&#xff0c;等你需要执行的时候再执行
3种方式测试: 文件 TestLoad.lua
print(&#39;load...&#39;)
--]]
print(&#39;---- 测试require加载3次 ----&#39;)
require(&#39;testload&#39;)
require(&#39;testload&#39;)
require(&#39;testload&#39;)
print(&#39;---- 测试require加载3次 ----&#39;)
print(&#39;---- 测试dofile加载3次 ----&#39;)
dofile(&#39;testload.lua&#39;)
dofile(&#39;TestLoad.lua&#39;)
dofile(&#39;TestLoad.lua&#39;)
print(&#39;---- 测试dofile加载3次 ----&#39;)
print(&#39;---- 测试loadfile加载3次 ----&#39;)
loadfile(&#39;testload&#39;)
loadfile(&#39;testload&#39;)
loadfile(&#39;testload&#39;)
print(&#39;---- 测试loadfile加载3次 ----&#39;)
-- 具体模块实现
--[[
模块名: TestMod.lua
local TestMod &#61; {}
local function getname()
return "无忌"
end
function TestMod.Greeting()
print("Hello, My name is "..getname())
end
return TestMod
--]]
-- 调用
local test_mod &#61; require("TestMod")
test_mod.Greeting()
Lua &#43; Redis集群 秒杀场景使用
实现逻辑&#xff1a;
- 秒杀商品先保存在Redis
- 确认下单资格&#xff0c;通过Lua脚本扣减库存&#xff0c;由于Redis是单线程模型&#xff0c;Lua可以保证多个命令原子性
初始化商品数据&#xff1a;
模拟数据格式:goodsId 商品ID&#xff0c;Total 商品总数&#xff0c;Booked 商品已预定数
"goodsId" : {
"Total": 3
"Booked": 0
}
执行redis初始化&#xff1a;
redis 127.0.0.1:6379> HMSET goodsId Total 3 Booked 0
OK
redis 127.0.0.1:6379> HMGET goodsId Total Booked
1) "3"
2) "3"
Lua实现扣减库存脚本: seckill.lua
local n &#61; tonumber(ARGV[1])
if not n or n &#61;&#61; 0 then
return 0
end
local vals &#61; redis.call(&#39;HMGET&#39;, KEYS[1], &#39;Total&#39;, &#39;Booked&#39;);
local total &#61; tonumber(vals[1])
local blocked &#61; tonumber(vals[2])
if not total or not blocked then
return 0
end
if blocked &#43; n <&#61; total then
redis.call(&#39;HINCRBY&#39;, KEYS[1], &#39;Booked&#39;, n)
return n;
end
return 0
将扣减库存脚本加载到Redis:
./redis-cli SCRIPT LOAD "$(cat /usr/local/redis/bin/seckill.lua)"
59dac41ffd27bef73ae87593da59b783b737a04b
执行扣减代码&#xff1a;
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
(integer) 1
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
(integer) 1
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
(integer) 1
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
(integer) 0
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
(integer) 0
127.0.0.1:6379> EVALSHA 59dac41ffd27bef73ae87593da59b783b737a04b 1 goodsId 1
返回 1 扣减成功
返回 0 扣减失败
执行函数说明
EVAL script numkeys key [key ...] arg [arg ...]
EVAL 执行脚本 key数量 key值 执行参数
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
EVALSHA 执行sha1效验值 key数量 key值 执行参数
每个被执行过的 Lua 脚本&#xff0c; 在 Lua 环境中都有一个和它相对应的函数&#xff0c; 函数的名字由 f_ 前缀加上 40 个字符长的 SHA1 校验和构成&#xff1a; 比如 f_5332031c6b470dc5a0dd9b4bf2030dea6d65de91 。
只要脚本所对应的函数曾经在 Lua 里面定义过&#xff0c; 那么即使用户不知道脚本的内容本身&#xff0c; 也可以直接通过脚本的 SHA1 校验和来调用脚本所对应的函数&#xff0c; 从而达到执行脚本的目的 —— 这就是 EVALSHA 命令的实现原理。
参考
Lua环境搭建&#xff1a;http://www.runoob.com/lua/lua-environment.html
Lua简明教程(入厕文章)&#xff1a;https://coolshell.cn/articles/10739.html
Aliyun redis集群: https://help.aliyun.com/document_detail/63920.html?spm&#61;a2c4g.11186623.6.611.A4j6rw
Redis中Lua脚本使用: http://redisbook.readthedocs.io/en/latest/feature/scripting.html