3. Lua: if语句#

本节中将介绍if语句。在我们编写拓展的时候,最常遇到的就是关于某些条件的检查, 比如“若你的体力值为1,你获得技能英姿英魂”,就要判断体力值是否就是1。 我们完成这种检查的手段就是if语句。

拿刚刚说的话先举个简单的例子:

-- 假设我们定义一个名为hp的变量,用来表示这是体力值
local hp = 3

if hp == 1 then
  print("获得英姿英魂!")
end

这种就是if语句,他检测hp是否是1,如果是的话,就打印出文字表示获得了技能。 这个示例涵盖了本章将介绍的很多概念。下面先来介绍可用来在程序中检查条件的测试。

3.1. 条件测试#

每条if语句的核心都是一个值为 truefalse 的表达式,这种表达式被称为条件测试。 Lua根据条件测试的值为 true 还是 false 来决定是否执行if语句中的代码。 如果条件测试的值为 true ,Lua就执行紧跟在if语句后面的代码,否则就跳过。

我们现在要开始新的操练了!新建一个名为if.lua的文件,然后编写代码并尝试执行吧。 事实上在文中举出例子的时候你也应该多去试着运行一下才好。

3.1.1. 检查是否相等#

大多数条件测试都将一个变量的当前值同特定值进行比较。 最简单的条件测试检查变量的值是否与特定值相等:

local hp = 4
print(hp == 1)

首先我们定义一个hp变量,用一个等号将他的值设置为4,这种在之前已经出现过很多次了。 接下来,我们使用两个等号( == )检查hp的值是否为3。这个双等号就被称为相等运算符, 当两边都相等时,运算结果为true,否则为false。这里hp的值和1不相等,所以打印出来的是false。

以此类推,如果hp的值是1,那么式子的值应该就会是true了:

local hp = 1
print(hp == 1)

关于一个等号还是两个等号的问题经常容易令人混淆。我们可以这么理解: 一个等号是陈述,对于 local hp = 4 ,可以理解为“我要把变量hp的值设置为4”; 两个等号是发问,对于 hp == 1 ,可以理解为“变量hp的值是1吗?”。 绝大多数编程语言使用等号的方式都和这里相同。

3.1.2. 检查是否不相等#

要判断两个值是否不相等,可以使用一个波浪线和一个等号( ~= ), 其中波浪线带有“不”、否定的意思。后面做武将的时候波浪线还用来表示阵亡台词呢。

这个符号被称为不等运算符,我们接着往if.lua加入下面的代码,看看结果:

local general = "徐盛"

if general ~= "孙策" then  -- [1]
  print("还不能获得英姿英魂!")
end

[1]处的代码将 general 的值和“孙策”比较,如果它们不相等,那么式子的值就是true, Lua就会执行紧跟在if语句之后的代码,也就是输出这行信息;但是如果相等, 那么式子的值就是false,于是就跳过接下来的代码。

注意到了不?这个运算符的判定方法刚好和 == 是反过来的。

3.1.3. 和数字有关的比较#

数字之间的话自然可以用 ==~= 比较相等和不相等了,但我们还可以进行许多数学比较, 比如大于、小于、不大于、不小于:

-- 接着if.lua的最后继续写,变量maxHp意为体力上限
local maxHp = 3
print(maxHp > 3) -- false
print(maxHp < 5) -- true
print(maxHp >= 3) -- true
print(maxHp <= 1) -- false

在if语句中可使用各种数学比较,这让你能够直接检查关心的条件。

3.1.4. 检查多个条件#

你可能想同时检查多个条件,例如,有时候你需要在两个条件都为 true 时才执行相应的操作, 而有时候你只要求一个条件为 true 时就执行相应的操作。在这些情况下, 关键字 andor 可助你一臂之力。

3.1.4.1. 使用and检查多个条件#

要检查是否两个条件都为true,可使用关键字 and 将两个条件测试合而为一;如果两个测试都通过了, 整个表达式就是true;只要有一个没通过,整个表达式就是false。

例如我要检测,必须武将是孙策而且体力值为1:

-- 一样的,接着if.lua的最后继续写
general = "孙策"
hp = 2
print(general == "孙策" and hp == 1)

我们之前已经定义过变量 generalhp 了;这里前两行先将它们的值分别设为“孙策”和1, 然后再检测general是否是孙策、hp是否是1;可以知道,and左边的测试是通过的,但是右边没有通过, 因此整个条件表达式的结果为false。

3.1.4.2. 使用or检查多个条件#

关键字or也能够让你检查多个条件,但只要至少有一个条件满足,就能通过整个测试。 仅当两个测试都没有通过时,使用or的表达式才为false。

现在我放宽条件,只要武将是孙策或者体力值是1就可以:

print(general == "孙策" or hp == 1)

在这里,由于general == "孙策" 已经通过了,所以整个or表达式的结果就是true。 但假如把general赋值为“徐盛”,那么两个测试就都无法通过了,因此表达式结果为false。

3.1.5. 布尔表达式#

随着你对编程的了解越来越深入,将遇到术语布尔表达式,它不过是条件测试的别名。 与条件测试表达式一样,布尔表达式的结果要么为true,要么为false。

布尔表达式通常记录条件,比如角色是否存活之类的:

local player_alive = true
local caochong_alive = false

在跟踪程序状态或程序中重要的条件方面,布尔值提供了一种高效的方式。

3.2. if语句#

理解条件测试后,就可以开始编写if语句了。if语句有很多种, 选择使用哪种取决于要测试的条件数。前面讨论条件测试时, 列举了多个if语句示例,下面更深入地讨论这个主题。

3.2.1. 简单if语句#

最简单的if语句只有一个操作:

if <条件测试> then
  <一些代码>
end

在第1行中,可包含任何条件测试,而在紧跟在测试后面的代码块中,可执行任何操作。 如果条件测试的结果为true,Lua就会执行紧跟在if语句后面的代码;否则Lua将忽略这些代码。

提示

所谓的代码块,其实就是一段范围内的代码总和,如果你想的话,一行代码也是代码块

代码块之间有嵌套/层级关系,如if语句内的代码块定义的变量和if本身处于的代码块的同名变量是不一样的。

在Python中,缩进是表明这种层级关系的唯一方法,Lua虽然没有这种限制,但我们仍然习惯使用缩进来明确代码块的层级。

假如定义一个表示某人年龄的变量,你想知道他是否成年了:

local age = 19
if age >= 18 then            -- [1]
  print("你已经成年了!")    -- [2]
end

在[1]处,Lua检查变量 age 的值是否大于等于18;答案是肯定的, 因此Lua执行[2]处代码打印出答复。

在if语句中,then和end之间包含的是一个代码块,意思就是你可以在then和end之间写很多行代码:

local age = 19
if age >= 18 then
  print("你已经成年了!")
  print("不知道你有没有了解一些成年的内容呢?")
end

上面的例子只是加了一行而已,当条件通过后,if语句会把这行也执行。如果不通过的话, 两行就直接全都跳过了。

3.2.2. if-else语句#

经常需要在条件测试通过了时执行一个操作,并在没有通过时执行另一个操作; 在这种情况下,可使用Lua提供的 if-else 语句。if-else 语句块类似于简单的 if 语句, 但其中的else语句让你能够指定条件测试未通过时要执行的操作。

if <条件测试> then
  <一些代码>
else
  <另一些代码>
end
local age = 9
if age >= 18 then
  print("你已经成年了!")  -- [1]
else
  print("你还是未成年!")  -- [2]
end

如果[1]处的条件测试通过了,就执行第一个print语句块;如果测试结果为false, 就执行[2]处的else代码块。这次 age 小于18,条件测试未通过,因此执行else代码块中的代码。

上述代码之所以可行,是因为只存在两种情形:要么成年,要么未成年。 if-else结构非常适合用于要让Lua执行两种操作之一的情形。 在这种简单的if-else结构中,总是会执行两个操作中的一个。

3.2.3. if-elseif-else结构#

经常需要检查超过两个的情形,为此可使用Lua提供的if-elif-else结构。 Lua只执行if-elif-else结构中的一个代码块,它依次检查每个条件测试,直到遇到通过了的条件测试。 测试通过后,Lua将执行紧跟在它后面的代码,并跳过余下的测试。

在游戏中,很多情况下需要考虑的情形都超过两个。例如,来看悲歌的一部分效果:

  • 若花色是红桃,回复1点体力。

  • 若花色是方块,摸两张牌。

  • 若花色是黑桃,伤害来源翻面。

  • 若花色是梅花,伤害来源弃置两张牌。

  • 其他花色,那暂且认定出错了吧。

我们可以套娃,在if-else结构的else分支里面套娃新的if语句,但这样就会导致if层数太深:

local suit = "spade"

if suit == "heart" then
  print("回复")
else
  if suit == "diamond" then
    print("摸2")
  else
    if suit == "spade" then
      print("翻面")
    else
      if suit == "club" then
        print("弃2")
      else
        print("出错了!")
      end
    end
  end
end

这里首先判断花色变量 suit 是否是红桃(heart);判断没有通过,于是进入第一个else分支, 在这里又判断是否是方块;又不通过,继续执行方块那个if语句的else分支;在这里又判断是否是黑桃, 这下判断通过了,于是输出“翻面”,不再执行后面那个else分支了。

上述代码虽然可以运行,但是由于嵌套的太深,会导致阅读起来很费力,代码整体也不美观。 这种情况下用if-elseif-else结构改写就能改善很多:

local suit = "spade"

if suit == "heart" then
  print("回复")
elseif suit == "diamond" then
  print("摸2")
elseif suit == "spade" then
  print("翻面")
elseif suit == "club" then
  print("弃2")
else
  print("出错了!")
end

这个代码其实他的判断流程和前者一样,但是因为用了elseif,代码明显清晰了不少。

像这样的结构就是if-elseif-else结构,中间可以穿插多个elseif语句块; 当然了,你也可以省略掉最后的那一个else语句块,Lua语法允许你这样做。

3.3. 关于if语句的格式#

本文的每个示例都展示了良好的格式设置习惯。在条件测试的格式设置方面, 建议就是在诸如== 、 >= 和 <= 等运算符两边各添加一个空格,例如, if age >= 18 then 要比 if age>=18 then 好。

这样的空格不会影响Lua对代码的解读,而只是让代码阅读起来更容易。

此外,if语句的代码块应当遵循良好的缩进。得益于插件的作用, 你可以在vscode中一键让代码缩进井然有序,先在编辑器右键,再点击“格式化文档”即可。