Lua: if语句
============

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

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

.. code:: lua

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

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

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

条件测试
---------

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

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

检查是否相等
~~~~~~~~~~~~

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

.. code:: lua

   local hp = 4
   print(hp == 1)

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

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

.. code:: lua

   local hp = 1
   print(hp == 1)

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

检查是否不相等
~~~~~~~~~~~~~~

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

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

.. code:: lua

   local general = "徐盛"

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

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

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

和数字有关的比较
~~~~~~~~~~~~~~~~

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

.. code:: lua

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

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

检查多个条件
~~~~~~~~~~~~

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

使用and检查多个条件
+++++++++++++++++++

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

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

.. code:: lua

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

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

使用or检查多个条件
++++++++++++++++++

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

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

.. code:: lua

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

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

布尔表达式
~~~~~~~~~~

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

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

.. code:: lua

   local player_alive = true
   local caochong_alive = false

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

if语句
-------

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

简单if语句
~~~~~~~~~~

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

::

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

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

.. hint::

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

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

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

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

.. code:: lua

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

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

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

.. code:: lua

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

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

if-else语句
~~~~~~~~~~~~

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

::

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

.. code:: lua

   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结构中，总是会执行两个操作中的一个。

if-elseif-else结构
~~~~~~~~~~~~~~~~~~

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

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

- 若花色是红桃，回复1点体力。
- 若花色是方块，摸两张牌。
- 若花色是黑桃，伤害来源翻面。
- 若花色是梅花，伤害来源弃置两张牌。
- 其他花色，那暂且认定出错了吧。

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

.. code:: lua

   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结构改写就能改善很多：

.. code:: lua

   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语法允许你这样做。

关于if语句的格式
-----------------

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

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

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