繁体   English   中英

如何检查我的代码在Erlang中卡住的位置?

[英]How do I check where my code gets stuck in Erlang?

我正在尝试编写一个函数,该函数接收列表,在列表中找到最大值的整数,然后将列表中的所有其他整数除以该值。

不幸的是,我的代码卡在了某个地方。 例如,如果这是python,我可以轻松地编写几个不同的“打印件”,看看它卡在哪里。 但是,您如何在Erlang中做到这一点?

这是代码。

highest_value([], N) ->
    if
        N =:= 0 ->
            'Error! No positive values.'
    end,
    N;
highest_value([H|T], N) when H > N, H > 0 ->
    highest_value([T], H);
highest_value([_|T], N) ->
    highest_value([T], N).

divide(_L) -> [X / highest_value(_L, 0) || X <- _L].

对于打印,您可以只使用io:format/2 一样。

highest_value([H|T], N) when H > N, H > 0 -> 
   io:format(">>> when H bigger than N~n"),
   io:format(">>> H: ~p,  T: ~p, N: ~p ~n", [H, T, N]),
   highest_value([T], H);
highest_value(List) ->
   highest_value(List, 0).

编辑

您弄错的一件事是[H | T] [H | T]语法。 H或head是列表中的第一个元素。 T代表尾巴,或“其余列表”。 顾名思义,tail是一个列表(可以是一个空列表,但仍然是一个列表)。 因此,当您进行递归操作时,无需将T放入新列表中。

highest_value([H|T], N) when H > N -> 
     highest_value(T, H); 

highest_value([_|T], N) -> 
     highest_value(T, N).

在您的旧代码中,您调用了:

   highest_value([T], N).

它使用一个元素创建了一个新列表,例如[[2,3,4,5]] 如果将其尾部放置,则将此仅元素列表作为头部,并将空列表作为尾部。


另外,在第一个函数子句中,您有一个原子'Error! No positive values.' 'Error! No positive values.' (单引号表示这只是一个长原子,而不是字符串),它永远不会返回(您将始终返回N )。 如果您想返回某个原子或N ,则取决于N值,您可以扩展对函数子句的使用

highest_value([], 0) ->
   'Error! No positive values.'
highest_value([], N) ->
   N;
[...]

而且您必须使用0初始化函数,这可能被认为是错误的模式。 您可以编写并使用highest_value/1为您执行此操作

highest_value(List) ->
   highest_value(List, 0).

甚至使用此算法的修改形式:由于最大的数字将是列表中的数字之一,因此可以将第一个元素用作函数初始化。

highest_value(_List = [First|T]) when First > 0 ->
   highest_value(T, First).

假设您现在不关心处理负数。

尽管通过print语句进行调试是很普遍的,甚至有时是有用的,并且io:format可以在Erlang中用于此目的( 如前所述) ,但Erlang提供了应使用的强大的内置跟踪功能。

假设您的highest_value/2divide/1函数驻留在名为hv的模块中。 首先,我们在Erlang shell中编译hv

1> c(hv).
{ok,hv}

接下来,我们使用Erlang的dbg模块启用对hv函数的跟踪:

2> dbg:tracer().
{ok,<0.41.0>}
3> dbg:p(self(),call).
{ok,[{matched,nonode@nohost,26}]}
4> dbg:tpl(hv,c).
{ok,[{matched,nonode@nohost,5},{saved,c}]}

在命令2中,我们启用调试跟踪,在命令3中,我们指示我们要跟踪当前进程中的函数调用(由self()返回)。 在命令4中,我们使用内置的c跟踪规范在hv模块中的所有函数上创建一个调用跟踪。

启用调试跟踪后,我们将调用hv:divide/1并开始跟踪输出:

5> hv:divide([4,8,12,16]).
(<0.34.0>) call hv:divide([4,8,12,16]) ({erl_eval,do_apply,6})
(<0.34.0>) call hv:'-divide/1-lc$^0/1-0-'([4,8,12,16],[4,8,12,16]) ({erl_eval,
                                                                     do_apply,
                                                                     6})
(<0.34.0>) call hv:highest_value([4,8,12,16],0) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[8,12,16]],4) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[]],[8,12,16]) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[]],[8,12,16]) ({hv,'-divide/1-lc$^0/1-0-',2})
...

首先,请注意,我缩写了跟踪输出,因为在...点处它已经处于无限循环中,并且跟踪的其余部分与...之前的两个语句相同。

跟踪输出告诉我们什么? 第一行显示了divid divide/1函数的调用,第二行显示了对divide/1内部列表理解的调用。 然后,我们看到对highest_value/2调用,首先将完整列表和N设置为0。下一个调用很有趣:因为在递归调用highest_value/2您的代码将[T]而不是T作为第一个参数highest_value/2H的值为[8,12,16] ,Erlang将其视为大于当前N值4,因此下一个递归调用为:

highest_value([T], [8,12,16]).

并且由于T[] ,因此变为:

highest_value([[]], [8,12,16]).

在这里, H[]T也是[] H不大于[8,12,16] ,因此在这一点之后所有剩余的递归调用都与此相同,并且递归是无限的。

为了解决这个问题,您需要正确地传递T ,如前所述

highest_value([H|T], N) when H > N, H > 0 ->
    highest_value(T, H);
highest_value([_|T], N) ->
    highest_value(T, N).

然后重新编译,这也会重新加载您的模块,因此,您还需要再次设置调试跟踪:

5> c(hv).
{ok,hv}
6> dbg:tpl(hv,c).
{ok,[{matched,nonode@nohost,5},{saved,c}]}
7> hv:divide([4,8,12,16]).
(<0.34.0>) call hv:divide([4,8,12,16]) ({erl_eval,do_apply,6})
(<0.34.0>) call hv:'-divide/1-lc$^0/1-0-'([4,8,12,16],[4,8,12,16]) ({erl_eval,
                                                                     do_apply,
                                                                     6})
(<0.34.0>) call hv:highest_value([4,8,12,16],0) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([8,12,16],4) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([12,16],8) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([16],12) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([],16) ({hv,'-divide/1-lc$^0/1-0-',2})
** exception error: no true branch found when evaluating an if expression
     in function  hv:highest_value/2 (/tmp/hv.erl, line 5)
     in call from hv:'-divide/1-lc$^0/1-0-'/2 (/tmp/hv.erl, line 15)

现在跟踪显示, highest_value/2可以按预期工作,但是我们现在使用if语句遇到了一个新问题,并且此问题的解决方法已经在另一个答案中进行了说明因此在此不再赘述。

如您所见,Erlang的跟踪功能远比使用“打印调试”功能强大。

  • 可以根据需要在Erlang Shell中以交互方式打开和关闭它。
  • 与其他语言的调试不同,调试跟踪不需要为模块提供特殊的编译标志。
  • 与调试打印语句不同,您无需更改代码并重复重新编译。

就Erlang的跟踪功能而言,我在这里展示的内容几乎没有触及表面,但是发现并解决问题已绰绰有余。

最后,请注意,使用lists:max/1标准库调用可以更轻松地实现模块要执行的操作:

divide(L) ->
    case lists:max(L) of
        N when N > 0 ->
            [V/N || V <- L];
        _ ->
            error(badarg, [L])
    end.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM