我刚开始学习erlang(和函数式编程),我被困在一个简单的程序上。该程序的目的是找到一个数字的最大素数因子。这是我的计划:
lprime(N, L, D) when D == N->
if N rem D == 0 -> D;
true-> L
end;
lprime(N,L,D) ->
if N rem D == 0 ->
lprime(N/D, D, D);
true -> lprime(N, L, D+1)
end.
lprime(N)->
lprime(N,1,2).
以下是一些输入的运行方式:
lprime(3)->lprime(3,1,2)->lprime(3,1,3)->3
lprime(36)->lprime(36,1,2)->lprime(18,2,2)->lprime(9,2,2)->lprime(9,2,3)->lprime(3,3,3)->3
lprime(14)->lprime(14,1,2)->lprime(7,2,2)->lprime(7,2,3)->lprime(7,2,4)->lprime(7,2,5)->lprime(7,2,6)->lprime(7,1,7)->7
但是程序总是返回第一个主要除数。 lprime(24)->2, lprime(9)->3
我在Python中编写了一个等效的(在我看来)程序,我更熟悉它,完全符合预期:
def lprime(N, L=1, D=2):
if D==N:
if N%D == 0: return D
return L
if N%D == 0:
return lprime(N/D, D, D)
return lprime(N, L, D+1)
我还尝试了另一个没有后卫的版本(它看起来也更干净)但是这个版本似乎进入无限递归,同样python等价物(IMO)按预期工作:
lprime2(1, L, D) ->
L;
lprime2(N,L,D) ->
if N rem D == 0 ->
lprime2(N/D, D, D);
true -> lprime2(N, L, D+1)
end.
lprime2(N)->
lprime2(N,1,2).
我试图使用dbg调试程序,其文档非常稀疏,我不太了解这些步骤。我使用的步骤是:
1> dbg:start().
{ok,<0.35.0>}
2> dbg:tracer().
{ok,<0.35.0>}
3> dbg:tp(first,lprime, 1, []).
{ok,[{matched,nonode@nohost,1}]}
4> dbg:tp(first,lprime,3,[]).
{ok,[{matched,nonode@nohost,1}]}
5> dbg:p(all,c).
{ok,[{matched,nonode@nohost,26}]}
6> first:lprime(10).
(<0.33.0>) call first:lprime(10)
2
7> first:lprime(10,1,2).
(<0.33.0>) call first:lprime(10,1,2)
编辑:强调添加
我没有从中找到任何有用的信息,我也很欣赏有关如何有效调试的任何指示,但主要是我想知道导致程序失败的原因。
答案 0 :(得分:1)
移植代码的错误在Python中,5 / 2 == 2
在Erlang中,5 / 2 == 2.5
。您需要使用div
运算符:5 div 2 == 2
1> 5 / 2.
2.5
2> 5 div 2.
2
因此,在您的代码中,替换:
lprime(N/D, D, D);
使用:
lprime(N div D, D, D);
通过此更改,我得到了预期的输出:
2> a:lprime(3).
3
3> a:lprime(36).
3
4> a:lprime(14).
7
关于您的逻辑,我非常确定N == D
,N rem D
是否始终等于0
,因此您可能希望简化那里的代码。< / p>
答案 1 :(得分:1)
您使用的是浮点除法而不是整数除法,这会导致rem
中出现异常。但是你没有看到这些例外,因为你在警卫中呼叫rem
。您可以使用case
而非if
:
lprime2(1, L, D) ->
L;
lprime2(N,L,D) ->
case N rem D of
0 -> lprime2(N/D, D, D);
_ -> lprime2(N, L, D+1)
end.
lprime2(N)->
lprime2(N,1,2).
这将让您看到例外情况:
1> c(lp).
lp.erl:4: Warning: variable 'D' is unused
{ok,lp}
2> lp:lprime2(14).
** exception error: an error occurred when evaluating an arithmetic expression
in function lp:lprime2/3 (/tmp/lp.erl, line 7)
要解决此问题,请在div
的第二个条款中使用/
而不是lprime/3
:
lprime2(N,L,D) ->
case N rem D of
0 -> lprime2(N div D, D, D);
_ -> lprime2(N, L, D+1)
end.
通常,惯用的Erlang代码使用case
而不是if
,因为后者只允许其子句中的守卫。
另外需要注意的是,在代码中使用函数子句(以及Python代码)中的警卫,当N == D
时,N rem D
将始终为真,所以你可以简化代码:
lprime(N,_,N) ->
N;
lprime(N,_,D) when N rem D == 0 ->
lprime(N div D, D, D);
lprime(N,L,D) ->
lprime(N, L, D+1).
在第一个子句中,我们对N
和N
参数使用相同的变量D
。仅当N
和D
相等时,此子句才会运行。在这种情况下,不需要进行rem
测试。