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

本文介绍了Lua语言中的函数定义与调用、条件语句、循环结构等内容,帮助初学者掌握控制流程和重复计算的基本方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第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语句进行判断

ifLua中进行判断的基础,它支持简单的条件,也支持复杂的语句。最基本的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关键字,其他的都可以认为是真)。下面都可以作为有效的条件使用:

l  name

l  type(name)==”string”

l  (not anonymous_flag) and type(name)==”string”

第一个例子简单地测试变量是不是除了falsenil以外的其他东西,第二个例子测试变量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

这样你就可以看到循环中的每一步和这些数是怎么计算的。这样的调试语句在实际测试代码时是很有用的,但要记得在最后发布代码时删除它们。

默然说话:关于循环语句,我想多罗嗦几句。由于这不是一本编程语言的入门书,所以如果你以前没有编程语言的基础,我还是建议你先学习一下编程语言的一般规律,而无论你学习哪一种语言,它们的基础其实都是一样的,所以如果你的确想好好学习插件的编写,并且想写出自己完全原创的插件,那还是先多花点时间学习一下编程语言吧,无论是JavaCC++,还是C#都有非常好的入门教程,这些入门教程里有非常详细的关于条件判断和循环语句的应用例子。而掌握了编程语言的一般规律,Lua对你来说根本算不得什么,很轻松就能掌握。

3.3.2 whilerepeat之间的差异

repeat的语法如下:

repeat

       ----循环体

until <布尔表达式>

repeatwhile的区别就在于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

与手动编写whilerepeat循环的终止条件相比,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,完全是按照upperstep两个变量的初始化值来工作的(默然说话:这里只是一个教学例子,仅仅为了说明一个道理,如果在实际代码中,你应该尽力做到不在循环体内去修改终止变量和步长变量的值,这是一个老程序员的直觉,直觉告诉我——这样做不好)。

3.4.3 循环中的变量作用域

当编写for循环的时候,要记住计数变量名的作用域会被自动地设置为局部,也就是说它们在for循环外是不能被访问的。

> i=15

> for i=1,3 do

>>   print(i)

>> end

1

2

3

> print(i)

15

从上面的例子可以看出来,在for循环内的变量并非是声明在循环外部的那个变量i,它是独立存在于for循环体内部的(因为它的输出是123而与15没有关系)。而我们在循环结束之后又一次输出i的值,我们发现,外部的变量i,它的值并没有发生变化,由此证明:此i非彼i

3.5        小结

本章介绍了函数的基本语法格式,此外,还介绍了条件和循环结构,这使得您可以轻松地控制程序的流程以及进行重复计算。第4章将探讨使用表的相关技术。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默然说话

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值