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