挑戰(zhàn)一晚上從零入門lua語(yǔ)言,直接對(duì)標(biāo)Python快速上手(lua語(yǔ)言和python)
@[toc]
緣起
緣起:項(xiàng)目要用。
學(xué)習(xí)時(shí)間:懶,所以速戰(zhàn)速?zèng)Q吧。
學(xué)習(xí)方法:直接對(duì)標(biāo)Python。
環(huán)境搭建
此處使用Linux環(huán)境。
wget www.lua.org/ftp/lua-5.4.3.tar.gz #版本自己挑 tar -xvf lu[tab] cd lu[tab] make linux test make install
簡(jiǎn)單粗暴,全程兩分鐘。
運(yùn)行方式
先給一行代碼去運(yùn)行一下找找成就感:
print("hallo world") --這里直接對(duì)標(biāo)Python的print
熟悉的 hallo world。
方式一:起一個(gè) .lua 文件,直接放進(jìn)去,保存退出來(lái),命令:lua XXX.lua 即可
方式二:起一個(gè) .lua 文件,開(kāi)頭放上:
#!/usr/local/bin/lua
再放上那一行代碼,保存退出,./XXX.lua
方式三:命令:lua -i,開(kāi)啟交互式編程渠道。
注釋
這里的注釋對(duì)標(biāo)的是 sql 語(yǔ)言。
--這是單行注釋 --[[ 這是多行注釋 ]]--
起名字
名字不可亂起,還記得我教Python課的時(shí)候給學(xué)員們說(shuō)起名字的方式嘛:
--[[ 直接英譯 多個(gè)單詞下劃線隔開(kāi) 單個(gè)單詞末尾加個(gè)下劃線 盡量不用大寫 --]]
妥妥的。
看一下關(guān)鍵字:
一般約定,以下劃線開(kāi)頭連接一串大寫字母的名字(比如 _VERSION)被保留用于 Lua 內(nèi)部全局變量。
按我的起名法,蘋果你就起個(gè) apple_,紅蘋果你就起個(gè) red_apple。沖突不了。
變量
直接寫,默認(rèn)是全局變量,不用糾結(jié)啥的,對(duì)標(biāo)Python。
全局變量不需要聲明,給一個(gè)變量賦值后即創(chuàng)建了這個(gè)全局變量,訪問(wèn)一個(gè)沒(méi)有初始化的全局變量也不會(huì)出錯(cuò),只不過(guò)得到的結(jié)果是:nil。 這里跟Python有點(diǎn)不一樣,Python訪問(wèn)沒(méi)有顯示定義的變量是要報(bào)錯(cuò)的。
刪除變量嗎?那你想多了。
你用,或者不用,它就在那里,不卑不亢。
不想用了,置空(nil)就好。
數(shù)據(jù)類型
Lua 是動(dòng)態(tài)類型語(yǔ)言,變量不要類型定義,只需要為變量賦值。這點(diǎn)直接對(duì)標(biāo)Python。
在lua里面查看變量類型也是使用type函數(shù)。不過(guò)我沒(méi)那個(gè)興趣就是了。
這里面基本都可以對(duì)標(biāo)Python,我只提一下比Python多的部分吧。
1、string,居然支持和數(shù)字進(jìn)行算術(shù)運(yùn)算。不過(guò)這里要求這個(gè)字符串是可以被轉(zhuǎn)數(shù)字的。這個(gè)特性其實(shí)就是在背地里進(jìn)行了類型轉(zhuǎn)換而已。在C++里這就是一個(gè)運(yùn)算符重載的事情而已。
2、還是string,可以用 [[[]]]來(lái)對(duì)標(biāo)Python中的 ‘’’’’’。
3、依舊是string,可以使用 # 來(lái)計(jì)算字符串長(zhǎng)度。例如:print(#“123456”),輸出6。
這里插播一條:字符串的拼接不是 +,而是 … ,下面那個(gè)示例有體現(xiàn)。
table
上面沒(méi)有講清楚。對(duì)標(biāo)的是字典。不過(guò)這個(gè)字典可以沒(méi)有設(shè)置鍵,會(huì)有包分配,從1開(kāi)始。鍵可以是數(shù)字或者是字符串。
a = {} a["key"] = "value" key = 10 a[key] = 22 a[key] = a[key] + 11 for k, v in pairs(a) do print(k .. " : " .. v) end
local tbl = {"apple", "pear", "orange", "grape"} for key, val in pairs(tbl) do print("Key", key) end
table 不會(huì)固定長(zhǎng)度大小,有新數(shù)據(jù)添加時(shí) table 長(zhǎng)度會(huì)自動(dòng)增長(zhǎng),沒(méi)初始的 table 都是 nil。
要?jiǎng)h除鍵也很簡(jiǎn)單,將nil賦值給那個(gè)鍵、
常用方法:
tips:
當(dāng)我們獲取 table 的長(zhǎng)度的時(shí)候無(wú)論是使用 # 還是 table.getn 其都會(huì)在索引中斷的地方停止計(jì)數(shù),而導(dǎo)致無(wú)法正確取得 table 的長(zhǎng)度。
可以使用以下方法來(lái)代替:
function table_leng(t) local leng=0 for k, v in pairs(t) do leng=leng+1 end return leng; end
function
這種把函數(shù)當(dāng)成對(duì)象的行為還是第一次見(jiàn)哈。
function factorial1(n) if n == 0 then return 1 else return n * factorial1(n - 1) end end print(factorial1(5)) factorial2 = factorial1 print(factorial2(5))
function 可以以匿名函數(shù)(anonymous function)的方式通過(guò)參數(shù)傳遞:
function testFun(tab,fun) for k ,v in pairs(tab) do print(fun(k,v)); end end tab={key1="val1",key2="val2"}; testFun(tab, function(key,val)--匿名函數(shù) return key.."="..val; end );
不過(guò)我想我應(yīng)該沒(méi)有吃飽那么撐去炫這個(gè)技吧,上面那個(gè)性能是比這個(gè)要差嗎?
線程和自定義類型后面再說(shuō)吧。
變量
在Python中,函數(shù)等塊內(nèi)部的就是局部變量,如果要在其中聲明全局變量則需要加 global 關(guān)鍵字。
lua 則相反,默認(rèn)統(tǒng)統(tǒng)是全局變量,如果要聲明局部變量則要加 local 關(guān)鍵字。
局部變量的作用域?yàn)閺穆暶魑恢瞄_(kāi)始到所在語(yǔ)句塊結(jié)束。
a = 5 -- 全局變量 local b = 5 -- 局部變量 function joke() c = 5 -- 全局變量 local d = 6 -- 局部變量 end joke() print(c,d) --> 5 nil do local a = 6 -- 局部變量 b = 6 -- 對(duì)局部變量重新賦值 print(a,b); --> 6 6 end print(a,b) --> 5 6
Lua 可以對(duì)多個(gè)變量同時(shí)賦值,變量列表和值列表的各個(gè)元素用逗號(hào)分開(kāi),賦值語(yǔ)句右邊的值會(huì)依次賦給左邊的變量。
這點(diǎn)可以直接對(duì)標(biāo)Python。
不過(guò)呢,當(dāng)變量個(gè)數(shù)和值的個(gè)數(shù)不一致時(shí),Lua會(huì)一直以變量個(gè)數(shù)為基礎(chǔ)采取以下策略:
a. 變量個(gè)數(shù) > 值的個(gè)數(shù) 按變量個(gè)數(shù)補(bǔ)足nil b. 變量個(gè)數(shù) < 值的個(gè)數(shù) 多余的值會(huì)被忽略
小tips:多值賦值經(jīng)常用來(lái)交換變量,或?qū)⒑瘮?shù)調(diào)用返回給變量。
應(yīng)該盡可能的使用局部變量,有兩個(gè)好處:
1. 避免命名沖突。 2. 訪問(wèn)局部變量的速度比全局變量更快。
索引
這是 lua 和 Python、C++等語(yǔ)言不同的地方了,lua 是從1開(kāi)始計(jì)數(shù)的,回憶一下前面的 table 示例。
循環(huán)
示例:
while( true ) do print("循環(huán)將永遠(yuǎn)執(zhí)行下去") end
lua中只有break,沒(méi)有continue,不過(guò)人的智慧是有無(wú)限可能的:
for i = 10, 1, -1 do repeat if i == 5 then print("continue code here") break end print(i, "loop code here") until true end
分支語(yǔ)句
概念直接對(duì)標(biāo)Python,不過(guò)需要注意的是,lua 里面,0是true。
if(0) then print("0 為 true") end
有if else、if… else if… else…寫法,不過(guò)我不想寫而已。
函數(shù)
[local] function function_name([argument1, argument2, argument3..., argumentn]) function_body return result_params_comma_separated end
[] 這個(gè)是可選的意思,不會(huì)有人不知道吧???
function max(a, b) if (a > b) then res = a; else res = b; end return res; end
該怎么調(diào)用?跟Python一樣。
同樣的,lua 也支持將函數(shù)作為參數(shù)進(jìn)行傳參,我更愿意稱之為:“函數(shù)指針”。
同樣,多返回值性質(zhì)也直接對(duì)標(biāo)Python。
可變參數(shù)也一樣,對(duì)標(biāo):
function add(...) local s = 0 for i, v in ipairs{...} do --> {...} 表示一個(gè)由所有變長(zhǎng)參數(shù)構(gòu)成的數(shù)組 s = s + v end return s end print(add(1,2,3,4))
運(yùn)算符
該咋樣就咋樣。我提一下和Python里面不一樣的(這里提一下,Python里面的 / 就是除法,不是整除)
1、~=:不等于,檢測(cè)兩個(gè)值是否相等,相等返回 false,否則返回 true。
2、… :連接運(yùn)算符,連接兩個(gè)字符串。
3、 # :返回字符串或表的長(zhǎng)度。
運(yùn)算符優(yōu)先級(jí)一般我是不管的,只要我括號(hào)加的勤。
字符串操作
放一些常用的:
1、
string.gsub(mainString,findString,replaceString,num)
在字符串中替換。
mainString 為要操作的字符串, findString 為被替換的字符,replaceString 要替換的字符,num 替換次數(shù)(可以忽略,則全部替換),如:
string.gsub(“aaaa”,“a”,“z”,3);
zzza 3
2、
string.char(arg) 和 string.byte(arg[,int])
char 將整型數(shù)字轉(zhuǎn)成字符并連接, byte 轉(zhuǎn)換字符為整數(shù)值(可以指定某個(gè)字符,默認(rèn)第一個(gè)字符)。
string.char(97,98,99,100)
abcd
string.byte(“ABCD”,4)
68
string.byte(“ABCD”)
65
3、
string.len(arg)
計(jì)算字符串長(zhǎng)度。
string.len(“abc”)
3
4、
string.rep(string, n)
返回字符串string的n個(gè)拷貝
string.rep(“abcd”,2)
abcdabcd
5、
string.format(...)
返回一個(gè)類似printf的格式化字符串
string.format(“the value is:%d”,4)
the value is:4
數(shù)組
一維數(shù)組
array = {"Lua", "Tutorial"} for i= 0, 2 do print(array[i]) end
nil
Lua
Tutorial
多維數(shù)組
-- 初始化數(shù)組 array = {} for i=1,3 do array[i] = {} for j=1,3 do array[i][j] = i*j end end -- 訪問(wèn)數(shù)組 for i=1,3 do for j=1,3 do print(array[i][j]) end end
1
2
3
2
4
6
3
6
9
區(qū)間迭代器
這個(gè)也直接對(duì)標(biāo)Python吧,C++11也引入了。
array = {"Google", "Runoob"} for key,value in ipairs(array) do print(key, value) end
模塊與包
從 Lua 5.1 開(kāi)始,Lua 加入了標(biāo)準(zhǔn)的模塊管理機(jī)制,可以把一些公用的代碼放在一個(gè)文件里,以 API 接口的形式在其他地方調(diào)用,有利于代碼的重用和降低代碼耦合度。
Lua 的模塊是由變量、函數(shù)等已知元素組成的 table,因此創(chuàng)建一個(gè)模塊很簡(jiǎn)單,就是創(chuàng)建一個(gè) table,然后把需要導(dǎo)出的常量、函數(shù)放入其中,最后返回這個(gè) table 就行。
-- 文件名為 module.lua -- 定義一個(gè)名為 module 的模塊 module = {} -- 定義一個(gè)常量 module.constant = "這是一個(gè)常量" -- 定義一個(gè)函數(shù) function module.func1() io.write("這是一個(gè)公有函數(shù)!\n") end local function func2() print("這是一個(gè)私有函數(shù)!") end function module.func3() func2() end return module
Lua提供了一個(gè)名為require的函數(shù)用來(lái)加載模塊。要加載一個(gè)模塊,只需要簡(jiǎn)單地調(diào)用就可以了。
require("<模塊名>")
or
require “<模塊名>”
-- test_module.lua 文件 -- module 模塊為上文提到到 module.lua require("module") print(module.constant) module.func3()
加載機(jī)制
對(duì)于自定義的模塊,模塊文件不是放在哪個(gè)文件目錄都行,函數(shù) require 有它自己的文件路徑加載策略,它會(huì)嘗試從 Lua 文件或 C 程序庫(kù)中加載模塊。
require 用于搜索 Lua 文件的路徑是存放在全局變量 package.path 中,當(dāng) Lua 啟動(dòng)后,會(huì)以環(huán)境變量 LUA_PATH 的值來(lái)初始這個(gè)環(huán)境變量。如果沒(méi)有找到該環(huán)境變量,則使用一個(gè)編譯時(shí)定義的默認(rèn)路徑來(lái)初始化。
當(dāng)然,如果沒(méi)有 LUA_PATH 這個(gè)環(huán)境變量,也可以自定義設(shè)置,在當(dāng)前用戶根目錄下打開(kāi) .profile 文件(沒(méi)有則創(chuàng)建,打開(kāi) .bashrc 文件也可以),例如把 “~/lua/” 路徑加入 LUA_PATH 環(huán)境變量里:
#LUA_PATH export LUA_PATH="~/lua/?.lua;;"
文件路徑以 “;” 號(hào)分隔,最后的 2 個(gè) “;;” 表示新加的路徑后面加上原來(lái)的默認(rèn)路徑。
接著,更新環(huán)境變量參數(shù),使之立即生效。
source ~/.profile
面向?qū)ο?/p>
其實(shí)我個(gè)人覺(jué)得不是很有必要哈,做好自己分內(nèi)的事情就好了。還有Python。
但是既然人家有這個(gè)功能,那不妨提一提,省的以后看到代碼里面寫了看不懂就很尬。
不多說(shuō),放碼過(guò)去:
-- 元類 Shape = {area = 0} -- 基礎(chǔ)類方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基礎(chǔ)類方法 printArea function Shape:printArea () print("面積為 ",self.area) end -- 創(chuàng)建對(duì)象 myshape = Shape:new(nil,10) myshape:printArea()
那繼承呢?
-- Meta class Shape = {area = 0} -- 基礎(chǔ)類方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基礎(chǔ)類方法 printArea function Shape:printArea () print("面積為 ",self.area) end -- 創(chuàng)建對(duì)象 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 = self return o end -- 派生類方法 printArea function Square:printArea () print("正方形面積為 ",self.area) end -- 創(chuàng)建對(duì)象 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 = self self.area = length * breadth return o end -- 派生類方法 printArea function Rectangle:printArea () print("矩形面積為 ",self.area) end -- 創(chuàng)建對(duì)象 myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
函數(shù)重寫呢?
-- 派生類方法 printArea function Square:printArea () print("正方形面積 ",self.area) end
內(nèi)存管理
對(duì)標(biāo)Python,不用我們管,哈哈
文件IO
基本一個(gè)語(yǔ)言學(xué)的差不多了都要對(duì)文件進(jìn)行IO操作,C++是這樣,Python是這樣,lua也一樣。
不喜歡廢話,直接用案例說(shuō)話吧:
-- 以只讀方式打開(kāi)文件 file = io.open("test.lua", "r") -- 設(shè)置默認(rèn)輸入文件為 test.lua io.input(file) -- 輸出文件第一行 print(io.read()) -- 關(guān)閉打開(kāi)的文件 io.close(file) -- 以附加的方式打開(kāi)只寫文件 file = io.open("test.lua", "a") -- 設(shè)置默認(rèn)輸出文件為 test.lua io.output(file) -- 在文件最后一行添加 Lua 注釋 io.write("-- test.lua 文件末尾注釋") -- 關(guān)閉打開(kāi)的文件 io.close(file)
io.read() 的參數(shù)列表:
“線程” – 協(xié)同程序
放到最后什么意思大家也都明白哈。
Lua 協(xié)同程序(coroutine)與線程比較類似:擁有獨(dú)立的堆棧,獨(dú)立的局部變量,獨(dú)立的指令指針,同時(shí)又與其它協(xié)同程序共享全局變量和其它大部分東西。
線程 VS 協(xié)同
線程與協(xié)同程序的主要區(qū)別在于,一個(gè)具有多個(gè)線程的程序可以同時(shí)運(yùn)行幾個(gè)線程,而協(xié)同程序卻需要彼此協(xié)作的運(yùn)行。 在任一指定時(shí)刻只有一個(gè)協(xié)同程序在運(yùn)行,并且這個(gè)正在運(yùn)行的協(xié)同程序只有在明確的被要求掛起的時(shí)候才會(huì)被掛起。 協(xié)同程序有點(diǎn)類似同步的多線程,在等待同一個(gè)線程鎖的幾個(gè)線程有點(diǎn)類似協(xié)同。
基本語(yǔ)法與演示:
各方法的演示:
-- coroutine_test.lua 文件 co = coroutine.create( function(i) print(i); end ) coroutine.resume(co, 1) -- 1 print(coroutine.status(co)) -- dead print("----------") co = coroutine.wrap( function(i) print(i); end ) co(1) print("----------") co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) --running print(coroutine.running()) --thread:XXXXXX end coroutine.yield() end end ) coroutine.resume(co2) --1 coroutine.resume(co2) --2 coroutine.resume(co2) --3 print(coroutine.status(co2)) -- suspended print(coroutine.running()) print("----------")
coroutine在底層實(shí)現(xiàn)就是一個(gè)線程,當(dāng)create一個(gè)coroutine的時(shí)候就是在新線程中注冊(cè)了一個(gè)事件,當(dāng)使用resume觸發(fā)事件的時(shí)候,create的coroutine函數(shù)就被執(zhí)行了,當(dāng)遇到y(tǒng)ield的時(shí)候就代表掛起當(dāng)前線程,等候再次resume觸發(fā)事件。
眾所周知,多線程性質(zhì)沒(méi)有好弄,所以我從網(wǎng)上多找了幾份代碼放這兒,看著研究:
-- 實(shí)例 function foo (a) print("foo 函數(shù)輸出", a) return coroutine.yield(2 * a) -- 返回 2*a 的值 end co = coroutine.create(function (a , b) print("第一次協(xié)同程序執(zhí)行輸出", a, b) -- co-body 1 10 local r = foo(a + 1) print("第二次協(xié)同程序執(zhí)行輸出", r) local r, s = coroutine.yield(a + b, a - b) -- a,b的值為第一次調(diào)用協(xié)同程序時(shí)傳入 print("第三次協(xié)同程序執(zhí)行輸出", r, s) return b, "結(jié)束協(xié)同程序" -- b的值為第二次調(diào)用協(xié)同程序時(shí)傳入 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("---分割線---") --輸出結(jié)果: 第一次協(xié)同程序執(zhí)行輸出 1 10 foo 函數(shù)輸出 2 main true 4 --分割線---- 第二次協(xié)同程序執(zhí)行輸出 r main true 11 -9 ---分割線--- 第三次協(xié)同程序執(zhí)行輸出 x y main true 10 結(jié)束協(xié)同程序 ---分割線--- main false cannot resume dead coroutine ---分割線--- resume和yield的配合強(qiáng)大之處在于,resume處于主程中,它將外部狀態(tài)(數(shù)據(jù))傳入到協(xié)同程序內(nèi)部;而yield則將內(nèi)部的狀態(tài)(數(shù)據(jù))返回到主程中。
生產(chǎn)-消費(fèi)者 實(shí)例
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 將生產(chǎn)的物品發(fā)送給消費(fèi)者 end end function consumer() while true do local i = receive() -- 從生產(chǎn)者那里得到物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示需要發(fā)送的值,值返回以后,就掛起該協(xié)同程序 end -- 啟動(dòng)程序 newProductor = coroutine.create(productor) consumer()
(慢慢研究,我得去睡了)
https Lua Python
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。