魔兽世界编程宝典读书笔记-3

来源:互联网 发布:苹果app看书软件 编辑:程序博客网 时间:2024/04/28 01:16
第3章             基本的函数和控制结构
本章首先介绍解释函数的概念,并且引 导你创建几个你自己的函数,而剩下的内容将会介绍基本的循环和条件语句。
3.1        使用函数
函数是程序的一部分,它可以用来执行 复杂的计算或简单地重复某个任务。当调用一个函数的时候,它可能会接收到几个参数,即在执行过程中函数可以使用的数据。当一个函数完成时(我们称为结束调 用),它可以将任意多个值返回调用函数的位置。
3.1.1创 建函数
function关键字可以用来创建 新的函数,然后可以将其存放在一个变量中,或者直接调用。基本的函数定义看上去应该如下所示:
> hello=function()
>> print("Hello 魔兽世界")
>> end
函数的结构会从你输入 function的地方开始,然后一直持续到对应的end关键字,在这两个分隔符中间的部分构成了这个函数的主体。Lua解释器识别新的代码块输入,然后 提示符会发生改变以表示你正在继续同一个代码块的输入,直到最后的end为止。
在上面的例子中,我们创建了一个没有 参数的函数,在结束之前输出了一句话:“Hello 魔兽世界”,并将这个函数赋值给了变量hello。现在,你不需要每个用的时候再打一遍print的语句了,你只需要直接输入hello()来调用这个新 的函数即可。
> hello()
Hello 魔兽世界
3.1.2局 部函数
既然函数可以赋值给一个变量,而变量 又可以声明为局部的,所以我们可以声明一个局部函数,如下:
> do
>> local hello2=function()
>> print("还是魔兽世界")
>> end
>> hello2()
>> end
还是魔兽世界
首先我们声明了一个do..end 块,然后在这个块里我们声明了一个Hello2的变量,把函数赋值给了它。最后在这个块结束的地方进行了Hello2的调用。在我们打完最后一个关键字 end的时候,我们就会看到Hello2这函数的结果输出。Hello2这个函数是不能在这个块的外部被调用的,如果你在块的外部调用Hello2,你会 收到一个错误信息:
> hello2()
stdin:1: attempt to call global 'hello2' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
在错误信息里显示Lua解释器把 Hello2当作一个全局(global)的对象来引用了,这自然是不对的。在剩余的章节中你会看到更多关于局部函数的应用,你也会真正理解局部函数所带 来的好处,而这一章主要使用的都属于全局函数。
3.1.3函 数的参数和返回值
当调用一个函数时,它可以接收任意多 个参数供其使用。另外,一个函数可以在它完成的时候返回任意多个值(默然说话:这是Lua跟目前流行的各种编程语言最大的不同,绝大部分流行的编程语言中,函数 都只能返回一个值)。
1.将摄氏温度 转化为华氏温度
温度转化由下面的转换公式给出:“将 华氏温度乘以1.8然后加上32”。与其用一个简单的数字推导解答这个问题,不如写一个函数,它接收一个参数值然后将转换的值作为结果返回。将下面的内容 输入Lua解释器中:
> convert_c2f=function(celsius)
>> local converted=(celsius*1.8)+32
>> return converted
>> end
这里创建了一个函数,它仅有1个参数 celsius。新函数的第一行计算转换的值,第二行将其返回。为了验证结果,输入下列命令:
> print(convert_c2f(0))
32
> print(convert_c2f(-40))
-40
当调用函数时,把参数(第一次是0, 第二次是-40)赋值给了局部变量celsius,函数会根据我们传入的参数返回不同的值。这样,我们就可以使用我们自己定义的转换器函数来完成任意的转 换,而不需要每一次都写一遍转换公式了。
3.1.4函 数是对象
在Lua中每个函数都是一个对象,它 的类型是function。这些对象可以被比较(使用==或~=),赋值给一个变量,传递给函数,从函数中返回,或者作为table中的键(table是 第4章的主题)。但凡能作以上操作的值都被称为“一级对象”(first-class object)。
默然说话:这里的“一级对 象”的说法,是从别的翻译书里看来的。这本书的原文是first-class object,而另一些书中又写为first-level object(也就是一级对象的原文),我觉得“一级对象”的翻译更贴切一些,更详细的解释我也说不清楚,总之是一个名词,用来表达对象的分类的,可能想 了解的更为详细一些,得参考关于“如何实现一门计算机语言”这一话题的关于文章吧。
默然说话:关于函数,我还想 说明的一点是,在Java等流行编程语言中,函数只是类的一部分,它不是对象。所以,你不能将一个函数赋值给一个变量或者进行相等不相等的比较,你也不可 以将一个函数作为Map的键,你更不可以在一个函数中去定义另一个函数。这样的函数似乎就被称为“二级对象”。
3.2        用if语句进行判断
if是Lua中进行判断的基础,它支 持简单的条件,也支持复杂的语句。最基本的if语句语法如下:
if <布尔表达式> then
       --语句块
end
3.2.1简 单的条件
如果if语句的布尔表达式为真,则执 行其中的语句块,否则就不执行,举个例子:
conditional_test=function(num)
print("你传入了:" .. num)
if (num==7) then
print("你找到了这个魔术数字!")
 end
end
这个例子声明了一个函数,它可以将你 传入的参数给显示出现,并且在你传入7的时候,它会多显示一条信息。你可以使用下面的语句来测试它。
> conditional_test(3)
你传入了:3
> conditional_test(7)
你传入了:7
你找到了这个魔术数字!
> conditional_test(13)
你传入了:13
有一点需要说明的是,if后面的条件 并不一定要加上小括号,只是加上小括号以后可以让你的代码更具有可读性。
3.2.2复 杂的表达式
在Lua中几乎所有的值都可以做为条 件,因为在Lua中所有的值都有其布尔的解读(默然说话:在前面曾经提过,只有两种情况 表示假,false关 键字和nil关键字,其他的都可以认为是真)。下面都可以作为有效的条件使用:
name
type(name)==”string”
(not anonymous_flag) and type(name)==”string”
第一个例子简单地测试变量是不是除了 false和nil以外的其他东西,第二个例子测试变量name的类型是不是字符串。最后一个例子测试anonymous_flag是否为false或者 nil,并且name的类型是否为string
3.2.3扩 展的条件语句
if语句可以允许进行多条件的判断。 完整的if语句的语法 是:
if <布尔表达式> then
       --语句块1
elseif <布尔表达式> then
       --语句块2
elseif <布尔表达式> then
       --语句块3
else
       --语句块4
end
当解释器运行这个语句块的时候,它依 次测试每个布尔值表达式。当某一个布尔表达式为真时,执行它所对应的语句块,如果所有的条件都不为真,就执行else部分的语句块。
3.2.4显 示个人问候信息
条件语句可以用来验证函数的参数。举 个例子,考虑一个接收人名并进行问候的函数(默然说话:很不好意思,Java写习惯了,每个语句后面我都忍不住加上了分号,在Lua中 分号可要可不要,如果你不打分号,它以换行符做为语句的结束,大家可以根据自己的习惯选择加不加分号):
> greeting=function(name)
>> if(type(name)=="string") then
>>     print("您好,我亲爱的" .. name);
>> elseif (type(name)=="nil") then
>>     print("您好,我亲爱的朋友");
>> else
>>     error("你在说什么?我不知道");
>> end
>> end
第一个条件测试name参数是否为 string,如果是就生成一句对客户的问候语并显示。如果name的参数是nil,则意味着没有参数传递给函数,这时它会输出字符串“您好,我亲爱的朋 友”。最后,如果前面两种情况都不匹配,则进入else部分,触发一个用error()函数定义的客户错误信息。测试结果如下:
> greeting(10)
stdin:7: 你在说什么?我不知道
stack traceback:
        [C]: in function 'error'
        stdin:7: in function 'greeting'
        stdin:1: in main chunk
        [C]: ?
> greeting("默然")
您好,我亲爱的默然
> greeting()
您好,我亲爱的朋友
3.3        用while语句来重复动作
while 语法如下:
while <布尔表达式> do
       --循环体
end
这里的布尔表达式会在每一次循环中求 值,当它不再为true时循环就会退出。
3.3.1计 算阶乘
计算一个数字的阶乘就是一个循环的好 例子。阶乘就是将从1到给定的数之间的所有整数乘在一起得到的乘积。所以3的阶乘就是1*2*3,以此类推。如果我们定义了一个计算阶乘的函数 factorial(),那么我们就可以简单地输入
>print(factorial(9))
而不是输入
>print(9*8*7*6*5*4*3*2*1)
现在通过下面的定义在解释器中定义这 个函数:
> factorial=function(num)
>> local total=1
>> while(num>1)do
>>     print("total:" .. total .. "num:" .. num)
>>     total=total*num
>>     num=num-1
>> end
>> return total
>> end
这个函数包括了一个print()函 数,它主要起到帮助我们查看循环在做些什么,没有实际的用处,在正式代码中应该删除。现在,在Lua解释器中测试:
> print(factorial(5))
total:1num:5
total:5num:4
total:20num:3
total:60num:2
120
这样你就可以看到循环中的每一步和这 些数是怎么计算的。这样的调试语句在实际测试代码时是很有用的,但要记得在最后发布代码时删除它们。
默然说话:关于循环语句,我 想多罗嗦几句。由于这不是一本编程语言的入门书,所以如果你以前没有编程语言的基础,我还是建议你先学习一下编程语言的一般规律,而无论你学习哪一种语 言,它们的基础其实都是一样的,所以如果你的确想好好学习插件的编写,并且想写出自己完全原创的插件,那还是先多花点时间学习一下编程语言吧,无论是 Java,C,C++,还是C#都有非常好的入门教程,这些入门教程里有非常详细的关于条件判断和循环语句的应用例子。而掌握了编程语言的一般规 律,Lua对你来说根本算不得什么,很轻松就能掌握。
3.3.2while 和repeat之间的差异
repeat的语法如下:
repeat
       ----循环体
until <布尔表达式>
repeat和while的区别就在 于while是先判断后执行,所以如果一来布尔表达式就是false那么循环体会一次都运行不到,而repeat/until循环却是先运行一次循环体然 后才开始进行循环条件的判断,所以它的循环最少都会被执行一次。下面是一个新定义的阶乘函数:
> factorial2=function(num)
>> local total=1
>> repeat
>>     total=total*num
>>     num=num-1
>> until (num<1)
>> return total
>> end
下面测试一下:
> print(factorial2(5))
120
> print(factorial2(3))
6
看上去蛮不错,我们再做一个测试,这 次输入一点意外的值(阶乘计算只考虑正数):
> print(factorial2(-3))
-3
> print(factorial(-3))
1
我们看到了,老版本(while循 环)的阶乘计算是正确的,而新版本(repeat/until循环)却得到了-3,这就是由于while在一开始的时候条件就为false,所以 total的值就保留了1的初始值,但repeat/until循环却先做了循环体,所以total被改为了-3。
3.4        用数值执行for循环
for循环语法如下:
for 变量名=初始值,结束值,步长 do
       ----循环体
end
下面是简单的for循环的例子:
> for i=1,3,1 do
>> print(i)
>> end
1
2
3
> for i=3,1,-1 do
>> print(i)
>> end
3
2
1
这两个循环如果用while就是这样 写的:
> do
>>   local i=1
>>   while(i<=3) do
>>     print(i)
>>     i=i+1
>>   end
>> end
1
2
3
> do
>>    local i=3
>>    while(i>=1) do
>>        print(i)
>>        i=i-1
>>    end
>> end
3
2
1
当没有提供for循环的步长的时 候,Lua假设循环的步长就是1。所以,前面第一个从1循环到3的例子就可以如下所示:
> for i=1,3 do
>> print(i)
>> end
3.4.1计 算阶乘
for循环可以写出很简单的阶乘函 数:
> factorial3=function(num)
>>   local total=1
>>   for i=1,num do
>>     total=total*i
>>   end
>>   return total
>> end
与手动编写while和repeat 循环的终止条件相比,for循环语句只需您提供一个初始值和一个终止值。这里我们使用了变量num,它在函数运行的时候会有正确的值。
3.4.2循 环条件的求值
在for循环中,初始值,终止条件, 还有步长三个部分都只会被执行一次,所以,即使你使用变量或表达式代替终止值和步长,在循环体中又不小心改变了终止值和步长,循环也能正常结束,看下面的 例子:
> upper=3
> step=1
> for i=1,upper,step do
>> upper=upper+1
>>step=step+1
>> print(i)
>> end
1
2
3
上面的例子在终止值和步长都使用了变 量,并且在循环体中进行了修改,但是我们发现循环仍然只循环了三次,每次i的增加值都是1,完全是按照upper和step两个变量的初始化值来工作的(默然说话:这里只是一个教学例子,仅仅为了说明一个道理,如果在实际代码中,你应该尽力做到不在循环体内去修改终止变 量和步长变量的值,这是一个老程序员的直觉,直觉告诉我——这样做不好)。
3.4.3循 环中的变量作用域
当编写for循环的时候,要记住计数变量名的作用域会被自动地设置为局部,也就是说它们在for循环外是不能被访问的。
> i=15
> for i=1,3 do
>>   print(i)
>> end
1
2
3
> print(i)
15
从上面的例子可以看出来,在for循 环内的变量并非是声明在循环外部的那个变量i,它是独立存在于for循环体内部的(因为它的输出是1,2,3而与15没有关系)。而我们在循环结束之后又 一次输出i的值,我们发现,外部的变量i,它的值并没有发生变化,由此证明:此i非彼i。
3.5        小结
本章介绍了函数的基本语法格式,此 外,还介绍了条件和循环结构,这使得您可以轻松地控制程序的流程以及进行重复计算。第4章将探讨使用表的相关技术。
原创粉丝点击