考虑以下代码:
-module(abc).
-export([f/1]).
f(X) when (X==0) or (1/X>2) -> X+100;
f(X) ->X.
和 abc:f(0)。得到结果 0 ,但为什么 1/X 不抛出异常?
考虑以下代码:
-module(abc).
-export([f/1]).
f(X) when (X==0) or (1/X>2) -> X+100;
f(X) ->X.
和 abc:f(0)。得到结果 0 ,但为什么 1/X 不抛出异常?
erlang 文档的Guard Sequences部分说:
如果算术表达式、布尔表达式、短路表达式或对保护 BIF 的调用失败(由于参数无效),则整个保护失败。如果守卫是守卫序列的一部分,则将评估序列中的下一个守卫(即下一个分号后面的守卫)。
换句话说,守卫中的异常被视为如果守卫返回 false 而不引发异常。守卫的评估与正常的 erlang 表达式略有不同。
当您调用 时,将评估abc:f(0)
表达式。(0==0) or (1/0>2)
这个表达式“失败”,因为除以零,所以守卫不匹配,下一个子句被评估以给出答案0
。
如果您希望这种情况返回100
,您有两个选择:使用保护序列或使用短路布尔运算符。这些将是
f(X) when X==0; 1/X>2 -> X + 100;
f(X) -> X.
和
f(X) when X==0 orelse 1/X>2 -> X + 100;
f(X) -> X.
分别。两种编写方式都将评估X==0
为单独的异常,1/X>2
如果结果为真则不执行。
有关文档,请参见此处。挑剔并准确地说:
守卫由一系列测试组成,而不是表达式,测试将成功或失败,如果测试中有错误,它不会产生异常,只会失败。
在守卫中,您可以有一个守卫序列,它是由 分隔的守卫序列;
,如果其中一个守卫成功,则整个守卫序列成功。所以;
分开备用守卫。
在一个守卫中可以有一系列由 分隔的守卫测试,守卫,
中的所有测试都必须成功,整个守卫才能成功。因此,最一般的后卫将是:
f(...) when <test11>, <test12> ; <test21>, <test22> ; ... ->
那么布尔运算符呢?它们与测试有什么,
关系;
?在警戒测试中使用布尔运算符是完全合法的,它们的行为符合预期,但它们与使用,
and不同;
。尤其是在失败方面。所以布尔表达式<test11> and <test12>
只是一个测试,而不是两个的序列。更重要的是<test11> or test<21>
(或使用orelse
)仍然是一个警卫测试,而不是两个警卫的序列。所以一个错误<test11>
将使整个警卫失败。而如果<test11> ; < test21>
出现错误,<test11>
则该警卫测试将失败,并且<test21>
将尝试替代警卫。
这就是@ShiDoiSi 提到的评论中建议背后的原因。您可以使用其中任何一种,但请注意它们的含义以及它们的行为方式。请记住:守卫由测试而不是表达式组成。
PS 关于为什么会这样的一些历史。这真的很简单:早在我们有布尔运算符之前我们就有了守卫,所以当我们最终得到布尔运算符时,守卫的语义已经很好地定义了,现在改变已经太晚了。虽然在守卫中允许布尔表达式允许您编写更精确的守卫,但它确实倾向于隐藏守卫的真实性质。