创建多重函数来控制程序流程有时会很费精力。在NumberCompare模块的示例中,我们必须构建辅助函数来处理>=运算结果。为琐碎的任务创建过多函数会让代码难以维护。可以使用Elixir的卫语句(guard clause)来解决这个问题。卫语句可以在函数中添加布尔表达式,为函数子句增加更多功能。
在函数参数之后加上when关键字,就能创建卫语句。让我们用它改进NumberCompare的代码:
在IEx中尝试上面的代码:
我们使用卫语句检查哪个数更大;一个函数返回第一个数,另一个函数返回第二个数。表达式number >= other_number是卫语句。如果是true,则执行该函数,返回变量number。如果表达式为false,将尝试运行第二个函数子句。第二个子句不包含任何阻止程序执行的检查,所以它总能匹配成功。
使用卫语句可以创建更简洁的函数,减少我们对辅助函数的需求。我们还可以用卫语句限定数据范围。让我们改进上一章创建的Checkout模块。它使用税率计算产品的总成本。应该确保输入的参数不是负数。在checkout.ex文件中输入以下内容:
表达式price >= 0和tax_rate >= 0确保参数都不是负数。在IEx中试试:
这里用了三组输入值测试卫语句。传递正数时,一切正常。传递负数时,出现了FunctionClauseError错误,这意味着函数total_cost/2无法处理负数。传递"Hello,World!"时,出现的错误是ArithmeticError,它发生在表达式price* (tax_rate+1)中,这意味着"Hello,World!"通过了卫语句的检查;换句话说,"Hello,World!"大于0。
将字符串与数字做比较看起来很奇怪。不过,Elixir的确可以比较文本、数字和其他类型的数据,这样就能对混合类型的列表进行排序,所以"Hello,World!"能通过卫语句的检查。Elixir是动态类型语言,开发人员通常不需要创建冗长的卫语句表达式检查数据类型。如果你希望提高数据类型的安全性,可以使用Elixir提供的函数,例如,用Kernel.is_integer/1检查整数。
Elixir和类型声明
Elixir是动态类型语言;编译器不会根据类型声明优化或修改代码。这意味着用Elixir编程时,我们不需对每个函数和变量做类型声明。相反,我们应该借助自动测试和模式匹配来确保代码的可用性。不过,类型规范有助于编写文档,也有利于静态分析找出不一致的问题和潜在错误。对Elixir的类型规范工具感兴趣的读者,可以查阅官方文档。[5]
匿名函数的参数也可以中使用模式匹配和卫语句。在函数体后面加上->运算符编写卫语句。让我们用匿名方式重写函数NumberCompare.check/2:(www.xing528.com)
Elixir官方文档列举了卫语句中允许使用的函数和运算符。[6]卫语句中不能使用标准函数,因为匹配算法需要非常快并且没有副作用才可行。只用足够快的函数才能用在卫语句里。Elixir列出了符合条件的函数。这个列表可以用Elixir宏(macro)函数进行扩展。
让我们用宏函数创建一个程序,判断数字是偶数还是奇数。这里要用到Elixir的宏函数is_even/1和is_odd/1。你可以在Elixir文档的整数部分找到它们。[7]创建一个名为even_or_odd.ex的文件,然后输入以下代码:
在IEx中运行它:
这里出现了一个新指令require。我们需要使用它,因为is_even/1和is_odd/1是宏函数。宏会在求值之前生成代码。例如,当我们使用Integer.is_even(2)时,它会在编译阶段生成代码(2 &&& 1) == 0。然后,当我们运行代码时,表达式的求值结果为true。Elixir的编译器需要require指令才能在编译阶段使用该模块。使用require指令还要注意它的词法作用域。请看:
require Integer出现在EvenOrOdd.is_even/1函数中,这意味着Integer宏函数仅在那里可用。EvenOrOdd.is_odd/1函数也想使用Integer宏函数。如果我们编译这个文件,将得到一个编译错误,提示EvenOrOdd.is_odd/1函数中缺少require Integer。
&&&是按位与运算符。它逐位检查每个值,如果两边的位均为1,则将值设置为1。本书不打算详细介绍位运算符,感兴趣的读者可以查看Elixir的官方文档。[8]
我们可以使用defguard指令轻松创建在卫语句中使用的宏函数。在模块中重用常见的卫语句非常方便。例如,让我们向Checkout模块添加一个新函数,并重用卫语句:
试试上面的代码:
借助宏函数能够创建更多用于卫语句的函数。唯一的规则是生成的代码必须遵守卫语句允许的函数列表。宏是Elixir元编程的一部分,你可以在Elixir的官方入门指南中查阅相关内容。[9]
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。