用的是Lunar封装
学习笔记, 是直接从pil(http://www.lua.org/pil/index.html)文档里翻译
第一部分:
1.从lua交互状态退出的方法
^+D, ^+Z, os.exit()
2.lua可连续运行两个文件, 如:
lua -la -lb
其中, l是连接符, 不可少. a,b是两个不同的文件, 这个对于测试和debug特别有效
3.dofile可用于立即执行文件
4.全局变量不需要声明,赋值时即同时声明, 所以应该在初始化时进行赋值
如果要删除全局变量, 用nil删除; 而如果只是局部使用某变量,应该声明成局部量local
5.lua中的关键字不能以数字开头, 只能以字母和下划线开头, 组成元素可以是字母, 数字和下划线
6.保留字不能用作关键字(废话, 几乎所有语言皆如此),lua是严格区分大小写的
7.块注释用--[[ 语句 ]], 行注释用-- , 无效的块注释:
----[[
--]]
8.lua的命令行用法:
lua [options] [script [args]]
如:
lua -e "print(math.sin(12))", -e选项表示直接执行后面的语句
9.lua -i -l a.lua -e "x = 10"
含义:
-i表示进入交互式, -l表示装入a.lua文件, 而-e则表示执行其后的语句. 所以连起来就是执行a.lua, 执行x=10, 然后进入交互状态
10.通过: lua -i -e "_PROMPT='lua>'"的方式可以改变lua交互状态下的提示符
11.lua的命令行参数用法:
prompt> lua -e "sin=math.sin" script a b
lua collects the arguments as follows:
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"
arg是它的命令行参数表
Lua学习笔记之 Type and Values
笔记内容:
1. lua提供了八种数据类型: nil, boolean, string, number, userdata, function, thread, table. 如果想看某个表达式是何种类型的, 可以使用type()函数, 它的返回值是string类型:
> print(type(10.4*3))
number
> print(type(type(10.4*3)))
string
2. 一个变量, 可以被多次赋以任何类型的值. 相比之下, lua中使用函数指针时的简单, 到了令人咋舌的程度: 再也不需要象c里那样弄个指针传来传去的了, 比如: a = print , 而后就可以用a(3)来打印表达式的值了. 让我觉得最爽的一点就是: 使用table+函数赋值的方式, 可以非常容易的实现函数表, 从而避免出现大量的if else语句. 形如下:
function func_1( para )
para = para + 1
return para
end
function func_2( para )
para = para+2
return para
end
func_table = {
["func1"] = func_1,
["func2"] = func_2,
}
print ( func_table["func1"](1) )
打印的结果是:
2
选择string作为 key, 是为了让你了解在lua里, 作函数表是如何的方便.
3. 全局变量在第一次赋值前, 默认是nil; 删除全局变量, 也是将此变量赋值为nil. nil值, 也常作为函数的无效返回值来使用.
4. boolean类型有两个值: false 和 true( 全为小写 ). 在条件表达式中, 只有false和nil才表示假, 其余任何值均表示真(0也表示真). 比如这些值:
if false then print( "ok" ) end
if nil then print("ok") end
if "" then print("ok") end
if '' then print( "ok" ) end
if "haha" then print( "ok") end
if 0 then print("ok") end
在上面的这些语句中, 只有前两条语句打印不出ok, 其它皆可以.
5. 对于number类型, lua只提供real实型数. 没有整数类型, 按pil的说法: as it does not need it.... Moreover, most modern CPUs do floating-point arithmetic as fast as (or even faster than) integer arithmetic. 我想, lua自身的这些"狡辩"已经有足够的说服力, 不用再罗索了. 什么叫自由? 看这里:
>print( string.sub( "hello", 1.1, 2.2 ) )
he
其效果等同于: print( string.sub( "hello", 1, 2 ) )
6. 都说lua的string类型功能很强大, 可就目前我的使用而言, 并没有感受到这种强大给我带来的更多的便利性, 我想, 可能还是码lua码得少了. lua里的string类型, 可以存放任意长的字符串, 一个字符串, 一旦创建便不可更改. 有人说, 这样操作起来岂不是效率很低? 其实, 也不尽然, 你想嘛, 那些对串进行操作的函数: sub, find, copy, replace, cat等等. 其中, 很多确实本身并没有要求一定要修改串本身, 很多函数都是不修改串的, 而同时, 修改串的那些操作, 还不如直接创建一个新串来得方便. lua对字符串的内存管理, 还是比较棒的, 编程者不用去管它的分配与回收, lua会根据自己的一套引用计数机制来自行决定何时释放掉这个串, 同时, 正是由于不可修改串本身, 也使得lua的字符串操作变成了单纯的引用操作, 这样效率应该是更高了.
字符串常量, 可以使用单引号, 也可以使用双引号括起来. 但是, 要培养一种良好的编程习惯: 固定使用一种风格的声明形式. 转义字符, 一样在前面加 "\" . 以\ 后引出的数字, 可以表示一个asc字符, 但\的数字最多不会超过3位.
定义含有多行的字符串时, 可以使用 [[ ]] 标识, 这样就不用在每行之间加\n作转义了.
lua 提供字符串与number类型之间的自动转换功能. 所谓的自动转换, 就是视调用时的函数行为, 决定当前的操作是按字符串处理, 还是按数值型处理, 比如: "10"+1, 这种情况会按数字处理, 因为字符 串没有加法操作, 如果想连接两个字符串, 要用 .. 运算符. 而 "10"..1, 其结果为: 101 . 当然, "a"+1这样的操作, 是非法的, 因为lua的转换, 并不是按asi来作, 而是等于执行了一个 atoi( "10"), 然后再加1.
在其它场合, 如果要将字符串转为数字, 使用tonumber, 转换失败时, 返回nil. 比如: tonumber("a")返回值就为nil. 如果要将数字转换成字符串, 有两种方法: 一种是使用tostring函数, 一种是使用连接符将数字和一个空串连接起来: 10.."", 其结果为:"10"
前面我们介绍过dofile函数, 其实dofile的实现如下:
function dofile (filename)
local f = assert(loadfile(filename))
return f()
end
loadfile函数很灵活, 我们可以多次调用loadfile的返回结果来反复执行一段代码. 但是如果出错loadfile将返回nil和一个错误提示信息.
loadstring功能和loadfile类似, 参数为要直接处理的script内容.
f = loadstring("i = i + 1")
i = 0
f(); print(i) --> 1
f(); print(i) --> 2
loadstring载入后相当于多了一个匿名函数定义,
-- file `foo.lua'
function foo (x)
print(x)
end
f = loadfile("foo.lua")
f() -- defines `foo'
foo("ok") --> ok
你也可以使用loadstring(s)()达到同样的效果.
使用loadstring时所用的变量都位于全局空间:
local i = 0
f = loadstring("i = i + 1") -- global i
g = function () i = i + 1 end --local i
The require Function
Lua提供了一个函数require用来载入库. require作的工作有些像dofile但有两点重要不同:require会在path指定的路径搜索文件, 而且require可以知道一个文件是否已被执行, 如果已被执行过, 那么不会再次被执行.
Lua使用的path和许多平台使用的标准path有些不同, 许多平台使用的path是一系列含有目标文件的文件夹,但是Lua不一样. Lua使用的ANSI C语言并没有path这样的概念, 因此Lua使用的是字符串匹配的方式. '?'代表文件名, 以';'隔开.
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
如果调用 require 'lili', 那么会尝试打开下列文件:
lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua
require会首先检查全局变量LUA_PATH, 如果LUA_PATH是一个字符串类型, 那么使用它的值. 否则检查环境变量LUA_PATH, 如果以上值都不存在, 那么使用一个定值, 这个值可以在编译LUA的时候方便的加以改变.
另一个require的主要功能是避免一个文件被执行多次. 为了实现这个目的, 它保存了一个table用来记录所有已经载入的文件. 注意, 这里的key值并不是真实的文件名而是一个虚拟文件名. 所以
require "foo"
require "foo.lua"
path是"?;?.lua", 那么foo.lua会被载入两次. 可以通过全局变量 _LOADED 访问到这个表的值.
path中可以含有固定文件的值, 例如:
?;?.lua;/usr/local/default.lua
在这种情况下, 如果 reqire 不能找到其他的匹配, 那么就使用/usr/local/default.lua.
文章来源于 http://www.luaer.cn
每一次
以下是一个简单的iterator factory, 用来遍历一个table.
function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t end
end
end
在这里, 每一次我们调用list_iter, 返回一个iterator, 这个iterator使用i, n来保存遍历时所需要的环境信息. 每一次我们调用这个iterator返回被遍历list的下一个值. 如果在我们遍历的list已经达到最后一个元素, 那么返回nil.
我们可以这样使用这个iterator:
t = {10, 20, 30}
iter = list_iter(t) -- creates the iterator
while true do
local element = iter() -- calls the iterator
if element == nil then break end
print(element)
end
我们也可以使用generic for循环, 这样功能相同, 但是代码更少:
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end
相比一般循环, generic for多做了一些事情, 它内部调用iterator factory产生iterator, 然后将iterator保存起来依次调用, 当iterator返回nil的时候退出循环.
以下是另一个iterator factory的例子:
function allwords ()
local line = io.read() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1 -- next position is after this word
return string.sub(line, s, e) -- return the word
else
line = io.read() -- word not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
虽然iterator factory有些复杂, 但是使用很简单直观:
for word in allwords() do
print(word)
end
一般说来, iterator是使用简单但是实现困难. 幸运的是多数情况下开发人员都不需要自己实现iterator而使用库中间已经定义好的iterator即可.
The Semantics of the Generic for
在上面我们使用upvalue用来保存iterator时所需要的环境信息, 其实Generic for就有保存iterator时所需要的环境信息的功能, 这样可以使代码更简洁高效.
Generic for的一般形式如下:
for <var-list> in <exp-list> do
<body>
end
<var-list>是一些变量的list, 以逗号分隔, <exp-list>是一些表达式的list, 以逗号分隔. 大多数情况<exp-list>只包含一个调用iterator factory的表达式:
for k, v in pairs(t) do
print(k, v)
end
<var-list>中的变量多数情况下也只有一个:
for line in io.lines() do
io.write(line, '\n')
end
在中的第一个变量称为控制变量, 如果它的值为nil, 那么循环退出. 在执行时首先会计算中的表达式, 应该返回3个元素, 第一个是iterator function, 第二个是一个常量用来记录一些环境信息, 最后一个是控制变量的初值.
function iter (a, i)
i = i + 1
local v = a
if v then
return i, v
end
end
function ipairs (a)
return iter, a, 0
end
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
可以看出, 以下表达式
for var_1, ..., var_n in explist do block end
与以下的循环是等价的.
do
local _f, _s, _var = explist
while true do
local var_1, ... , var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
block
end
end
Stateless Iterators
Stateless Iterator就是不含状态和环境信息的iterator. 如同上面所说的,Stateless Iterator的遵从Generic For的约定, 主要靠Generic For来保存状态和环境信息.
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
function iter (a, i)
i = i + 1
local v = a
if v then
return i, v
end
end
function ipairs (a)
return iter, a, 0
end
当Generic For调用ipairs时, 他返回三个值, 分别为iterator function, invariant state和control value. 以后每次进入循环调用iter函数时使用两个参数, invariant state和control value.
文章来源于http://www.luaer.cn
在LUA中, Function和Number, String一样属于基本类型. 凡是基本类型可以出现的地方Function一样可以出现. 这意味着Function可以存储在变量中,可以作为另一个Function的参数传递,也可以作为某个Function的返回值. 可以把Function德名字视为一种变量,它指向具体的Function实现. 这样的机制给程序设计带来了极大的弹性. 一个程序可以重写某个Function以便给他增加功能, 或者删除某个函数创建安全运行环境(SandBox).
a = {p = print}
a.p("Hello World") --> Hello World
print = math.sin -- `print' now refers to the sine function
a.p(print(1)) --> 0.841470
sin = a.p -- `sin' now refers to the print function
sin(10, 20) --> 10 20
function foo (x) return 2*x end 等价于 foo = function (x) return 2*x end
在调用LUA的Function时,所有的参数应该包围在'('和')'之间, 即使函数没有参数, '('和')'也不应该被省略.
print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
print(os.date())
这儿有一种例外情况, 如果Function只有一个参数且这个参数是literal string或者table constructor, 这时大括号keyi省略.
print "Hello World" <--> print("Hello World")
dofile 'a.lua' <--> dofile ('a.lua')
print [[a multi-line <--> print([[a multi-line
message]] message]])
f{x=10, y=20} <--> f({x=10, y=20})
type{} <--> type({})
对于面向对象程序设计, LUA支持一种特殊的语法支持, 这里的冒号称作冒号运算符. 例如 o:foo(x) 的意义是调用 o.foo 且把 o 作为额外的参数放到第一个参数的位置.
Function Definition
LUA中的函数定义和许多程序设计语言一样, 需要有一个函数名, 一系列的参数和函数体.
-- add all elements of array `a'
function add (a)
local sum = 0
for i,v in ipairs(a) do
sum = sum + v
end
return sum
end
函数的参数是局部变量, LUA中比较特殊的是调用函数时参数的个数可以和定义时不一样. LUA会调整参数的个数, 这一点很像多重赋值, 调用函数时所给的多余参数会被丢掉, 调用函数时如果参数不够后边的会被赋为nil.
例如函数定义为:
function f(a, b) return a or b end
如下调用:
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4 (5被抛弃)
这种机制和大多数强类型编成语言(C,C++,Java)不同, 不过这种做法带来了一些好处:
function incCount (n)
n = n or 1
count = count + n
end
这里1作为函数的默认参数, 如果调用时不给参数, 那么n的值就是nil, 否则就是用用户调用时给的值.
Multiple Results
多重返回值是LUA提供的一种并不符合传统, 但是十分方便的机制. 一些LUA预定义的函数也使用了多重返回值的机制, 例如 string.find 函数, 他的两个返回值分别表示查找到的字符串的起始和结束位置. 如果没找到返回值为nil.
s, e = string.find("hello Lua users", "Lua")
print(s, e) --> 7 9
使用多重返回值的方法很简单, 你只需要将返回的变量列举出来就可以了. 以下的例子函数返回一个数组中的最大值和最大值元素的索引.
function maximum (a)
local mi = 1 -- maximum index
local m = a[mi] -- maximum value
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})) --> 23 3
Variable Number of Arguments
在LUA中Function支持不定个数的参数, 用 ... 表示. 这和LUA中普通函数的调用是有所区别的. 调用函数时所给参数可以和定义时不同, 但这里其实是LUA编译器对参数作了自动补足, 调用时参数不够的话多余的置为nil, 调用时参数过多则将多余的抛弃. 并不是真正的参数个数不定.
printResult = ""
function print (...)
for i,v in ipairs(arg) do
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
这里...表示参数个数不确定, 当print调用时, 所有的参数都被存储在一个table内, 这个隐藏变量的名字叫arg, 除了函数的参数外, arg还含有一个额外的元素n, 用来表示参数的个数.
一个Function可以有多个返回值, 有时我们想指定使用返回值中的某一个而忽略其他的, 这时可以用 _ 表示忽略.
local _, x = string.find(s, p)
-- now use `x'
...
另一种方法是使用select函数,
print(string.find("hello hello", " hel")) --> 6 9
print(select(1, string.find("hello hello", " hel"))) --> 6
print(select(2, string.find("hello hello", " hel"))) --> 9
对于以下情况:
function g (a, b, ...) end
调用结果如下:
g(3) a=3, b=nil, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
Named Arguments
在LUA中, 如果一个函数有许多参数而且大多数参数都是可选的话, 将参数定义为table会带来一些额外的方便性, 你不需要记住参数的位置, 只需要记住参数的名字即可.
例如一个产生窗口的函数可能有许多参数,
function Window (options)
-- check mandatory options
if type(options.title) ~= "string" then
error("no title")
elseif type(options.width) ~= "number" then
error("no width")
elseif type(options.height) ~= "number" then
error("no height")
end
-- everything else is optional
_Window(options.title,
options.x or 0, -- default value
options.y or 0, -- default value
options.width, options.height,
options.background or "white", -- default
options.border -- default is false (nil)
)
end
调用时很简单, 你只需要给出参数的名字和值, 顺序无关紧要.
w = Window{ x=0, y=0 }
Closure
Function属于基本类型, 所以一个Function可以返回另一个Function.
function foo()
return function() return end
end
c1 = foo() --> 每次调用返回一个匿名的function对象.
c2 = foo()
print(c1) --> function: 0x87dd470
print(c2) --> function: 0x87dd490
c1和c2是不同的对象.
同样, 如果一个作为返回值的函数对象内使用了上层的局部变量, 每一个返回的函数对象内使用的这个局部变量都是不同的. 这种情况称作closure.
function foo()
local x=10
return function()
x=x+1
return x end
end
c1 = foo() --> 每次调用返回一个匿名的function对象.
c2 = foo()
print(c1) --> function: 0x87dd470
print(c2) --> function: 0x87dd490
print(c1()) --> 11
print(c1()) --> 12
print(c2()) --> 11
print(c1()) --> 13
print(c2()) --> 12
Proper Tail Calls
Proper Tail Calls是LUA的另一个有趣的特性, 在一个LUA函数中, 如果最后一个操作是返回一个函数调用, 例如 return g(...), 那么LUA不会把它当作一个函数调用而建立调用堆栈而只简单的跳转到另一个函数中.
function foo (n)
if n > 0 then return foo(n - 1) end
end
这个函数无论n是多大都不会发生堆栈溢出, 这里return foo(...)的功能相当于goto.
以下调用并不是Tail Calls, 因为函数调用返回后还作了其他的操作:
return g(x) + 1 -- must do the addition
return x or g(x) -- must adjust to 1 result
return (g(x)) -- must adjust to 1 result
在LUA中, 只有形如return g(...)是tail call. 然而g和他的参数可以是复杂的调用形式, 因为LUA会先计算表达式的值然后调用函数, 所以下面是一个tail call.
return x .foo(x[j] + a*b, i + j)
文章来源于http://www.luaer.cn