添加最大值和最小值核心函数。
目前,Erlang 语言没有内置支持最大值和最小值操作。因此,我们添加新函数
erlang:min(E1, E2)
,其效果和值与
(T1 = E1, T2 = E2, if T1 > T2 -> T2 ; true -> T1 end)
erlang:max(E1, E2)
相同,其效果和值与
(T1 = E1, T2 = E2, if T1 > T2 -> T1 ; true -> T2 end)
除了我们期望它们使用单个 VM 指令实现,并且我们期望 HiPE 在具有条件移动的机器上使用条件移动。
当且仅当没有本地定义的 max/2
(或 min/2
) 时,max/2
(或 min/2
) 的 erlang:
模块前缀可以省略。
最大值和最小值是非常有用的操作。Erlang 中没有标准方法来表达它们的事实产生了可预见的结果:在 tool_utils
、tv_pg_gridfcns
、tv_pb
、tv_comm_func
、ssh_connection_handler
、bssh_connection_handler
、ssh_cli
、hipe_arm
、hipe_schedule
、hipe_ultra_prio
、hipe_ppc_frame
、?HIPE_X86_FRAME
(据推测,每个 32 位和 64 位 PC 各有一个)、hipe_sparc_frame
、erl_recomment
、erl_syntax_lib
、appmon_info
中都有 max/2
的定义,哦,这个列表还在不断增加。有几十个副本。min/2
的副本也几乎一样多。这还不包括可能使用不同名称的副本。
这些操作不仅有用,而且编译器可以比程序员更有效地实现它们。如果 X < Y
可以是一个 VM 指令,那么 min
和 max
也可以是。这是一个初步的实现草案
OpCase(i_minimum): {
r(0) = CMP_GT(tmp_arg1, tmp_arg2)) ? tmp_arg1 : tmp_arg2;
Next(1);
}
OpCase(i_maximum): {
r(0) = CMP_GT(tmp_arg1, tmp_arg2)) ? tmp_arg2 : tmp_arg1;
Next(1);
}
注意:未经测试的代码!除此之外,我不知道所有需要更新的地方,或者在添加新指令时如何更新。这些指令旨在像 < 和其他朋友一样,以 i_fetch
指令开头。
这比 Erlang 函数调用便宜得多,并且 HiPE 更容易识别何时涉及两个浮点数的最大值或最小值,并可以将其转换为比较和条件移动。
最重要的是消除了思想上的障碍。当我编写 Fortran 时,我知道 max 和 min 已经存在了几十年,我自由地使用这些操作。当我编写 C 时,我知道这些操作不存在,并且传统的宏存在问题,所以我避免使用它们。作为一个实验,我为我维护的 AWK 版本添加了 max() 和 min() 函数。这很容易,结果是我现在有很多 AWK 代码无法被其他任何东西运行,因为这些操作非常方便。Erlang 除了 lists
模块中的函数外,没有其他文档化的最大值或最小值函数,编写 lists:max([X,Y])
足够令人痛苦,以至于只有最坚定的人才会使用它。
函数还是运算符?
我相信使用来自格理论的标准 /\
和 \/
符号有充分的理由。然而,在 EEPs 邮件列表中的讨论表明,社区分为
作为语言标准组成部分的操作的随时可用性比它们的名称重要得多,因此为了增加接受度,此 EEP 的第二个草案切换为内置函数。
最终让我下定决心的论点是国际化论点:日本程序员可能正在使用键盘,其中 \
表示或者屏幕上 \
显示为日元,因此 /\
和 \/
对他们来说根本不起作用。
我们不能将 max
和 min
用作运算符,因为编译器不会让你将符号同时用作运算符和函数名称,并且有很多使用 max
和 min
作为函数名称的情况。这正是我们试图在此处解决的问题。因此,它们必须是函数名称。
向 erlang:
模块添加新函数没有太大困难。
我不想在这里编写 erlang:
前缀。对于某些函数,使 erlang:
前缀可选也不是什么新鲜事。
我们想要的是让现有模块拥有自己的 max/2
和/或 min/2
的定义仍然是合法的,然后只需删除冗余定义即可进行升级。
假设你想找到一组 2D 点的边界框。(这是从 Wings3D 中的代码改编而来。)
bounding_box([{X0,Y0}|Pts]) ->
bounding_box(Pts, X0,X0, Y0,Y0).
bounding_box([{X,Y}|Pts], Xlo,Xhi, Ylo,Yhi) ->
if X < Xlo -> Xlo1 = X, Xhi1 = Xhi
; X > Xhi -> Xlo1 = Xlo, Xhi1 = X
; true -> Xlo1 = Xlo, Xhi1 = Xhi
end,
if Y < Ylo -> Ylo1 = Y, Yhi1 = Yhi
; Y > Yhi -> Ylo1 = Ylo, Yhi1 = Y
; true -> Ylo1 = Ylo, Yhi1 = Yhi
end,
bounding_box(Pts, Xlo1,Xhi1, Ylo1,Yhi1);
bounding_box([], Xlo,Xhi, Ylo,Yhi) ->
{{Xlo,Ylo}, {Xhi,Yhi}}.
使用最大值和最小值运算符,这将变为
bounding_box([{X,Y}|Pts], Xlo,Xhi, Ylo,Yhi) ->
bounding_box(Pts, min(X,Xlo), max(X,Xhi),
min(Y,Ylo), max(Y,Yhi));
bounding_box([], Xlo,Xhi, Ylo,Yhi) ->
{{Xlo,Ylo}, {Xhi,Yhi}}.
没有问题。如果模块已经具有 max/2
或 min/2
,则需要 erlang:
前缀才能获得新函数。
我对 BEAM 或编译器的理解不够深入,无法提供一个参考实现,但上面的指令定义可以证明对于那些了解的人来说应该不难。如果此 EEP 被接受,我很乐意为这些运算符编写文档。
本文档已置于公共领域。