Lua：循环
==========

本节中我们来了解另一个重要概念——循环。循环可以让计算机自动完成许多重复的工作，
同时也可以让我们的所有操作更为简化。

遍历一个列表
-------------

上文提到了列表，并且解说了对列表的修改和增删操作。然而在实际操练中，我们经常需要\
**遍历**\ 列表，对每个元素执行相同的操作。例如技能“神愤”，要对所有人造成1点伤害，
这就需要我们对保存着“所有角色”的列表中的每个元素都执行相同的操作了。举个例子：

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   print("对" .. generals[1] .. "造成了1点伤害")
   print("对" .. generals[2] .. "造成了1点伤害")
   print("对" .. generals[3] .. "造成了1点伤害")
   print("对" .. generals[4] .. "造成了1点伤害")
   print("对" .. generals[5] .. "造成了1点伤害")

上面的代码中我们徒手操作列表，对其中的每个人造成了一点伤害；我们先用索引取得对应的字符串，
然后再用拼接运算符拼出一条完整的信息，打印到屏幕上。但这种做法显得很笨，
我们接下来看看如何使用Lua的 ``for`` 循环完成一样的操作：

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   for _, general in ipairs(generals) do  -- [1]
     local message = "对" .. general .. "造成了1点伤害"
     print(message)
   end

运行一下，发现输出的是一样的结果。你可以试试修改generals列表中的内容，程序会毫不保留的\
将所有内容都打印一行出来。

仔细观察[1]处的代码：我们定义了一个for循环，这行代码能让Lua从列表generals中\
先取出一个元素，并把他储存在临时变量general中。这之后，Lua就会执行for语句\
内层的那个语句块了，这里就是拼出一条消息然后打印出来。这样一来，对于generals\
中的每个元素，Lua都会重复执行一次for内层的代码，直到所有元素都处理完毕了。

在遍历列表中执行一些特殊的操作
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

因为for语句内部是一个语句块，所以我们可以在那里编写任意多的代码。何不趁现在\
操练一下之前学习的if语句？我们修改一下代码吧，如果是对徐盛造成了伤害，则多\
输出一条消息，毕竟这有着某种特殊的含义。

做法很简单；我们只要在内层进行一次if判断就行了，如果当前遍历到的是“徐盛”，\
那么就多输出一条消息：

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   for _, general in ipairs(generals) do  -- [1]
     local message = "对" .. general .. "造成了1点伤害"
     print(message)
     if general == "徐盛" then
       print("你犯大吴疆土了！")
     end
   end

回忆一下for语句执行的步骤：首先，他从列表generals中拿出一个元素，然后将其\
保存到临时变量general中。有了这个临时变量之后，Lua就会继续执行for语句的\
内层代码块。如此循环往复，直到列表中的元素被全部拿完了。因为拿出来的元素\
是保存在临时变量里面的，所以当我们遍历到“徐盛”的时候，变量general的值就是\
“徐盛”，于是就通过了if语句的条件检测，执行了if语句那里的print。

使用break中途退出循环
~~~~~~~~~~~~~~~~~~~~~~

要立即退出for循环，不再运行循环中余下的代码，可使用break语句。
break语句用于控制程序流程，可使用它来控制哪些代码行将执行，
哪些代码行不执行，从而让程序按你的要求执行你要执行的代码。

例如，当对徐盛造成伤害后，我们想让徐盛保卫吴国疆土，以保证孙权不受伤，
这里就可以加一个break语句强行退出循环：

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   for _, general in ipairs(generals) do  -- [1]
     local message = "对" .. general .. "造成了1点伤害"
     print(message)
     if general == "徐盛" then
       print("徐盛保卫了大吴疆土，神愤被中途结束了。")
       break
     end
   end

运行一下，可以看到对孙权造成伤害的那行不再显示了。这是因为这里使用了\
break，把整个循环中途掐断了。

while循环简介
--------------

前面我们提到了for循环，他可以通过遍历一个列表来不断的执行循环；而while\
循环则更加通用，他像if一样进行着一个条件检测，如果通过的话就执行代码，
如此循环往复，直到那个条件检测不通过为止。

下面的代码使用了while循环，让程序从1数到5：

.. code:: lua

   local num = 1
   while num <= 5 do
     print(num)
     num = num + 1
   end

在第1行，我们将 num 设置为1，从而指定从1开始数。接下来的while循环被设置\
成这样: 只要 num 小于或等于5，就接着运行这个循环。循环中的代码打印num的值，
再使用代码num = num + 1将其值加1。

只要满足条件 num <= 5 ，Lua就接着运行这个循环。由于1小于5,因此Lua\
打印1，并将num加1，使其为2；由于2小于5，因此Lua打印2，并将num加1，使其为3，
以此类推。一旦num大于5，循环将停止，整个程序也将到此结束。

while循环非常有用。例如，出牌阶段就是一个while循环：只要角色没有阵亡，
他就一直循环往复的让玩家选择出一张牌或者发动一个技能，直到玩家想要结束的\
时候才退出循环。

和for循环一样，使用break语句亦可让while循环立即结束。这个就请读者们自行操练吧。

操练：使用while来遍历列表
-------------------------

回到前面关于for循环遍历列表的话题吧。既然while这么有用，想必也可以用来遍历列表。

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   for _, general in ipairs(generals) do  -- [1]
     local message = "对" .. general .. "造成了1点伤害"
     print(message)
   end

像这样的for循环可以直接遍历整个列表；而while循环需要我们手动编写条件检测。
回想一下for循环的运行流程，首先拿出一个元素，然后执行代码，下一轮执行又拿出\
下一个元素，直到没有可以拿的为止。为此，我们可以设置一个变量表示当前元素的\
索引，每执行一次while循环，就根据这个索引取出值，然后把索引加一；如此往复，
直到取不出值为止（取了个nil出来），此时我们就用break手动结束掉循环。

代码如下：

.. code:: lua

   local generals = { "张飞", "刘备", "曹操", "徐盛", "孙权" }
   print("发动技能神愤！")
   local index = 1
   while true do    -- 条件判断填了个true，这将导致无限的循环下去
     local general = generals[index]   -- 首先取出值，保存在变量general
     index = index + 1   -- 然后将索引加一
     if general == nil then   -- 如果我已经没有值可以取了怎么办？
       break             -- 用break直接中断循环就行了
     end

     local message = "对" .. general .. "造成了1点伤害"
     print(message)
   end
