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

Recursion 在Erlang中调试一个简单的程序,recursion,functional-programming,erlang,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

我刚刚开始学习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
但是程序总是返回第一个素数除数<代码>首字母(24)->2,首字母(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)
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).
我还尝试了另一个没有防护装置的版本(它看起来也更干净),但这一版本似乎进入了无限递归,同样,python等价物(IMO)也按预期工作:

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)
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调试程序,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)
1>dbg:start()。
{好的,}
2> dbg:tracer()。
{好的,}
3> dbg:tp(第一,lprime,1,[])。
{好的,[{匹配,nonode@nohost,1}]}
4> dbg:tp(第一,lprime,3,[])。
{好的,[{匹配,nonode@nohost,1}]}
5> dbg:p(全部,c)。
{好的,[{匹配,nonode@nohost,26}]}
6> 第一:lprime(10)。
()先打电话:lprime(10)
2.
7> 第一个:lprime(10,1,2)。
()先调用:lprime(10,1,2)
编辑:添加强调符号

我没有从中找到任何有用的信息,我也很感激任何关于如何有效调试的建议,但主要是我想知道是什么导致程序失败

移植代码的错误在于,在Python中,
5/2==2
而在Erlang中,
5/2==2.5
。您需要使用
div
运算符:
5div2==2

1> 5 / 2.
2.5
2> 5 div 2.
2
因此,在代码中,替换:

lprime(N/D, D, D);
与:

通过此更改,我获得了预期的输出:

2> a:lprime(3).
3
3> a:lprime(36).
3
4> a:lprime(14).
7

关于您的逻辑,我非常确定如果
N==D
N rem D
总是等于
0
,那么您可能需要简化那里的代码。

移植代码的错误是,在Python中,
5/2==2
而在Erlang中,
5/2==2.5
。您需要使用
div
运算符:
5div2==2

1> 5 / 2.
2.5
2> 5 div 2.
2
因此,在代码中,替换:

lprime(N/D, D, D);
与:

通过此更改,我获得了预期的输出:

2> a:lprime(3).
3
3> a:lprime(36).
3
4> a:lprime(14).
7

关于您的逻辑,我很确定如果
N==D
nremd
总是等于
0
,那么您可能需要简化那里的代码。

您使用的是浮点除法而不是整数除法,这会导致
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)
要修复它,请在
lpinite/3
的第二个子句中使用
div
而不是
/

    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
D
参数使用相同的变量
N
。只有当
N
D
相等时,此子句才会运行。在这种情况下,不需要进行
rem
测试。

您使用的是浮点除法而不是整数除法,这导致
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)
要修复它,请在
lpinite/3
的第二个子句中使用
div
而不是
/

    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
D
参数使用相同的变量
N
。只有当
N
D
相等时,此子句才会运行。在这种情况下,不需要进行
rem
测试。

使用trace和dbg复制@SteveVinoski是我的次要问题,主要是我想知道这个程序失败的原因。另一个工具是调试器应用程序:debugger:start()debugger:start()是一个图形工具吗?我找不到它可以与哪个软件包一起使用,或者它是任何IDE的一部分。使用trace和dbg可能复制@SteveVinoski是我的第二个关注点,主要是我想知道这个程序失败的原因。另一个工具是调试器应用程序:debugger:start()debugger:start()是图形工具吗?我找不到它可以与哪个软件包一起使用,或者它是任何IDE的一部分。谢谢。在我使用的几乎每种语言中,“/”都默认为整数除法,并且必须强制转换浮点。而if/case澄清是最有帮助的。我用“了解一些Erlang”作为参考,它特别引入了“如果”作为“如果是什么”,现在我知道了原因。谢谢。在我使用的几乎每种语言中,“/”都默认为整数除法,并且必须强制转换浮点。而if/case澄清是最有帮助的。我使用“LearningyousomeErlang”作为参考,它特别引入了“if”作为“whatheif”,现在我知道了原因。