4

考虑以下代码:

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

4

2 回答 2

6

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如果结果为真则不执行。

于 2012-08-14T06:06:05.547 回答
4

有关文档,请参见此处。挑剔并准确地说:

守卫由一系列测试组成,而不是表达式,测试将成功或失败,如果测试中有错误,它不会产生异常,只会失败。

在守卫中,您可以有一个守卫序列,它是由 分隔的守卫序列;,如果其中一个守卫成功,则整个守卫序列成功。所以;分开备用守卫。

在一个守卫中可以有一系列由 分隔的守卫测试,守卫,中的所有测试都必须成功,整个守卫才能成功。因此,最一般的后卫将是:

f(...) when <test11>, <test12> ; <test21>, <test22> ; ... ->

那么布尔运算符呢?它们与测试有什么,关系;?在警戒测试中使用布尔运算符是完全合法的,它们的行为符合预期,它们使用,and不同;。尤其是在失败方面。所以布尔表达式<test11> and <test12>只是一个测试,而不是两个的序列。更重要的是<test11> or test<21>(或使用orelse)仍然是一个警卫测试,而不是两个警卫的序列。所以一个错误<test11>将使整个警卫失败。而如果<test11> ; < test21>出现错误,<test11>则该警卫测试将失败,并且<test21>将尝试替代警卫。

这就是@ShiDoiSi 提到的评论中建议背后的原因。您可以使用其中任何一种,但请注意它们的含义以及它们的行为方式。请记住守卫由测试而不是表达式组成。

PS 关于为什么会这样的一些历史。这真的很简单:早在我们有布尔运算符之前我们就有了守卫,所以当我们最终得到布尔运算符时,守卫的语义已经很好地定义了,现在改变已经太晚了。虽然在守卫中允许布尔表达式允许您编写更精确的守卫,但它确实倾向于隐藏守卫的真实性质。

于 2012-08-14T14:58:17.340 回答