Elixir 1.5

case, cond, and if

case, cond, and if

在本章中,我们将了解casecond以及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

除了casecond,药剂还提供了宏if/2unless/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砌块

在这一点上,我们已经学会了四种控制结构:casecondif,和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

关键字列表在语言中扮演着重要角色,并且在许多功能和宏中很常见。我们将在未来的章节中进一步探讨它们。现在是讨论“二进制文件,字符串和字符列表”的时候了。