请考虑以下代码:
-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不会抛出异常??
答案 0 :(得分:6)
erlang文档的Guard Sequences部分说:
如果算术表达式,布尔表达式,短路表达式或对保护BIF的调用失败(因为参数无效),则整个保护失败。如果守卫是守卫序列的一部分,则将评估序列中的下一个守卫(即下一个分号后面的守卫)。
换句话说,守卫中的异常被视为守卫在没有提出异常的情况下返回虚假。警卫的评估与正常的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
。
答案 1 :(得分:4)
有关文档,请参阅here。挑剔而准确:
一个防护包含一系列测试,而不是表达式, test 将成功或失败,如果测试中出现错误,它将不会生成一个例外,它将失败。
在守卫中你可以有一个守卫序列,这是一系列由;
分隔的守卫,如果其中一个守卫成功,则整个守卫序列成功。所以;
将候补警卫分开。
在一名后卫中可以进行一系列由,
分隔的防守测试,所有后卫的测试必须成功让整个后卫成功。所以最普遍的后卫将是:
f(...) when <test11>, <test12> ; <test21>, <test22> ; ... ->
那么布尔运算符怎么样呢?它们与测试,
和;
有什么关系呢?在守卫测试中使用布尔运算符并且它们按预期运行是完全合法的, 但 它们不与使用{{1}相同}和,
。特别是在失败方面。所以布尔表达式;
只是一个测试,而不是两个序列。更重要的是<test11> and <test12>
(或使用<test11> or test<21>
)仍然是一个防守测试,而不是两个守卫的序列。因此orelse
中的错误将导致整个警卫失败。鉴于<test11>
,<test11> ; < test21>
中的错误将导致该防护测试失败,并且将尝试替代后卫<test11>
。
这就是@ShiDoiSi提到的评论中建议的原因。您可以使用其中任何一种,但要注意它们的含义以及它们的行为方式。并且记住:警卫包含测试而不是表达式。
P.S。关于为什么会这样的一些历史。实际上非常简单:我们在拥有布尔运算符之前很久就有了守卫,所以当我们最终得到布尔运算符时,守卫的语义已经很好地定义了,而且改变已经太晚了。虽然在守卫中允许布尔表达式允许你编写更精确的守卫,但它确实隐藏了守卫的本性。