第3 章 条件和循环 经过前两章的学习后 应用程序。这些应用程序的工作将由你的响应决定 ,我相信你会很愿意用Julia创建一些真实的 ,有时还决定 于你希望它们如何工作。然而,在你可以做到这一点之前,你首先必须 学习一些关于编程的基本知识:如何让计算机做出决定以及如何遍历 数据。 在本章中,你将学习 : . 什么是条件(condition) ; . 条件操作符是什么 ; . 计算机如何使用if/else-if/else语句检查和断言条件进行决策 ; iei . 什么是迭代(traton) ; . 如何使用for循环迭代 ; . 如何使用while循环迭代。 34 极简Julia语言———机器学习跃迁之路 3.什么是条件 1 在我们关心的上下文中,条件是“一种情况或一种结果”。当与if一 起使用时,条件将导致真或假(是或否), 程序控制可以转向一条(第一 条)或另一条(第二条)路径。 这是使计算机能够运行的最基本和最关键的操作,你使用计算机所 做的几乎所有操作都有与之相关的某种条件。例如: ①当你在Chrome 浏览器中打开新选项卡时,浏览器必须检查是否 已打开过多的选项卡。 ②当你注册在线服务时,表单需要根据你的出生日期确保你的年 龄足够大。 ③当你使用键盘在文本编辑器上输入时,编辑器需要检查Caps Lock键是否打开或是否按下Shift键。 ④当你问Siri“我今天需要穿雨衣吗?”,手机需要查询天气是否会 下雨。 以上只是几个简单的例子,在日常生活中,几乎所有不同场景中使 用的应用程序都会使用条件。 下面你将学习如何在Julia语言中实现这些条件。 3.条件操作符是什么 2 假设我们要构建一个应用程序,用来检查某个用户是否超过某一年 龄阈值,以便让该用户享受相关服务。继续操作并打开名为old_ enough.l的新文件,输入以下代码: j 第3章 条件和循环3 5 1 minimum_age = 18 2 print("Enter your age: ") 3 user_age = parse(Int64, readline()) 我相信你还记得第2章中的这段代码做了以下三件事: ① 定义一个minimum_age变量,赋值为18; ② 打印提示信息,以便用户提供其年龄信息; ③ 从用户处读取输入,将其从字符串转换为整数形式,并存储在 user_age中。 一旦获取了user_age变量,就需要确定该user_age变量的值是否 足够大,使用户能够进入服务行列。我们可以使用一个条件运算符实现 这一点。 在继续讨论条件之前,我们需要先了解一些将要使用的简单术语。 什么是操作符呢? 让我们举个例子: user_age > minimum_age 这行代码读作user_age大于minimum_age,这里的大于(>)符号 是一个操作符,因为它分别对写在其左右两侧的user_age和minimum_ age进行操作。此外,user_age和minimum_age也被称为操作数。这样 写,表示这是一个评估两种可能结果之一(true或false)的条件,取决于 存储在左右变量中的值。我们将在创建的应用程序中学习并使用许多 这样的操作符。 正如我们刚刚学习过的,加法(+)符号需要读取两个数并返回它 们的和,星号(*)符号需要读取两个数并返回它们的乘积。相比之 下,条件操作符需要接收一个或两个值,并返回一个布尔(true或 false)值。 Julia中有很多不同的条件操作符,以下是其中的一些。 3 6 极简Julia语言———机器学习跃迁之路 操作符叫法描 述 > 大于需要两个值,并检查左边的值是否大于右边的值 < 小于需要两个值,并检查左边的值是否小于右边的值 == 等于需要两个相同类型的值,并检查它们是否都表示相同的值 != 不等于需要两个值,并检查它们是否都表示不同的值 <= 小于或等于需要两个值,并检查左边的值是否小于或等于右边的值 >= 大于或等于需要两个值,并检查左边的值是否大于或等于右边的值 ! 布尔逆 这个运算符有点不同,它只需要右边的一个值,将返回它 所传递的值的逆。例如,如果传递false,则将返回true; 如果传递true,则返回false 备注:==操作符比较特殊,并且不同于=操作符。=操作符用来 为一个变量赋值,==操作符用来比较两个变量,结果返回true或 false。 在这个例子中,我们希望确保用户的年龄大于或等于我们定义的最 小年龄。要想执行此操作并打印出结果,需要添加以下代码: 4 println(user_age >= minimum_age) 好了! 如果只使用4行代码运行应用程序,那么你会得到一个提 示,要求输入你的年龄。如果输入的值小于18,则会显示false;否则会 显示true。 但这并不是很有趣,也没有做太多的事情,所以让我们带着它进入 下一级。如果条件操作符返回true,则要执行一些代码;否则要执行其 他代码,这就是if语句出现的地方! 第3章 条件和循环3 7 3.3 计算机如何使用if/else-if/else语句进行决策 使用if语句可以根据条件为true还是false控制代码流。让我们先 来看一个例子。 首先,继续从old_enough.jl文件中删除最后一行代码,然后输入 代码 4 if user_age >= minimum_age 5 println("You..re old enough. Welcome!") 6 end 让我们来解析一下这三行代码。 ① 第4行告诉Julia我们正在开始一个if语句,并提供了一个需要 检查的条件。 ② 如果第4行中的条件为true,则执行第5行。 ③ 第6行告诉Julia,if语句的代码块已经结束。如果第1行(行4) 中的条件为true,那么在这一行之前和第一行(行4)之后的所有内容都 将被执行,它可以是这两行之间任意数量的代码行。这一部分代码被称 为一个代码块。 现在,如果要运行应用程序,你会看到提示,但还有一些问题。如果 你提供的年龄足够大,即18岁或以上,则它确实会通过println函数在 下一行打印消息。然而,如果不是,那么什么 也不会发生。程序并没有说你的年龄还不够 大,只是什么也没做。右面这张流程图将会让 你看得更清楚。 但是,你想要的流程应该是这样的: 3 8 极简Julia语言———机器学习跃迁之路 这种情况不会发生,因为我们还没有告诉if语句当判断结果为 false时要执行什么代码。 if user_age < minimum_age println("You're too young — sorry!") end 这样就可以正常工作了,但这还不是最理想的,你明明已经检查过 一次条件了,为什么还要再检查一次呢? 相反,你可以使用else子句,它 是一个在条件非true时提供代码的子句。 替换已有的if语句: .4 if (user_age >= minimum_age) 5 println("You're old enough. Welcome!") 6 else 7 println("You're too young — sorry!") 8 end 这样就足够了! 如果运行应用程序,那么现在应该可以看到输入年 龄大于和小于阈值时的输出,并告诉它们是否符合条件。 但是你还可以做更多的事情。例如,如果用户的年龄超过了阈值, 你想欢迎他们,让他们进入;如果他们的年龄正好是阈值年龄,则要发出 警告;如果他们的年龄小于阈值年龄,就不允许他们进入,等等。当然, 你可以编写以下这样的代码: 第3章 条件和循环3 9 .4 if user_age > minimum_age 5 println("You're old enough. Welcome!") 6 else 7 if user_age == minimum_age 8 println("You're old enough, but be careful.") 9 else 10 println("You're too young — sorry!") 11 end 12 end 事实上,我建议你尝试一下,以便通过输入不同的年龄了解程序是 如何工作的。但是,还有一种更有效的方法———if/else-if/else语句。 让我们来看一个例子,然后我会解释一下它是如何工作的。 .4 if user_age > minimum_age 5 println("You're old enough. Welcome!") 6 elseif user_age == minimum_age 7 println("You're old enough, but be careful.") 8 else 9 println("You're too young — sorry!") 10 end 让我们逐行来看一下,从第4行开始。 ① 第4行将检查用户的年龄是否大于最小年龄。 ② 如果第4行的条件为true,则运行第5行并打印消息“You..reold enough.Welcome!”。 ③ 如果第4行的条件为false,则第6行将检查用户的年龄是否等 于最小年龄。 ④ 如果第6行的条件为true,而第4行的条件为false,则运行第7 行并打印消息“You..reoldenough,butbecareful!”。 ⑤ 第8行准备让Julia看看即将到来的代码。 4 0 极简Julia语言———机器学习跃迁之路 ⑥ 如果第4行和第6行中的条件都为false,则运行第9行。 ⑦ 第9行打印消息“You..retooyoung—sorry!”。 ⑧ 第10行告诉Julia,你在第4 行开始的if语句的代码块已经 结束。⑨ 事实上,你也可以编写多个else-if语句,例如: .4 if (user_age > minimum_age + 1) 5 println("You're old enough. Welcome!") 6 elseif (user_age == minimum_age + 1) 7 println("You're old enough, and I'm sure you'll . be fine.") 8 elseif (user_age == minimum_age) 9 println("You're old enough, but be careful!") 10 else 11 println("You're too young — sorry!") 12 end 将对每个条件进行判断,直到其中一个条件为true,然后运行其相 应的代码块。如果没有判断为true的条件,则运行与else语句对应的 代码块。 现在,你已经了解了条件以及if语句是如何工作的了。 练习1 现在是时候练习一下如何创建以下应用程序了。 1.提示用户输入一个整数,并通知用户输入的数字为偶数或奇数。 2.提示用户输入一个整数,并通知用户输入的数字是否可以被7 整除。 3.提示用户输入两个整数作为平行四边形的底和高,并计算出 面积。 第3章 条件和循环4 1 3.4 什么是迭代 正如前面提到的,条件是编程的重要组成部分,但仅仅如此还有点 不完整。为了让计算机真正发挥作用,它们还需要支持迭代,即多次执 行任务。 事实上,如果你仔细想想就会明白,这才是我们使用计算机的初衷, 因为它们可以数百万次地做同一件事情,不会觉得“无聊”或需要“休 息”,这可是我们人类做不到的! Julia中有两种迭代的方法———for循环和while循环。前者使用得 更广泛,而后者则更灵活一些。 3.5 如何使用for循环迭代 实际上,for循环非常简单,请打开一个名为countUpTo.jl的新文 件,然后输入代码 1 for iteration_number in 1:5 2 println(iteration_number) 3 end 我将会解释这段代码的作用,但请先运行一下代码,你应该会看到 以下输出: 12 3 4 5 4 2 极简Julia语言———机器学习跃迁之路 这段代码的本意是告诉Julia“执行以下代码,并在第1行创建一个 名为iteration_number的变量。此变量的初始值为1,并且每次执行循 环代码时此变量的值都会加1。最终,当你得到5并执行代码时,停止 循环”。 for循环的基本格式为 for <iteration_number> in <start_at>:<end_at> [your_code] end 术 语解 释 iteration_number 任何有效的变量名,这个变量被称为循环控制变量 start_at 任何数值———值、变量或表达式。这是循环控制变量的初 始值 end_at 任何数值———值、变量或表达式。当循环控制变量的值超过 此值时,将停止循环。循环控制变量运行for和end之间的 代码,每次到达end时,iteration_number变量的值都会增 加,并检查其值是否小于或等于end_at中存储的值。如果 是,则进行下一次迭代;否则退出循环,并执行end后面的 代码 为了更好地理解程序是如何工作的,请在for循环语句之前添加以 下两行代码: 1 print("Enter a number to count up to: ") 2 max_number = parse(Int64, readline()) 然后将for循环改为 3 for iteration_number in 1:max_number 4 println(iteration_number) 5 end 现在,运行代码,你可以输入一个数字,并看到程序计数到这个数。 第3章 条件和循环4 3 例如,如果输入8,则会看到这样的输出: Enter a number to count up to: 8 12345678 就这样,你已经学会了for循环的基本知识! 但是你还可以做得更 多。例如,如果你希望每次都将iteration_number增加2而不是增加1, 需要做的改变就是 for iteration_number in 1:2:max_number 现在,如果你在提示符处输入12,则会看到这样的输出: Enter a number to count up to: 12 13579 11 如果你要打印偶数,则必须从2开始。因此,请更改第1行为 for iteration_number in 2:2:max_number 在这里,如果你在提示时输入12,则会看到这样的输出: Enter a number to count up to: 12 246 4 4 极简Julia语言———机器学习跃迁之路 8 10 12 现在,你已经了解了for循环的基本知识,让我们开始构建一些更 复杂的程序,并将条件与它结合起来。 创建一个名为stop_at_divisible.jl的新文件。在此文件中,我们将 编写一些能够接收以下输入的代码: ① 起始数; ② 结束数; ③ 整除参考数。 本质上,我们将创建一个for循环,从用户提供的第一个数字(起始 数)迭代到用户提供的第二个数字(结束数),此循环的增量为1。每次 迭代时,我们都将打印出当前的数。如果数字可以被用户提供的第三个 数整除(整除参考数),则停止循环并打印要结束循环的内容。 为了给你一点挑战,我将写出所有代码,然后分块描述它们的作用。 1 print("Enter a number to start at: ") 2 start_index = parse(Int64, readline()) 3 print("Enter a number to end at: ") 4 end_index = parse(Int64, readline()) 5 print("Enter a divisibility reference number: ") 6 div_ref = parse(Int64, readline()) 78 for iteration_number in start_index:end_index 9 println(iteration_number) 10 if iteration_number % div_ref == 0 11 println("Found the right number! Ending loop.") 12 break 13 end 14 end 我相信你可以理解大部分代码,所以让我们只关注第8行及之后的 第3章 条件和循环4 5 代码。第8行定义了for循环,它非常简单,它告知Julia使用iteration_ number变量在start_index和end_index的值之间进行循环迭代,并运 行其块内代码。此块在第14行(end关键字)结束。 在该块中,我们首先在第9行打印iteration_number,然后if语句检 查iteration_number是否可被整除参考值整除,这一步使用模(modulo) 运算符,这是用百分号(%)表示的。 模运算符返回给定值的除法运算的余数。例如,如果运行 15 / 7 则会得到 2.1428571429 但是如果你想得到这个除法的余数而不是一个小数值,则可以使用 模运算符,其结果为1。 如果要手动执行长除,你将得到以下结果: 15 / 7 Quotient = 2 Remainder = 1 要想证明这一点,你可以做以下计算: 7 * 2 + 1 = 15 (15 - 1) / 2 = 7 现在请记住这条规则:如果A 可以被B整除,那么A 除以B将返 回一个等于0的余数。我们可以利用这个规则,如果A 模B是0,那么 A就可以被B整除。这正是第9行的if语句检查到的条件。 如果遇到一个在循环中可以整除的数,则将发生以下两件事: ① 该程序将打印出“Foundtherightnumber! Endingloop.” 4 6 极简Julia语言———机器学习跃迁之路 ② 该程序将停止循环,这是因为break关键字会中断循环中任何剩 余的迭代。 现在你已经明白了程序是如何工作的,那就继续吧! 你已经赢得了 一个当之无愧的休息机会。 我希望你喜欢构建的这个应用程序,但还有更多的内容! 让我们通 过实现FizzBuzz示例继续复习刚刚学习的for循环、if语句和操作符的 相关内容吧。 FizzBuzz是一个经典的编程练习示例,你可以按照以下指示操作: ① 从数字1循环到100; ② 对于每个可以被3整除的数字打印“Fizz”; ③ 对于每个可以被5整除的数字打印“Buzz”; ④ 对于每个可以同时被3和5整除的数字打印“FizzBuzz”; ⑤ 对于每个不能被3或5整除的数字打印当前的数字。 一开始听起来很复杂,但它真的可以只用11行Julia代码实现。继 续操作并打开FizzBuzz.jl,输入以下代码: 1 for iteration_num in 1:100 2 if (iteration_num % 3 == 0) && (iteration_num % 5 == 0) 3 println("FizzBuzz") 4 elseif (iteration_num % 3 == 0) 5 println("Fizz") 6 elseif (iteration_num % 5 == 0) 7 println("Buzz") 8 else 9 println(iteration_num) 10 end 11 end 我相信你能理解以上这段代码,它实际上就是刚才五个步骤的 实现。有 一个从1到100的循环,还有一个称为iteration_num 的迭代器。 第3章 条件和循环4 7 循环中的第一个条件检查iteration_num 是否可以被3和5同时整除; 如果是,则打印FizzBuzz,如果否,则检查它是否可以被3整除;如果是, 则打印Fizz,如果否,则检查它是否可以被5整除,如果是,则打印Buzz, 如果否,则打印iteration_num。 练习 请问以下代码可以实现FizzBuzz吗? 1 for iteration_num in 1:100 2 if iteration_num % 3 == 0 3 print("Fizz") 4 end 5 if iteration_num % 5 == 0 6 print("Buzz") 7 end 8 if iteration_num % 3 != 0 && iteration_num % 5 != 0 9 print(iteration_num) 10 end 11 print("\n") 12 end 提示:print("\n")可以在屏幕上打印新的一行,换句话说,它会将 光标移到下一行。 3.6 如何使用while循环迭代 正如你所看到的,for循环很棒,但有时它们并不能如你所愿。有 时,你需要更多的灵活性,这就是while循环的作用。使用while循环可 以在特定条件保持true时连续遍历某些代码。下面让我们构建一个简 单的示例,从前面讨论的for循环中复制一些功能。 创建一个名为Next_or_quit.jl的新文件。 4 8 极简Julia语言———机器学习跃迁之路 1 show_next = "c" 2 next_multiple = 0 34 while show_next == "c" 5 global next_multiple = next_multiple + 1 6 println(5 * next_multiple) 7 print("Type c to continue, any other letter to stop: ") 8 global show_next = readline() 9 end 10 println("bye") 下面对上述代码进行说明,它从创建一个值为c的变量show_next 开始,这里选择字母c的意思是continue,它还创建了另一个值为0的变 量next_multiple。第4行显示了使用while循环的一种方式,从关键字 while开始,然后是条件 show_next == "c" 因为我们已经创建了初始值为c的变量show_next,所以条件判断 为true,因此会执行while循环中的代码块,即while和end关键字之间 的代码,即从第5行到第8行。 ① 第5行会将next_multiple的值加1。 ② 第6行打印next_multiple中的值乘以5后的结果。 ③ 第7行打印一条消息,用户输入c可继续,输入任何其他字母则 停止。④ 第8行获取用户的输入。请注意,在这个例子中,因为我们需要来 自用户的字符串,而不是Int64,所以没有使用以前用过的parse(Int64, readline())代码。如果用户想要继续,则需要输入一个字母c;如果用户想 要停止,则需要输任何其他字母。所以我们只需要用到readline(),而不需 要将它转换为Int64。 ⑤ 第9行表示while循环的结束。此时,控制返回到while循环开 第3章 条件和循环4 9 始的地方,然后再次判断条件。如果用户输入c,则条件show_next= ="c"的结果为true,再次运行代码块。但是,如果用户输c之外的任何 其他字母,那么代码块就不再被运行。 ⑥ 第10行将为用户打印消息bye,如果没有其他迭代,则应用程序 将停止。 注意:这与for循环不同,它没有像iteration_number这样的循环 控制变量,因此while循环不会运行预定的次数。用户可以继续显示下 一个乘5运算任意多次,这使得while循环既特别又灵活,你也可以使 用它代替for循环。 但在继续学习之前,我们需要理解一些看起来较新的代码。变量名 show_next和next_multiple之前的global告诉Julia它引用的是在 while循环之外,即前面声明过的变量。如果没有使用关键字global,那 么应用程序将会崩溃,因为Julia不明白你引用的是while循环中的变量 还是在该循环之外创建的变量。 每个块都创建了自己的变量,并且只在该块中使用,一旦退出该代 码块,这些变量就不复存在了。不过,我们需要在while循环的第1行 和第2行使用之前在while循环之外声明过的变量及其值,所以需要使 用关键字global。 下面让我们通过示例探索更多使用while循环的方法。创建一个 名为simple_while.jl的新文件,并输入以下代码: 1 iteration_num = 0 23 while iteration_num < 10 4 global iteration_num += 1 5 println(iteration_num) 6 end 在继续学习之前,需要再次强调两点: 5 0 极简Julia语言———机器学习跃迁之路 ① 为什么我们在iteration_num 之前使用global这个词呢? 我们 将在下一章讨论其真正的复杂性,但简而言之,这是因为我们试图设置 在全局范围(即while循环外)内定义的变量的值,正如第一个while循 环示例Next_or_Quit.jl中描述的那样。 ② 新的操作符“+=”是什么意思呢? 使用“+=”操作符可以告诉 Julia将此运算符左侧的变量值设置为其值加上运算符右侧的值之后 的值。 在继续之前,请先查看以下代码(无须输入): a = 10 a = a + 5 print(a) 该代码将打印 a = 10 a += 5 print(a) 还将打印 15 因为“+=”告诉Julia将变量a的值设置为其值加5。同样,你也可 以使用诸如“-=”“*=”“/=”等操作符的速记代码。 现在让我们回到while循环。请注意,在while关键字的旁边,我们 提供了一个条件,while循环将一遍又一遍地执行代码,直到该条件返回 false,此时循环将停止。因为我们在增加iteration_num 的值,所以它最 终将达到10,使条件为false并停止循环。 但是,上述程序使用for循环会更快,那么什么时候才需要使用 while循环呢? 当你要执行一个循环,但不确定需要循环多少次时应该 第3章条件和循环51 使用whe循环。你已经在应用程序Ne__jl中看到了这样的 例子。 ilxtorQuit. 好,一旦我们知道如何将变量组合在一起,我们就可以在第4章学 习while循环的更多用法了! 你已经学习了两个基本的编程概念:条件和迭代。现在你应该已 经准备好学习更多的中级功能了,其中有更多的类型,并能真正拓宽知 识的应用广度。 强化练习 1. 创建一个应用程序,它可以获取用户输入的3个数,并按照从小 到大的顺序打印。 2. 创建一个程序,它可以遍历用户提供的任意范围的数,并打印除 了能被用户提供的一个参考数整除的那些数以外的所有数字。 3.“== ”和“=”操作符有什么区别? 4. 创建一个程序,它可以编写任意数字的倍数表,并以正向和反向 的顺序显示从1到12 的倍数。 5. 提示用户给出两个正整数x和y,并告知用户较大数是否可以被 较小数整除。