Programming in Lua (基础篇)

2012-04-02 wcdj


$ lua -v

Lua 5.2.0 Copyright (C) 1994-2011 Lua.org, PUC-Rio

目录

0 什么是Lua?... 1

1 Lua简单介绍... 1

2 基本语法... 3

3 函数... 4

4 实践代码... 6

5 参考:... 14

 

0 什么是Lua?

Lua本身是以简单优雅为本,着眼于处理那些C不擅长的任务;

Lua本身完全遵循ANSI C而写成,只要有C编译器的地方,Lua便可发挥她的力量;

对那种在产品的生命周期内变化比较多的应用方向,使用Lua可以更方便的适应变化;

Lua有很高的执行效率,统计表明Lua是目前平均效率最高的脚本语言;

Lua的使用者分为三类:使用Lua嵌入到其他应用中的、独立使用Lua的、将Lua和C混合使用的;


1 Lua简单介绍

n  和其他脚本一样,Lua有两种执行方式。

(1) 命令行交互式。即不带参数运行,使用文件结束符退出Ctrl-D in Unix, Ctrl-Z in DOS/Windows,或者调用os.exit()退出。

(2) 调用脚本执行(带脚本文件参数运行)。

(3) 在命令行交互过程中,可以使用dofile函数调用外部的脚本文件来加载需要的library。

n  Lua是动态类型语言,变量不用类型定义。

全局变量不需要声明,给一个变量赋值后即创建了这个全局变量。访问一个没有初始化的全局变量也不会出错,只是得到结果为 nil。当且仅当一个变量不等于nil时,这个变量才存在。

Lua中有8个基本类型:

nil, boolean, number, string, userdata,function, thread, table

注意:

(1) 函数type可以测试给定变量或值的类型。

(2) 变量没有预定义的类型,每一个变量都可能包含任一种类型的值。

(3) 可以使用函数像使用其他值一样使用(将一个函数赋值给一个变量)。

(4) nil是Lua中特殊的类型,一个全局变量没有被赋值以前默认值为nil;给全局变量赋值为nil可以删除该变量。

(5) boolean有两个取值false和true。Lua中所有的值都可以作为条件。在控制结构的条件中除了false和nil为假,其他值都为真。所以,Lua认为0和空串都是真。

(6) number表示实数,Lua中没有整数。Lua的number可以处理任何长整数不用担心误差。

(7) string指字符的序列。string和其他对象一样,Lua自动进行内存分配和释放,一个string可以只包含一个字母也可以包含一本书,Lua可以高效地处理长字符串,1M的string在Lua中是很常见的。可以使用单引号或双引号表示字符串。为了风格统一,最好使用一种,除非两种引号嵌套情况,对于字符串中含有引号的情况还可以使用转义符\来表示。

(8) Lua中的转义序列和C语言中类似,特殊的有:

\[ 左中括号

\] 右中括号

(9) 还可以使用 [[...]] 表示字符串。这种形式的字符串可以包含多行,可以嵌套且不会解释转义序列。这种形式的字符串用来包含一段代码是非常方便的。

(10) Lua会自动在string和number之间自动进行类型转换,当一个字符串使用算术操作符时,string就会被转成数字。反之,当Lua期望一个string而碰到数字时,会将数字转成string。

Note: 尽管字符串和数字可以自动转换,但两者是不同的,像 10 == "10" 这样的比较永远都是错的(比较的时候,不会自动转换,需要显示转换!)。如果需要显示将string转成数字可以使用函数tonumber(),如果string不是正确的数字该函数将返回nil。反之,可以调用tostring()将数字转成字符串。

(11) .. 在Lua中是字符串连接符,当在一个数字后面写 .. 时,必须加上空格以防止被解释出错。

(12) Function和其他变量相同,函数可以存储在变量中,可以作为函数的参数,也可以作为函数的返回值。(这个特性给了Lua很大的灵活性:一个程序可以重新定义函数增加新的功能或者为了避免运行不可靠代码创建安全运行环境而隐藏函数)

(13) Userdata可以将C数据存放在Lua变量中,userdata在Lua中除了赋值和相等比较外没有预定义的操作。Userdata用来描述应用程序或者使用C实现的库创建的新类型。

n  任何一门语言都有词法约定。

Lua的标识符约定与C语言相同。

注意:

(1) Lua的保留字,不能当作标识符。

and; break; do; else; elseif; end;false; for; function; if; in; local; nil; not; or; repeat; return; then; true;until; while;

(2) Lua是大小写敏感的。

(3) 单行注释的方法:--

(4) 多行注释的方法:--[[    --]]

n  一些常用的命令行选项

-e                  直接将命令传入Lua

-l                   加载一个文件

-i                   进入交互模式

-PROMPT      内置变量作为交互模式的提示符      

注意:

全局变量arg存放Lua的命令行参数。在运行以前,Lua使用所有构造参数构造arg表,脚本名索引为0,脚本的参数从1开始增加。脚本前面的参数从-1开始减少。

n  表达式

Lua中的表达式包括:数字常量、字符串常量、变量、一元和二元运算符、函数调用。

(1) 算术运算符

二元运算符:+ - * /^ 加减乘除幂

一元运算符:- 负值

这些运算符的操作数都是实数。

(2) 关系运算符

<  >   <=   >=   ==   ~=

(3) 逻辑运算符

and  or   not

注意:

逻辑运算符认为false和nil是假(false),其他为真(0也是真)。

a and b

如果a为false,则返回a,否则返回b

a or b

如果a为true,则返回a,否则返回b

C语言中的三元运算符

A ? B : C

在Lua中可以实现为:

(A and B) or C

not的结果一直返回false或者true。

(4) 连接运算符

..   两个点

注意:

字符串连接,如果操作数为数字,Lua将数字转成字符串。

n  表的构造(table)

构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组。

注意:

(1) 不推荐数组下标从0开始,否则很多标准库不能使用。

(2) 在构造函数的最后的“,”是可选的,可以方便以后的扩展。

(3) 在构造函数中域分隔符逗号可以用分号代替,通常使用分号来分割不同类型的表元素。


2 基本语法

 

n  赋值语句

Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。(遇到赋值语句,Lua会先计算右边所有的值,然后再执行赋值操作)

注意:

当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

A. 变量个数 > 值的个数 (按变量个数补足nil)

B. 变量个数 < 值的个数 (多余的值会被忽略)

Note: 如果要对多个变量赋值,必须依次对每个变量赋值(常见错误)。

n  局部变量与代码块

使用local创建一个局部变量。与全局变量不同,局部变量只在被声明的那个代码块内有效。

应该尽可能使用局部变量:

(1) 避免命名冲突;

(2) 访问局部变量的速度比全局变量更快;

可以给block划定一个明确的界限:

do .. end 内的部分(当想更好地控制局部变量的作用域的时候这是很有用的)

n  控制结构语句

(1) if有3种形式

(2) while语句

(3) repeat-until语句

(4) for有2种形式

A: 数值for循环

for var = exp1, exp2, exp3 do

Loop-part

end

for将用exp3作为step从exp1(初始值)到exp2(终止值),执行loop-part。其中,exp3可以忽略,默认step = 1。

注意:

(1) for中的三个表达式只会被计算一次,并且是在循环开始前。

(2) 控制变量var是局部变量自动被声明,并且只在循环内有效。

(3) 如果需要保留控制变量的值,需要在循环中将其保存。

(4) 循环过程中不要改变控制变量的值,那样做的结果是不可预知的。如果要退出循环,使用break语句。

B:范型for循环

for i, val in ipairs(a) do print(v) end

范型for遍历迭代子函数返回的每一个值(val)。


3 函数

注意:

(1) 调用函数的时候,如果参数列表为空,必须使用()表明是函数调用。但是,当函数只有一个参数并且这个参数是字符串或者表结构的时候,()是可选的。

(2) Lua也提供了面向对象方式调用函数的语法,比如o:foo(x)与o.foo(o, x)是等价的。

(3) Lua使用的函数可以是Lua编写的也可以是其他语言编写的,对于Lua程序员来说用什么语言实现的函数使用起来都一样。

(4) Lua函数实参和形参的匹配与赋值语句类似,多余部分被忽略,缺少部分用nil补足。

(5) Lua函数可以返回多个结果值。比如,string.find,其返回匹配串开始和结束的下标,如果不存在匹配串返回nil。Lua函数中,在return后列出要返回的值的列表即可返回多值。

Note:

return f() 这种类型返回f()返回的所有值;

print((foo())) 可以使用圆括号强制使调用返回一个值;

(6) 函数多值返回的特殊函数unpack,其接受一个数组作为输入参数,返回数组的所有元素。

(7) Lua函数可以是可变参数,和C语言类似在函数参数列表中使用“...”表示函数有可变的参数。Lua将函数的参数放在一个arg的表中,除了参数以外,arg表中还有一个域n表示参数的个数。

(8) 命名参数

 

再论函数

 

(1) 闭包

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。

(2) 非全局函数(局部函数)

Lua中函数可以作为全局变量也可以作为局部变量。函数作为表table的域:

A: 表和函数放在一起

B: 使用表构造函数

 

 Lib_wcdj = {}

 Lib_wcdj.add = function (x, y) return x + yend

 Lib_wcdj.subtract = function (x, y) return x -y end

 

 Lib_gerry = {

    multiply = function (x, y) return x * y end,

    divide = function (x, y) return x / y end

 }

 print(Lib_wcdj.add(1, 2))

 print(Lib_wcdj.subtract(1, 2))

 print(Lib_gerry.multiply(2, 3))

 print(Lib_gerry.divide(5, 2))

 

 --[[

 3

 -1

 6

 2.5

 --]]

 

(3) 正确的尾调用

尾调用是一种类似在函数结尾的goto调用,当函数最后一个动作是调用另外一个函数时,称这种调用为尾调用。例如:g的调用是尾调用

function f(x)

return g(x)

end

f调用g后不会再做任何事情,这种情况下当被调用函数g结束时,程序不需要返回到调用者f,所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。一些编译器比如Lua解释器利用这种特性在处理尾调用时,不使用额外的栈,称这种语言支持正确的尾调用。由于尾调用不需要使用栈空间,那么尾调用递归的层次是可以无限制的,不会导致栈溢出。

注意:

必须明确什么情况属于尾调用。


4 实践代码

--[[
test.lua 
2012-04-02 wcdj
-- --]]

-- defines a factorial function
function fact (n)
	if n == 0 then
		return 1
	else
		return n*fact(n-1)
	end
end

function norm(x, y)
	local n2 = x^2 + y^2
	return math.sqrt(n2)
end

function twice (x)
	return 2*x
end


print("enter a number: ")
input = io.read() -- read a number
data_in = tonumber(input)

if data_in == nil then
	print("invalid number")
else
	print(fact(data_in))
end

print(tostring(10) == "10") -- true
print(10 .. "" == "10")		-- true

--[[
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0]  = "script"
arg[1]	= "a"
arg[2]  = "b"
--]]

--[[
print(arg[-3])
print(arg[-2])
print(arg[-1])
print(arg[0])
print(arg[1])
print(arg[2])
print(arg[3])
--]]

print(type("Hello world"))
print(type(10.4*3))
print(type(print))
print(type(type))
print(type(true))
print(type(nil))
print(type(type(X)))

--[[
string
number
function
function
boolean
nil
string
--]]

print(type(a))
a = 10
print(type(a))
a = "a string !"
print(type(a))
a = print
print(type(a))
a(type(a))

--[[
nil
number
string
function
function
--]]

a = "one string"
-- change string parts
b = string.gsub(a, "one", "another")
print(a)
print(b)

--[[
one string
another string
--]]

print("10" + 1)
print("10 + 1")
-- error
-- print("hello" + 1)

--[[
11
10 + 1
--]]


print(10 .. 20)
-- malformed number near '10..20'
-- print(10..20)
print("I'm " .. 10 .. " years old!")

--[[
1020
I'm 10 years old!
--]]

print(4 and 5)
print(nil and 6)
print(false and 13)
print(4 or 5)
print(false or 5)

--[[
5
nil
false
4
5
--]]

print(not nil)
print(not false)
print(not 0)
print(not not nil)

--[[
true
true
false
false
--]]

print("Hello " .. "World")
print(0 .. 1)

--[[
Hello World
01
--]]

days = {"Sunday", 
		"Monday", 
		"Tuesday", 
		"Wednesday",
		"Thursday",
		"Friday",
		"Saturday",
		}
print(days[4])

a = {x = 100, y = 200; "one", "two", "three"}
print(a.x)
print(a[0])
print(a[1])
print(a[3])
print(a[4])

b = {}
b.x = 300
b.y = 400
print(b.x)


--[[
Wednesday
100
nil
one
three
nil
300
--]]

a, b, c = 10, 2^4, "wcdj"
print(a)
print(b)
print(c)
a, b, c = 0
print(a)
print(b)
print(c)

--[[
10
16
wcdj
0
nil
nil
--]]

-- swap
x = 1
y = 3
x, y = y, x
print(x)
print(y)

--[[
3
1
--]]

x = 100
if "wcdj" ~= "gerry" then
	local x
	x = 1;
	print(x)
else
	print(x)
end
print(x)

--[[
1
100
--]]

x = 100
local x = 1
print(x)
do
	local x = 2
	print(x)
end
print(x)

--[[
1
2
1
--]]

print(8*9, 1/3)
a = math.sin(3) + math.cos(10)
print(os.date())

--[[
72      0.33333333333333
Mon Apr  2 14:54:02 2012
--]]

print "Hello World"
print [[My name is wcdj]]

--[[
Hello World
My name is wcdj
--]]

a, b = string.find("hello Lua users", "Lua")
print(a, b)

--[[
7       9
--]]

function maximum(a)
	local max_idx = 1
	local max = a[max_idx]

	for i, val in ipairs(a) do
		if val > max then
			max_idx = i
			max = val
		end
	end
	return max, max_idx
end

print(maximum({8, 10, 23, 12 ,5}))
print(maximum{8, 10, 23, 12 ,5})

--[[
23      3
23      3
--]]

function newCounter()
	local i = 0
	return function() -- anonymous function
		i = i + 1
		return i
	end
end

add = newCounter()
print(add())
print(add())

--[[
1
2
--]]

Lib_wcdj = {}
Lib_wcdj.add = function (x, y) return x + y end
Lib_wcdj.subtract = function (x, y) return x - y end

Lib_gerry = {
	multiply = function (x, y) return x * y end, 
	divide = function (x, y) return x / y end
}
print(Lib_wcdj.add(1, 2))
print(Lib_wcdj.subtract(1, 2))
print(Lib_gerry.multiply(2, 3))
print(Lib_gerry.divide(5, 2))

--[[
3
-1
6
2.5
--]]


5 参考:

[1] Programming in Lua http://www.lua.org/pil/

[2] Lua 5.2 Reference Manual http://www.lua.org/manual/5.2




你可能感兴趣的:(Programming in Lua (基础篇))