应该为 guards 添加一个新的内置函数,
is_between(Term, Lower_Bound, Upper_Bound)
当 Term
、Lower_Bound
和 Upper_Bound
都是整数,且 Lower_Bound =< Term =< Upper_Bound
时成功。
添加一个新的 guard BIF。
is_between(Term, LB, UB)
在表达式中使用时,如果 LB 或 UB 不是整数,则会抛出 badarith 异常,就像尝试对非整数参数执行余数或按位运算一样。在 guard 中使用时,该异常会变成失败。
这是一个类型测试,如果 Term 是一个整数并且介于 LB 和 UB(包括 LB 和 UB)之间,则该测试成功(或返回 true),否则对 Term 的其他值失败(或返回 false)。
作为表达式,它具有与以下相同的效果:
( X = Term, Y = LB, Z = UB,
Y bor Z,
( is_integer(X), X >= Y, X =< Z )
)
其中 X、Y 和 Z 是未导出的新变量。
特别地,
is_integer(tom, dick, harry)
应该引发异常,而不是返回 false,因为只有在发现 LB 和 UB 是整数之后才会测试 is_integer(Term)
。
作为 guard 测试,它具有与以下相同的效果:
( X = Term, Y = LB, Z = UB,
is_integer(Y), is_integer(Z), is_integer(X),
X >= Y, X =< Z
)
如果允许的话。但是,它可以实现更高效的实现。
目前,有些人通过以下方式测试变量是否为字节:
-define(is_byte(X), (X >= 0 andalso X =< 255)).
这是实际的当前做法。但是,它没有检查 X
是否为整数,因此 ?is_byte(1.5)
成功,它可能会评估 X
两次,因此 ?is_byte((Pid ! 0))
将发送两条消息,而不是预期的一条消息,并且当前的 Erlang 编译器在 guards 中为 ‘andalso’ 和 ‘orelse’ 生成的代码明显比为 ‘,’ 和 ‘;’ 生成的代码差。
测试下标是否在范围内也很有用,
-define(in_range(X, T), (X >= 1 andalso X =< size(T))).
这也有类似的问题。
使用 is_between
,我们可以用以下定义替换这些定义:
-define(is_byte(X), is_between(X, 0, 255)).
-define(in_range(X, T), is_between(X, 1, size(T))).
这些定义没有那些问题
这种设计的一种替代方案是遵循 Common Lisp 的示例(以及 HP 3000 上的系统编程语言的更早示例)并允许
E1 =< E2 =< E3 % (<= E1 E2 E3) in Lisp
(并且可能还有
E1 =< E2 < E3
E1 < E2 =< E3
E1 < E2 < E3) % (< E1 E2 E3) in Lisp
作为 guards 和表达式,每个表达式都准确评估一次。我非常喜欢这种语法,并且很高兴看到它。这将解决 E2
的双重评估、E3
可能的未评估以及 ‘andalso’ 的低效率问题。但是,它不会解决字节或索引不仅仅是某个范围内的数字而是整数的问题。如果 Erlang 有多个比较语法,仍然可以使用 is_between/3
。
定义名为 is_between/3
的函数的代码将受到影响。由于 Erlang 编译器在语义分析之前解析整个模块,因此很容易
is_between/3
的定义没有。但是,我们可以草拟一个。需要两个新的 BEAM 指令
{test,is_between,Lbl,[Src1,Src2,Src3]}
{bif,is_between,?,[Src1,Src2,Src3],Dst}
测试执行:
if Src2 is not an integer, goto Lbl.
if Src3 is not an integer, goto Lbl.
if Src1 is not an integer, goto Lbl.
if Src1 < Src2, goto Lbl.
if Src3 < Src1, goto Lbl.
bif 执行:
if Src2 is not an integer, except!
if Src3 is not an integer, except!
if Src1 is not an integer
or Src1 < Src2
or Src3 < Src1
then move 'false' to Dst
else move 'true' to Dst.
这里没有什么根本性的新东西,只是我对如何向仿真器添加指令的不熟悉阻止了我这样做。以及我对如何告诉 HiPE 关于它们的完全无知!
当 Src2 和 Src3 是整数文字时,使用这些指令的变体可能有一些意义;我当然希望 HiPE 在这里省略冗余测试。
编译器只需识别 is_between/3
并发出适当的 BEAM,就像它识别 is_atom/1
一样。我对如何扩展仿真器的无知超过了我对如何扩展编译器的无知。当然,我们需要
...
is_bif(erlang, is_between, 3) -> true;
...
is_guard_bif(erlang, is_between, 3) -> true;
...
is_pure(erlang, is_between, 3) -> true;
...
(但不是 is_safe
规则) 在 erl_bifs.erl
中。或者我们需要吗?我还没弄清楚 is_guard_bif/3
在哪里被调用。还需要在 genop.tab 中添加一个新条目。哦,erl_internal.erl
在 .../stdlib
中,而不是 .../compiler
中。好的,所以需要修补 erl_internal.erl
中的几个函数来识别 is_between/3
;需要更改什么来生成 BEAM?令人恼火的是,如果我熟悉编译器,添加它比编写它更容易。
这是要放入文档中的一些文本
is_integer(Term, LB, UB) -> bool() Types: Term = term() LB = integer() UB = integer()
如果 Term 是一个介于 LB 和 UB(包括 LB 和 UB)之间的整数(LB =< Term,Term =< UB),则返回 true;否则返回 false。在表达式中,如果 LB 或 UB 不是整数,则会引发异常。UB < LB 不是错误。
允许在 guard 测试中使用。
本文档已置于公共领域。