您的位置:首页 > 编程语言 > Lua

<Lua程序设计(第二版)>书摘-3

2014-04-07 00:46 281 查看
资料摘自<Lua程序设计(第二版)>

语句

赋值

a = "hello".."world"

t.n = t.n + 1

c,b = 10,2*x --多重赋值,也就是一下子将多个值赋予多个变量,每个值或每个变量之间以逗号分隔,赋值后c为10,b为2*x

在多重赋值中,Lua先对等号右边的所有元素求值,然后才执行赋值。这点与其它语言不同:

x,y = y,x --交换x与y

a[i],a[j] = a[j],a[i] --交换a[i]与a[j]

Lua总是会将等号右边值的个数调整到与左边变量的个数一致。规则是:若值的个数少于变量的个数,那么多余的变量会被赋为nil;若值的个数更多的话,那么多余的值会被"静悄悄地"丢弃掉:

a,b,c = 0,1

print(a,b,c) --> 0 1 nil

a,b = a + 1,b + 1, b+2 --其中b+2会被忽略

print(a,b) -->1 2

a,b,c = 0

print(a,b,c) -->0 nil nil

局部变量与块(block)

相对于全局变量,Lua还提供了局部变量。通过local语句来创建局部变量:

j = 10 --全局变量

local i = 1 --局部变量

与全局变量不同的是,局部变量的作用域仅限于声明它们的那个块。一个块(block)是一个控制结构的执行体、或者是一个函数的执行体再或者是一个程序块(chunk):

x = 10

local i = 1 --程序块中的局部变量

while i<=x do

local x = i*2 --while循环体中的局部变量

print(x) -->2,4,6,8,...

i = i + 1

end

使用do-end可以显式地界定一个块,每当输入了do时,Lua就不会单独地执行后面的每行的内容,而是直至遇到一个相应的end时,才会执行整个块的内容:

do

local a2 = 2*a

local d = (b^2-4*a*c)^(1/2)

x1 = (-b + d)/a2

x2 = (-b - d)/a2

end --a2和d的作用域至此结束

print(x1,x2)

"尽可能地使用局部变量"是一种良好的编程风格。局部变量可以避免将一些无用的名称引入全局环境,避免搞乱了全局环境。此外,访问局部变量比访问全局变量更快。最后,一个局部变量通常会随着其作用域的结束而消失,这样便使垃圾收集器可以释放其值。

Lua将局部变量的声明当做语句来处理。因此可以在任何允许书写语句的地方书写局部变量的声明。所声明的局部变量的作用域从声明语句开始,直至所在块的结尾。声明语句中还可以包含初始化赋值,其规则与普通的赋值语句完全一样:额外的值值会被丢弃;额外的变量会被赋予nil。如果一条声明语句没有初始化赋值,那么它声明的所有变量都会初始化为nil:

local a,b = 1,10

if a < b then

print(a) -->1

local a --具有隐式的nil

print(a) -->nil

end

print(a,b) -->1 10

在Lua中,有一种习惯写法是:

local foo = foo

这句代码创建了一个局部变量foo,并将用全局变量foo值初始化它。如果后续其他函数改变了全局foo的值,那么可以在这里先将它的值保存起来。这种方式还可以加速在当前作用域中对foo的访问。

控制结构

Lua提供了一组传统的、小巧的控制结构,包括用于条件执行的if,用于迭代的while、repeat和for。所有的控制结构都有一个显式的终止符:if、for和while以end作为结尾,repeat以until作为结尾。

控制结构中的条件表达式可以是任何值,Lua将所有不是false和nil的值视为"真"。

if then else

if语句先测试条件,然后根据测试结果执行then部分或else部分。else部分是可选的。

if a < 0 then a=0 end

if a<b then return a else return b end

if line>MAXLINES then

showpage()

line=0

end

若要编写嵌套的if,可以使用elseif。它类似于在else后面紧跟一个if,它还可以避免在这样的嵌套中出现多个end:

if op == "+" then

r = a + b

elseif op == "-" then

r = a - b

elseif op == "*" then

r = a * b

elseif op == "/" then

r = a / b

else

error("invalid operation")

end

注意Lua不支持switch语句。

while

与其他语言中的while循环一样,Lua先测试while的条件。如果条件为假,那么循环结束;不然,Lua执行循环体,并重复这一过程。

local i = 1

while a[i] do

print(a[i])

i = i + 1

end

repeat

一条repeat-until语句重复执行其循环体直到条件为真时结束。测试是在循环体之后做的,因此循环体至少会执行一次。

--打印输入的第一行不为空的内容

repeat

line = io.read()

until line ~= ""

print(line)

与其他大多数语言不同的是,在Lua中,一个声明在循环体中的局部变量的作用域包括了条件测试:

local sqr = x/2

repeat

sqr = (sqr + x/sqr)/2

local error = math.abs(sqr^2-x)

until error <x/10000 --在此仍可以访问error

数字型for

for语句有两种形式:数字型for和泛型for

数字型for的语法如下:

for var = exp1,exp2,exp3 do

<执行体>

end

var从exp1变化到exp2,每次变化都以exp3作为步长递增var,并执行一次"执行体"。第三个表达式exp3是可选的,若不指定的话,Lua会将步长默认为1。以下是这种循环的一个典型示例:

for i= 1,f(x) do print(i) end

for i = 10,1,-1 do print(i) end

如果不想给循环设置上限的话,可以使用常量math.huge:

for i = 1,math.huge do

if(0.3*i^3-20*i^2-500 >= 0) then

print(i)

break

end

end

为了更好际使用for循环,还需要了解一些小细节。首先,for的3个表达式是在循环开始前一次求值的。例如,上例中的f(x)只会执行一次。其次,控制变量会被自动地声明为for语句的局部变量,并且仅在循环体内可见。因此,控制变量在循环结束后就不存在了:

for i =1 ,10 do print(i) end

max = i --可能是错误的。这是访问的是一个全局的i

如果需要在循环结束后访问控制变量的值,必须将该值保存到另一个变量中:

--在一个列表中查找一个值

local found = nil

for i = 1,#a do

if a[i]<0 then

found = i --包含i的值

break

end

end

print(found)

最后一点,不要在循环过程中修改控制变量的值,否则会导致不可预知的效果。如果想在for循环正常结束前终止循环,可以像上例中那样使用break语句。

泛型for

泛型for循环通过一个迭代器函数来遍历所有值:

--打印数组a的所有值

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

Lua的基础库提供了ipairs,这是一个用于遍历数组的迭代器函数。在每次循环中,i会被赋予一个索引值,同时v被赋予一个对应于该索引的数组元素值。下面是另一个类似的示例,演示了如何遍历一个table中所有的key:

--打印table t中所有的key

for k in paris(t) do print(k) end

从外观上看泛型for比较简单,但其实它是非常强大的。通过不同的迭代器,几乎可以遍历所有的东西,而且写出的代码极具可读性。标准库提供了几种迭代器,包括用于迭代文件中每行的(io.lines)、迭代table元素的(pairs)、迭代数组元素的(ipairs)、迭代字符串中单词的(string.gmatch)等。当然,读者还可以编写自己的迭代器。虽然泛型for的使用非常简单,但编写迭代器函数却有不少细节需要注意。

泛型for循环与数字型for循环有两个相同点:1,循环变量是循环体的局部变量;2,决不应该对循环变量任何赋值

对于泛型for的使用,再来看一个更具体的示例。假设有这样一个table,它的内容是一周中每天的名称:

days = {"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}

现在要将一个名称转换成它在一周中的位置。为此,需要根据给定的名称来搜索这个table。然而在Lua中,通常更有效的方法是创建一个"逆向table"。例如这个逆向table叫revDays,它以一周中每天的名称作为索引,位置数字作为值:

revDays={["sunday"]=1,["monday"]=2,["tuesday"]=3,["wednesday"]=4,["thursday"]=5,["friday"]=6,["saturday"]=7}

接下来,要找出一个名称对应的序号,只需用名字来索引这个reverse table即可:

x = "tuesday"

print(revDays[x]) -->3

当然,不必手动声明这个逆向table,而是通过原来的table自动地构造出这个逆向table:

revDays={}

for k,v in pairs(days) do

revDays[v]=k

end

这个循环会为每个元素进行赋值,其中变量k为key(1,2,...),变量v为value("sunday","monday",...)

break与return

break和return语句用于跳出当前的块。

break语句用于结束一个循环,它只会跳出包含它的那个内部循环。return语句用于从一个函数中返回结果,或者用于简单地结束一个函数的执行。任何函数的结尾处都有一句隐式的return。所以如果有一个函数,它没有值需要返回,那么就无须在其结尾处添加return语句。

由于语法构造的原因,break或return只能是一个块的最后一条语句。换句话说,它们应是程序块的最后一条语句,或者是end、else或until前的一条语句。例如,下例中的break就是then块的最后一条语句。

local i=1

while a[i] do

if a[i] == v then break end

i = i+1

end

因为那些位于return或break之后的语句将无法执行到,所以通常只能在上述几个位置使用这些语句。然而有时可能希望在一个块的中间插入一句return或break。例如,准备调试一个函数,但又不想执行该函数的内容。在这种情况下,可以使用一个显式的do块来包住一条return语句:

function foo()

return --<<语法错误

--在下一个块中return就是最后一条语句

do return end --ok

<其他语句>

end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: