在Erlang中调试一个简单的程序

时间:2016-05-03 12:22:05

标签: recursion functional-programming erlang

我刚开始学习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)

编辑:强调添加
我没有从中找到任何有用的信息,我也很欣赏有关如何有效调试的任何指示,但主要是我想知道导致程序失败的原因。

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 == DN 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).

在第一个子句中,我们对NN参数使用相同的变量D。仅当ND相等时,此子句才会运行。在这种情况下,不需要进行rem测试。