case, cond, and if
在本章中,我们将了解case,cond以及if控制流结构。
case
case 允许我们将一个值与许多模式进行比较,直到找到匹配的值:
iex> case {1, 2, 3} do
...> {4, 5, 6} ->
...> "This clause won't match"
...> {1, x, 3} ->
...> "This clause will match and bind x to 2 in this clause"
...> _ ->
...> "This clause would match any value"
...> end
"This clause will match and bind x to 2 in this clause"如果您想对现有变量进行模式匹配,则需要使用该^运算符:
iex> x = 1 1 iex> case 10 do ...> ^x -> "Won't match" ...> _ -> "Will match" ...> end "Will match"
条款还允许通过 guards 指定额外的条件:
iex> case {1, 2, 3} do
...> {1, x, 3} when x > 0 ->
...> "Will match"
...> _ ->
...> "Would match, if guard condition were not satisfied"
...> end
"Will match"上面的第一个条款只有在x肯定时才会匹配。
请记住 guards 的错误不会泄漏,而只是让 guards 失败:
iex> hd(1)
** (ArgumentError) argument error
iex> case 1 do
...> x when hd(x) -> "Won't match"
...> x -> "Got #{x}"
...> end
"Got 1"如果没有任何条款匹配,则会发生错误:
iex> case :ok do ...> :error -> "Won't match" ...> end ** (CaseClauseError) no case clause matching: :ok
有关guards的更多信息,如何使用它们以及允许使用哪些表达式,请参阅guards的完整文档。
注意匿名函数也可以有多个子句和警卫:
iex> f = fn ...> x, y when x > 0 -> x + y ...> x, y -> x * y ...> end #Function<12.71889879/2 in :erl_eval.expr/5> iex> f.(1, 3) 4 iex> f.(-1, 3) -3
每个匿名函数子句中的参数数量必须相同,否则会引发错误。
iex> f2 = fn ...> x, y when x > 0 -> x + y ...> x, y, z -> x * y + z ...> end ** (CompileError) iex:1: cannot mix clauses with different arities in function definition
cond
case当您需要匹配不同的值时,它非常有用。但是,在很多情况下,我们想检查不同的条件并找到第一个评估为真的条件。在这种情况下,可以使用cond:
iex> cond do ...> 2 + 2 == 5 -> ...> "This will not be true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> 1 + 1 == 2 -> ...> "But this will" ...> end "But this will"
这相当于else if许多命令式语言中的子句(尽管在此使用频率较低)。
如果没有条件返回true,则会引发错误(CondClauseError)。出于这个原因,可能需要添加一个true总是匹配的最终条件,等于:
iex> cond do ...> 2 + 2 == 5 -> ...> "This is never true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> true -> ...> "This is always true (equivalent to else)" ...> end "This is always true (equivalent to else)"
最后,注意到cond考虑除了nil并且false是真实的任何价值:
iex> cond do ...> hd([1, 2, 3]) -> ...> "1 is considered as true" ...> end "1 is considered as true"
if和unless
除了case和cond,药剂还提供了宏if/2和unless/2当您需要检查的条件只有一个,其是有用的:
iex> if true do ...> "This works!" ...> end "This works!" iex> unless true do ...> "This will never be seen" ...> end nil
如果if/2返回的条件false或者nil给定的主体之间的do/end条件没有被执行,而是返回nil。恰恰相反unless/2。
他们也支持else块:
iex> if nil do ...> "This won't be seen" ...> else ...> "This will" ...> end "This will"
注意:关于
if/2和有趣的一点unless/2是,它们在语言中被实现为宏; 它们不是特殊的语言结构,因为它们会以多种语言显示。您可以查看文档和源if/2中的Kernel模块文档。该Kernel模块也是运营商喜欢+/2和功能is_function/2定义的地方,默认情况下全部自动导入并在您的代码中可用。
do/end砌块
在这一点上,我们已经学会了四种控制结构:case,cond,if,和unless,和他们都裹着do/end块。发生这种情况我们也可以这样写if:
iex> if true, do: 1 + 2 3
注意上面的例子在true和之间有一个逗号do:,这是因为它使用了 Elixir 的常规语法,其中每个参数都用逗号分隔。我们说这个语法是使用关键字列表。我们也可以else使用关键字:
iex> if false, do: :this, else: :that :that
do/end块是基于关键字之一构建的句法便利。这就是为什么do/end块不需要前一个参数和块之间的逗号。它们非常有用,因为它们在编写代码块时删除了详细信息。这些是等同的:
iex> if true do ...> a = 1 + 2 ...> a + 10 ...> end 13 iex> if true, do: ( ...> a = 1 + 2 ...> a + 10 ...> ) 13
使用do/end块时要记住的一件事是它们总是绑定到最外层的函数调用。例如,下面的表达式:
iex> is_number if true do ...> 1 + 2 ...> end ** (CompileError) undefined function: is_number/2
将被解析为:
iex> is_number(if true) do ...> 1 + 2 ...> end ** (CompileError) undefined function: is_number/2
这导致未定义的函数错误,因为该调用传递两个参数,并且is_number/2不存在。这个if true表达本身是无效的,因为它需要这个块,但由于这个元素is_number/2不匹配,Elixir甚至没有达到它的评价。
添加明确的括号足以将该块绑定到if:
iex> is_number(if true do ...> 1 + 2 ...> end) true
关键字列表在语言中扮演着重要角色,并且在许多功能和宏中很常见。我们将在未来的章节中进一步探讨它们。现在是讨论“二进制文件,字符串和字符列表”的时候了。
