case,cond和if在这一章中我们将学习case,cond和if的流程控制结构。
casecase允许我们对很多模式的值进行比较,知道找到匹配的:
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
如果你想对一个现存的变量进行模式匹配,你需要^操作符:
iex> x = 1
1
iex> case 10 do
...> ^x -> "Won't match"
...> _ -> "Will match"
...> end
字句也允许用附加调教作为守护:
iex> case {1, 2, 3} do
...> {1, x, 3} when x > 0 ->
...> "Will match"
...> _ ->
...> "Won't match"
...> end
在上面的例子中,第一个字句只有当x的值为正的时候才匹配。Erlang虚拟机只允许有限的一些表达式作为守护:
==, !=, ===, !==, >, <, <=, >=)and, or) and negation operators (not, !)+, -, *, /)<> 和 ++in 操作符下面的这些类型检查hanshu:
is_atom/1is_binary/1is_bitstring/1is_boolean/1is_float/1is_function/1is_function/2is_integer/1is_list/1is_map/1is_number/1is_pid/1is_port/1is_reference/1is_tuple/1外加这些函数:
abs(number)bit_size(bitstring)byte_size(bitstring)div(integer, integer)elem(tuple, n)hd(list)length(list)map_size(map)node()node(pid | ref | port)rem(integer, integer)round(number)self()size(tuple | bitstring)tl(list)trunc(number)tuple_size(tuple)谨记,守护函数中的错误是不可见的,它只能让守护函数失效:
iex> hd(1)
** (ArgumentError) argument error
:erlang.hd(1)
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
注意匿名函数也能用好几个子句和守护:
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
每个匿名函数的子句所接受的参数量必须相同,不然也会报错。
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子句,Elixir也有if和else不过用的较少。
如果所有的条件都不为真,会导致一个错误。因此,有必要把最后一个条件设置为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
最后,请注意在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,当你值需要检查一个条件的时候,Elixir也提供了两个有用的宏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有趣的地方在于它们在语言中是用宏的方式实现的,和在其他语言中一样它们是语言的特殊构件。你可以在kernel模块文档里查阅if/2的文档和代码。类似+/2这样的操作符和is_function/2之类的函数都位于Kernel模块,当程序启动的时候它们会自动被载入。
do 块到目前为止,我们已经学习了四种控制结构:case,cond,if和unless,并且它们都被do/end块所包含。其实if也能写成这样:
iex> if true, do: 1 + 2
3
在Elixir中,do/end块是一种把一堆表带式传给do:的方便表示。下面两个是等价的:
iex> if true do
...> a = 1 + 2
...> a + 10
...> end
13
iex> if true, do: (
...> a = 1 + 2
...> a + 10
...> )
13
我们说第二中语法用了关键字列表。else也可以用同样的表达:
iex> if false, do: :this, else: :that
:that
牢记一个细节非常重要,当我们用do/end块,它们总是和最远的那个函数调用绑定。例如,下面的表达式:
iex> is_number if true do
...> 1 + 2
...> end
会被解析成:
iex> is_number(if true) do
...> 1 + 2
...> end
这会导致Elixir试图去调用函数if/1, 当然会抛出一个未定义函数错误。加上圆括号可以避免这样的歧义:
iex> is_number(if true do
...> 1 + 2
...> end)
true
关键字列表在这个语言中扮演了一个重要的角色,在很多的函数和宏中都广泛地应用。我们将未来的章节中对它们进行进一步的的探索。现在是时候谈谈关于“二进制,字符串和字符列表”了。