挑战一晚上从零入门lua语言,直接对标Python快速上手
文章目录
- 缘起
- 环境搭建
- 运行方式
- 注释
- 起名字
- 变量
- 数据类型
- table
- function
- 变量
- 索引
- 循环
- 分支语句
- 函数
- 运算符
- 字符串操作
- 数组
- 一维数组
- 多维数组
- 区间迭代器
- 模块与包
- 加载机制
- 面向对象
- 内存管理
- 文件IO
- “线程” -- 协同程序
缘起
缘起:项目要用。
学习时间:懒,所以速战速决吧。
学习方法:直接对标Python。
环境搭建
此处使用Linux环境。
wget www.lua.org/ftp/lua-5.4.3.tar.gz #版本自己挑
tar -xvf lu[tab]
cd lu[tab]
make linux test
make install
简单粗暴,全程两分钟。
运行方式
先给一行代码去运行一下找找成就感:
print("hallo world") --这里直接对标Python的print
熟悉的 hallo world。
方式一:起一个 .lua 文件,直接放进去,保存退出来,命令:lua XXX.lua 即可
方式二:起一个 .lua 文件,开头放上:
#!/usr/local/bin/lua
再放上那一行代码,保存退出,./XXX.lua
方式三:命令:lua -i,开启交互式编程渠道。
注释
这里的注释对标的是 sql 语言。
--这是单行注释
--[[
这是多行注释
]]--
起名字
名字不可乱起,还记得我教Python课的时候给学员们说起名字的方式嘛:
--[[
直接英译
多个单词下划线隔开
单个单词末尾加个下划线
尽量不用大写
--]]
妥妥的。
看一下关键字:
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
按我的起名法,苹果你就起个 apple_,红苹果你就起个 red_apple。冲突不了。
变量
直接写,默认是全局变量,不用纠结啥的,对标Python。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。 这里跟Python有点不一样,Python访问没有显示定义的变量是要报错的。
删除变量吗?那你想多了。
你用,或者不用,它就在那里,不卑不亢。
不想用了,置空(nil)就好。
数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。这点直接对标Python。
在lua里面查看变量类型也是使用type函数。不过我没那个兴趣就是了。
这里面基本都可以对标Python,我只提一下比Python多的部分吧。
1、string,居然支持和数字进行算术运算。不过这里要求这个字符串是可以被转数字的。这个特性其实就是在背地里进行了类型转换而已。在C++里这就是一个运算符重载的事情而已。
2、还是string,可以用 [[[]]]来对标Python中的 ‘’’’’’。
3、依旧是string,可以使用 # 来计算字符串长度。例如:print(#“123456”),输出6。
这里插播一条:字符串的拼接不是 +,而是 … ,下面那个示例有体现。
table
上面没有讲清楚。对标的是字典。不过这个字典可以没有设置键,会有包分配,从1开始。键可以是数字或者是字符串。
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) doprint(k .. " : " .. v)
end
local tbl = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl) doprint("Key", key)
end
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
要删除键也很简单,将nil赋值给那个键、
常用方法:
tips:
当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。
可以使用以下方法来代替:
function table_leng(t)local leng=0for k, v in pairs(t) doleng=leng+1endreturn leng;
end
function
这种把函数当成对象的行为还是第一次见哈。
function factorial1(n)if n == 0 thenreturn 1elsereturn n * factorial1(n - 1)end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))
function 可以以匿名函数(anonymous function)的方式通过参数传递:
function testFun(tab,fun)for k ,v in pairs(tab) doprint(fun(k,v));end
endtab={key1="val1",key2="val2"};
testFun(tab,
function(key,val)--匿名函数return key.."="..val;
end
);
不过我想我应该没有吃饱那么撑去炫这个技吧,上面那个性能是比这个要差吗?
线程和自定义类型后面再说吧。
变量
在Python中,函数等块内部的就是局部变量,如果要在其中声明全局变量则需要加 global 关键字。
lua 则相反,默认统统是全局变量,如果要声明局部变量则要加 local 关键字。
局部变量的作用域为从声明位置开始到所在语句块结束。
a = 5 -- 全局变量
local b = 5 -- 局部变量function joke()c = 5 -- 全局变量local d = 6 -- 局部变量
endjoke()
print(c,d) --> 5 nildolocal a = 6 -- 局部变量b = 6 -- 对局部变量重新赋值print(a,b); --> 6 6
endprint(a,b) --> 5 6
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
这点可以直接对标Python。
不过呢,当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
小tips:多值赋值经常用来交换变量,或将函数调用返回给变量。
应该尽可能的使用局部变量,有两个好处:
1. 避免命名冲突。
2. 访问局部变量的速度比全局变量更快。
索引
这是 lua 和 Python、C++等语言不同的地方了,lua 是从1开始计数的,回忆一下前面的 table 示例。
循环
示例:
while( true )
doprint("循环将永远执行下去")
end
lua中只有break,没有continue,不过人的智慧是有无限可能的:
for i = 10, 1, -1 dorepeatif i == 5 thenprint("continue code here")breakendprint(i, "loop code here")until true
end
分支语句
概念直接对标Python,不过需要注意的是,lua 里面,0是true。
if(0)
thenprint("0 为 true")
end
有if else、if… else if… else…写法,不过我不想写而已。
函数
[local] function function_name([argument1, argument2, argument3..., argumentn])function_bodyreturn result_params_comma_separated
end
[] 这个是可选的意思,不会有人不知道吧???
function max(a, b)if (a > b) thenres = a;elseres = b;endreturn res;
end
该怎么调用?跟Python一样。
同样的,lua 也支持将函数作为参数进行传参,我更愿意称之为:“函数指针”。
同样,多返回值性质也直接对标Python。
可变参数也一样,对标:
function add(...)
local s = 0 for i, v in ipairs{...} do --> {...} 表示一个由所有变长参数构成的数组 s = s + v end return s
end
print(add(1,2,3,4))
运算符
该咋样就咋样。我提一下和Python里面不一样的(这里提一下,Python里面的 / 就是除法,不是整除)
1、~=:不等于,检测两个值是否相等,相等返回 false,否则返回 true。
2、… :连接运算符,连接两个字符串。
3、 # :返回字符串或表的长度。
运算符优先级一般我是不管的,只要我括号加的勤。
字符串操作
放一些常用的:
1、
string.gsub(mainString,findString,replaceString,num)
在字符串中替换。
mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:
string.gsub(“aaaa”,“a”,“z”,3);
zzza 3
2、
string.char(arg) 和 string.byte(arg[,int])
char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
string.char(97,98,99,100)
abcd
string.byte(“ABCD”,4)
68
string.byte(“ABCD”)
65
3、
string.len(arg)
计算字符串长度。
string.len(“abc”)
3
4、
string.rep(string, n)
返回字符串string的n个拷贝
string.rep(“abcd”,2)
abcdabcd
5、
string.format(...)
返回一个类似printf的格式化字符串
string.format(“the value is:%d”,4)
the value is:4
数组
一维数组
array = {"Lua", "Tutorial"}for i= 0, 2 doprint(array[i])
end
nil
Lua
Tutorial
多维数组
-- 初始化数组
array = {}
for i=1,3 doarray[i] = {}for j=1,3 doarray[i][j] = i*jend
end-- 访问数组
for i=1,3 dofor j=1,3 doprint(array[i][j])end
end
1
2
3
2
4
6
3
6
9
区间迭代器
这个也直接对标Python吧,C++11也引入了。
array = {"Google", "Runoob"}for key,value in ipairs(array)
doprint(key, value)
end
模块与包
从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}-- 定义一个常量
module.constant = "这是一个常量"-- 定义一个函数
function module.func1()io.write("这是一个公有函数!\n")
endlocal function func2()print("这是一个私有函数!")
endfunction module.func3()func2()
endreturn module
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。
require("<模块名>")
or
require “<模块名>”
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")print(module.constant)module.func3()
加载机制
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 “~/lua/” 路径加入 LUA_PATH 环境变量里:
#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
文件路径以 “;” 号分隔,最后的 2 个 “;;” 表示新加的路径后面加上原来的默认路径。
接着,更新环境变量参数,使之立即生效。
source ~/.profile
面向对象
其实我个人觉得不是很有必要哈,做好自己分内的事情就好了。还有Python。
但是既然人家有这个功能,那不妨提一提,省的以后看到代码里面写了看不懂就很尬。
不多说,放码过去:
-- 元类
Shape = {area = 0}-- 基础类方法 new
function Shape:new (o,side)o = o or {}setmetatable(o, self)self.__index = selfside = side or 0self.area = side*side;return o
end-- 基础类方法 printArea
function Shape:printArea ()print("面积为 ",self.area)
end-- 创建对象
myshape = Shape:new(nil,10)myshape:printArea()
那继承呢?
-- Meta class
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)o = o or {}setmetatable(o, self)self.__index = selfside = side or 0self.area = side*side;return o
end
-- 基础类方法 printArea
function Shape:printArea ()print("面积为 ",self.area)
end-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)o = o or Shape:new(o,side)setmetatable(o, self)self.__index = selfreturn o
end-- 派生类方法 printArea
function Square:printArea ()print("正方形面积为 ",self.area)
end-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea()Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o,length,breadth)o = o or Shape:new(o)setmetatable(o, self)self.__index = selfself.area = length * breadthreturn o
end-- 派生类方法 printArea
function Rectangle:printArea ()print("矩形面积为 ",self.area)
end-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea()
函数重写呢?
-- 派生类方法 printArea
function Square:printArea ()print("正方形面积 ",self.area)
end
内存管理
对标Python,不用我们管,哈哈
文件IO
基本一个语言学的差不多了都要对文件进行IO操作,C++是这样,Python是这样,lua也一样。
不喜欢废话,直接用案例说话吧:
-- 以只读方式打开文件
file = io.open("test.lua", "r")-- 设置默认输入文件为 test.lua
io.input(file)-- 输出文件第一行
print(io.read())-- 关闭打开的文件
io.close(file)-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")-- 设置默认输出文件为 test.lua
io.output(file)-- 在文件最后一行添加 Lua 注释
io.write("-- test.lua 文件末尾注释")-- 关闭打开的文件
io.close(file)
io.read() 的参数列表:
“线程” – 协同程序
放到最后什么意思大家也都明白哈。
Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
线程 VS 协同
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
基本语法与演示:
各方法的演示:
-- coroutine_test.lua 文件
co = coroutine.create(function(i)print(i);end
)coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- deadprint("----------")co = coroutine.wrap(function(i)print(i);end
)co(1)print("----------")co2 = coroutine.create(function()for i=1,10 doprint(i)if i == 3 thenprint(coroutine.status(co2)) --runningprint(coroutine.running()) --thread:XXXXXXendcoroutine.yield()endend
)coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3print(coroutine.status(co2)) -- suspended
print(coroutine.running())print("----------")
coroutine在底层实现就是一个线程,当create一个coroutine的时候就是在新线程中注册了一个事件,当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。
众所周知,多线程性质没有好弄,所以我从网上多找了几份代码放这儿,看着研究:
-- 实例
function foo (a)print("foo 函数输出", a)return coroutine.yield(2 * a) -- 返回 2*a 的值
endco = coroutine.create(function (a , b)print("第一次协同程序执行输出", a, b) -- co-body 1 10local r = foo(a + 1)print("第二次协同程序执行输出", r)local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入print("第三次协同程序执行输出", r, s)return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")--输出结果:第一次协同程序执行输出 1 10
foo 函数输出 2
main true 4
--分割线----
第二次协同程序执行输出 r
main true 11 -9
---分割线---
第三次协同程序执行输出 x y
main true 10 结束协同程序
---分割线---
main false cannot resume dead coroutine
---分割线---resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。
生产-消费者 实例
local newProductorfunction productor()local i = 0while true doi = i + 1send(i) -- 将生产的物品发送给消费者end
endfunction consumer()while true dolocal i = receive() -- 从生产者那里得到物品print(i)end
endfunction receive()local status, value = coroutine.resume(newProductor)return value
endfunction send(x)coroutine.yield(x) -- x表示需要发送的值,值返回以后,就挂起该协同程序
end-- 启动程序
newProductor = coroutine.create(productor)
consumer()
(慢慢研究,我得去睡了)
挑战一晚上从零入门lua语言,直接对标Python快速上手相关推荐
- python与c语言在语法上的区别-Python与C语言基础对比(Python快速入门)
代码较长,建议使用电脑阅读本文. 10分钟入门Python 本文中使用的是Python3 如果你曾经学过C语言,阅读此文,相信你能迅速发现这两种语言的异同,达到快速入门的目的.下面将开始介绍它们的异同 ...
- python和c语言的对比_Python与C语言基础对比(Python快速入门)
原博文 2019-07-05 09:23 − > 代码较长,建议使用电脑阅读本文. # 10分钟入门Python `本文中使用的是Python3` 如果你曾经学过C语言,阅读此文,相信你能迅速发 ...
- python快速上手 让繁琐工作自动化 英文版_入门python:《Python编程快速上手让繁琐工作自动化》中英文PDF+代码...
入门推荐学习<python编程快速上手>前6章是python的基础知识,通俗易懂地讲解基础,初学者容易犯错的地方,都会指出来.从第三章开始,每章都有一个实践项目,用来巩固前面所学的知识. ...
- PyQt5入门——手把手教你配置环境,快速上手GUI程序开发(Anaconda+PyCharm+Qt Designer+pyuic)
文章目录 引言 1. 安装python环境 1.1 安装anaconda 1.2 创建虚拟环境 2. 安装PyQt库 3. 安装pycharm 4. 在pycharm中配置PyQt 4.1 配置PyQ ...
- Python与C语言基础对比(Python快速入门)
[c,c++,c#,java?这些有什么区别?转] c,c++,c#,java?这些有什么区别? C语言: 目前最著名.最有影响.应用最广泛的windows.linux和UNIX三个操作系统都是用C语 ...
- python快速编程入门课本第六章_python编程快速上手第六章实践项目参考code
代码如下: 题目的意思是通过一个函数将列表的列表显示在组织良好的表格中,每列右对齐 tableData = [['apples', 'oranges', 'cherries', 'banana'], ...
- c语言添加miracl库,密码学C语言函数库——Miracl库快速上手中文指南(VC)
一.简介 密码学学习.研究人员往往着重于理论研究,难以与实践直接挂钩,今天介绍一下国外著名密码学C语言函数库--Miracl库的使用方法. 该库针对公钥密码学和椭圆曲线密码学的实现,写了很多函数,在这 ...
- Maven从入门到精通,小白也能快速上手
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 文章目 ...
- 密码学C语言函数库——Miracl库快速上手中文指南(VC)
一.简介 密码学学习.研究人员往往着重于理论研究,难以与实践直接挂钩,今天介绍一下国外著名密码学C语言函数库--Miracl库的使用方法. Miracl库的官方网站是http://www.shamus ...
最新文章
- “偷懒”上热搜!南京大三学生自制宿舍关灯神器火了,网友:希望量产
- 面试两个星期来的一点体会
- 最近24小时记录:虚拟机与Wireshark 2.0
- 踏踏实实做事,老老实实做人
- 逆向python生成的可执行文件
- 国内互联网广告生态现状【计算广告】
- RobHess的SIFT代码解析之RANSAC
- linux怎么同时查看两个文件,MultiTail - 在单个Linux终端中同时监视多个文件
- 判断 JS 中对象的类型
- 详解SaaS产品的5类核心指标
- 关于bootstrap的table表显示无法找到匹配内容的问题随笔
- 在线CSV转XML工具
- C语言中static的使用
- 数学建模学习:蒙特卡洛模拟
- 高低频磨皮(商业磨皮一种)
- EL表达式的语法、用法及说明
- 《最好的告别》是有尊严的离开
- cdh6.2离线安装(傻瓜式安装教程)
- 初学编程丨从零开始进修编程的基本路线,BAT程序员亲手总结!
- vue echarts 水球图 多个水球图并存配置