为什么这个后卫不会抛出异常?

时间:2012-08-14 05:47:08

标签: erlang

请考虑以下代码:

    -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不会抛出异常??

2 个答案:

答案 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。关于为什么会这样的一些历史。实际上非常简单:我们在拥有布尔运算符之前很久就有了守卫,所以当我们最终得到布尔运算符时,守卫的语义已经很好地定义了,而且改变已经太晚了。虽然在守卫中允许布尔表达式允许你编写更精确的守卫,但它确实隐藏了守卫的本性。