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