Chinaunix首页 | 论坛 | 博客
  • 博客访问: 66939
  • 博文数量: 7
  • 博客积分: 450
  • 博客等级: 下士
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-10 10:02
文章分类
文章存档

2010年(3)

2009年(1)

2008年(3)

我的朋友

分类: LINUX

2008-03-04 16:01:26

3.表达式(Expressions)
表达式其实表示的就是值。Lua中表达式包括:数字常量,字符常量,变量,一元和二元操作符号,函数呼叫(function calls)。表达式还包括一些非传统的函数定义,和表构造(table constructors)

3.1 算术运算符
Lua支持普遍的数学运算,其中包括: 二元运算符:+ - * / ^ (加减乘除)以及 一元运算符:- (负值)。上述所有的操作其操作对象都是实数,即操作数都是实数。
对于"^"(exponentiation)幂操作,Lua也提供了部分的支持。提供这种幂操作的主要用意是因为,Lua本身是一个很小的core解释器,而每次需要使用幂操作的时候,Lua都是通过调用C语言中的pow函数来实现的,而这种实现意味着程序员总是需要在Lua程序中链接C语言的的数学类库(mathematical library)。正是为了避免这种问题的发生,Lua在内核中提供了对幂操作的支持,且仅仅提供了对幂操作的操作的语句的解释。在Lua的语法优先级中,幂操作的优先级是最高的。通过这种对幂操作支持的数学类库(虽然幂操作被列入了标准类库中,但它并不是Lua内核的一部分,Lua内核只是提供了解释幂操作语句的功能。)很好的解决了上面出现的问题。

3.2 关系运算符
Lua提供了下面的关系操作:

   1.    <   >   <=  >=  ==  ~=

上述所有的操作符返回结果只有false或者true。
操作符==用于判断相等的操作;操作符~=用于判断两个值不相等的操作。我们可以在任意两个变量或者常量之间应用这两个操作。如果两个变量或者常量的类型不同,Lua认为两者不同。Lua的这种比较是根据它们的类型进行的。特别指出的是:nil只和自己相等。
Lua通过引用(reference)的方式比较tables,userdata,functions。也就是说当且仅当两者同时指向同一个对象时相等。

   1.    a = {}; a.x = 1; a.y = 0
   2.    b = {}; b.x = 1; b.y = 0
   3.    c = a

上述例子中,Lua认为a == c但是a ~=b

当我们比较两个数字或者字符的时候,我们通常是根据某一种排序的方式进行的。Lua比较数字按传统的数字大小进行,比较字符串按字母的顺序进行,但是字母顺序依赖于本地环境。因为不同的Lua设置,对于字母的排序是不同的。例如,采用European Latin-1的本地设置后,"acai" < "a?aí" < "acorde"是可以得到的,但是,如果我们采用其他的字符设置,可能上面三个字符串得到的结果可能就不对了。当我们比较两个类型不同的值的时候,请记住:"0"==0的返回结果是false。2<15
很明显也是返回真(true),但是,"2"<"15"返回的结果就是false了(按字母排序的!)。为了避免这种现象的发生,Lua将类型不同的值得比较定义为一个错误。

3.3 逻辑运算符
逻辑操作符包括:与(and),或(or),非(not)。逻辑运算符认为false和nil是假(false),除此以外所有的斗室真(true),0也是true。与(and)和或(or)的运算结果不是true和false,而是和它的两个操作数相关。例如:

   1.    a and b                      :如果a为false,则返回a,否则返回b
   2.    a or  b                      :如果a为true,则返回a,否则返回b
   3.    print(4 and 5)               --> 5
   4.    print(nil and 13)            --> nil
   5.    print(false and 13)          --> false
   6.    print(false or 5)            --> 5
   7.    print(4 or 5)                --> 4

与(and)和或(or)使用的是捷径赋值(short-cut evaluation)得方式进行运算的。即只有第一个表达式为true的时候,才会执行第二个表达式,否则,如果第一个表达式为false,则后面的表达式就不需要执行了。

一个很实用的技巧:如果x为false或者nil则给x赋初始值v,否则x保持原来的值不变。x = x or v 等价于 if not x then x = v end 。这个例子说明,设定x的默认值是v ,当x没有设定其他值为默认值的时候(包括当x没有设定什么状态为false的时候)。

另一个实用的技巧是:(a and b) or c(或者简写成:a and b or c,因为and的优先级比or姚高),它等价于C语言中的表达式:a ? b : c。假设b的值为非假(not false)。例如,我们可以得到两个数的最大数,这表达式可写成:

   1.    max = (x > y) and x or y

当x > y,则第一个表达式的返回值为真(true),此时判断第二个表达式(第二个表达式的返回值也是真(true),因为第二个表达式也是这个数字)所以or表达式的返回值是第一个表达式的返回值x。当x > y返回值为假(false)的时候,and表达式也是假(false),所以or返回第二个表达式的值y非(not)操作总是返回true或者false:

   1.    print(not nil)      --> true
   2.    print(not false)    --> true
   3.    print(not 0)        --> false
   4.    print(not not nil)  --> false

3.4 连接运算符
 Lua提供的字符连接运算符为".." (两点)。如果操作对象是一个数字,则Lua会将该数字对象转换成字符,然后进行连接。

   1.    print("Hello " .. "World")  --> Hello World
   2.    print(0 .. 1)               --> 01

值得注意的是:Lua中的字符是不可变的常量。字符连接符总是创建一个新的字符空间,而不用改变原来的操作对象的。

   1.    a = "Hello"
   2.    print(a .. " World")        --> Hello World
   3.    print(a)                    --> Hello

3.5 优先级
Lua中的操作优先级按照下表,优先级别高的在先的顺序排列如下:

从高到低的顺序:

   1.    ^
   2.    not  - (unary)
   3.    *   /
   4.    +   -
   5.    ..
   6.    <   >   <=  >=  ~=  ==
   7.    and
   8.    or

除了^(幂操作)和..(字符连接符)是右连接的外,所有的二元运算符都是左连接的。因此下面的表达式中,左边的表达式等价于右边的表达式:

   1.    a+i < b/2+1          <-->       (a+i) < ((b/2)+1)
   2.    5+x^2*8              <-->       5+((x^2)*8)
   3.    a < y and y <= z     <-->       (a < y) and (y <= z)
   4.    -x^2                 <-->       -(x^2)
   5.    x^y^z                <-->       x^(y^z)

当我们在写上述的表达式的时候,比较好的习惯是用圆括号“()”来区分其优先级,这样写,有利于我们查看和阅读。

 

3.6 表的构造(Table Constructors)
构造的过程是创建和初始化表的表达式。表(Table)是Lua提供的最具特别的功能之一,也是Lua强大和方便的重要机制之一。最简单的构造函数是{},用来创建一个空表.可以直接初始化数组:

   1.    days = {"Sunday", "Monday", "Tuesday", "Wednesday",
   2.        "Thursday", "Friday", "Saturday"}

Lua将用string "Sunday"初始化days[1](第一个元素索引为1,而不是0),用"Monday"初始化days[2]:

   1.    print(days[4])  --> Wednesday

构造器不仅可以使用常量,而且还可以使用任何表达式初始化:

   1.    tab = {sin(1), sin(2), sin(3), sin(4),
   2.        sin(5), sin(6), sin(7), sin(8)}

 如果想初始化一个表作为record使用,可以这样:

   1.    a = {x=0, y=0}

上面的表达式等价于:

   1.    a = {}; a.x=0; a.y=0

 不管用何种方式创建table,我们都可以在任何时候,向表中添加或者删除任何类型的字段(field)。例如:

   1.    w = {x=0, y=0, label="console"}
   2.    x = {sin(0), sin(1), sin(2)}
   3.    w[1] = "another field"
   4.    x.f  = w
   5.    print(w["x"])   --> 0
   6.     print(w[1])     --> another field
   7.     print(x.f[1])   --> another field
   8.     w.x = nil       -- remove field "x"

因为构造器仅仅影响表的初始化,故所有上面被创建的表都可以在我们需要的时候,对字段(field)进行新增和删除。每次调用构造器,Lua都会创建一个新的table,利用这种特性,我们可以使用table构造一个list:

   1.     list = nil
   2.     for line in io.lines() do
   3.       list = {next=list, value=line}
   4.     end

这段代码从标准输入读进每行,然后反序形成链表。每一个节点(node)在整个list中代表的是一个表(table),这个表有两个字段:一个是保存字符串的value,还有一个字段是一个引用(reference),它保存得是下一个节点的地址。下面的代码打印list的内容:

   1. l = list
   2. while l do
   3.   print(l.value)
   4.   l = l.next
   5. end

(上面的这个应用,事实上我们是应用list作为一个栈(stack),所以,我们在打印的时候其顺序是反的。)虽然上面的例题是有用的,但是我们很难将上面的例题应用于实际的Lua程序当中。通常来说,我们将list作为数组(array)来使用会比作为栈(stack)要好。具体的使用方法请参照第11章节的内容。在同一个构造函数中可以将列表风格和record风格混在一起进行初始化,例如:

   1. polyline = {color="blue", thickness=2, npoints=4,
   2.            {x=0,   y=0},
   3.            {x=-10, y=0},
   4.            {x=-10, y=1},
   5.            {x=0,   y=1}
   6.            }

这个例子也表明构造器可以嵌套来表示复杂的数据结构。而每一个元素,例如polyline[1], ..., polyline[4]代表着这个表(Table)的一条记录(Record):

   1.    print(polyline[2].x)    --> -10

上面两种构造器的初始化方式存在其自身的局限。例如,你不能使用负索引(negative indices)或者字符串索引(string indice)来初始化你的字段(field),并且负索引(negative indices)或者字符串索引(string indice)也不能被恰当的表示,为了迎合这种需要,下面介绍一种更一般的初始化方式,我们明确指出索引的意义,并且用一个表达式来初始化这个索引:

   1.    opnames = {["+"] = "add", ["-"] = "sub",
   2.    ["*"] = "mul", ["/"] = "div"}
   3.    i = 20; s = "-"
   4.    a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
   5.    print(opnames[s])    --> sub
   6.    print(a[22])         --> ---

虽然这种语句在书写的时候会非常的麻烦,但是这种方法有利于日后的扩展,而且写出来的代码,也比较容易阅读。list风格初始化和record风格初始化是这种一般初始化的特例:
{x=0, y=0}

它等价于:
   1.    {["x"]=0, ["y"]=0}

而构造器

   1.    {"red", "green", "blue"}

等价于:

   1.    {[1]="red", [2]="green", [3]="blue"}

如果真的想要数组下标从0开始,按照下面的书写方式其实也不是很困难,如下所示:

   1.    days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday",
   2.            "Thursday", "Friday", "Saturday"}

从上面的例子,我们可以得到,"Sunday"的索引值为0。而这个索引将不会影响到其它的字段(field)。并且,很自然的"Monday"的索引值为1,但是这样做的结果是,构造器将认"Monday"是整个list的第一个节点(node)。所以,尽管我们可以很容易的指定下标为0的数组,我们还是不建议使用这种方法,因为在Lua中大部分的函数都认为数组的下标是从1开始的,所以,当这样的数组被建立后,Lua解释器在对其进行运算的时候,是不会得到正确的处理结果的。在构造函数的最后的","是可选的,可以方便以后的扩展。

   1.    a = {[1]="red", [2]="green", [3]="blue",}

上面的这个写法,给了我们很大的便利,通过这样的方法,可以让我们很容易的写出我们的程序,很容易的产生出一个Lua表(table),因为,有了这个逗号,Lua解释器将不会将最后一个成员作为一个特殊的情况进行处理了。在构造函数中域分隔符逗号(",")可以用分号(";")替代,通常我们使用分号用来分割不同类型的表元素。

   1.    {x=10, y=45; "one", "two", "three"}

 
 
阅读(2474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~