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

时间:2014-10-19 15:39:57

标签: debugging functional-programming erlang pattern-matching

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

不幸的是,我的代码卡在某处。如果这是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].

2 个答案:

答案 0 :(得分:2)

对于打印件,您只需使用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或head是列表中的第一个元素。 T代表尾巴,或者&#34;列表的其余部分&#34;。顾名思义,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.'(单引号意味着这只是一个长原子,而不是一个字符串),它永远不会被返回(你将永远返回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).

这假设处理负数是你现在不关心的事情。

答案 1 :(得分:1)

虽然通过print语句进行调试很常见,有时甚至是有用的,并且io:format可以在Erlang as already noted中用于此目的,但Erlang提供了强大的内置跟踪功能,您应该使用它。

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

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

接下来,我们使用Erlang's dbg module启用对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})
...

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

跟踪输出告诉我们什么?第一行显示divide/1函数的调用,第二行显示divide/1内对列表推导的调用。然后,我们会看到对highest_value/2的调用,首先是完整列表,N设置为0.下一个调用是有趣的:因为您的代码通过[T]而不是{{1}作为递归调用T的第一个参数,highest_value/2具有值H,Erlang认为它大于当前[8,12,16]值4,所以下一个递归调用是:

N

由于highest_value([T], [8,12,16]). T,因此变为:

[]

此处highest_value([[]], [8,12,16]). H[]也为T[]不大于H,因此此后所有剩余的递归调用都与此相同,并且递归是无限的。

要解决此问题,您需要正确传递[8,12,16] as already noted

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语句遇到了新问题,并且已经解释了此问题的解决方案in another answer所以我赢了在这里重复一遍。

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

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

就Erlang的追踪功能而言,我在这里展示的内容几乎没有表面,但它足以找到并解决问题。

最后,请注意,使用if标准库调用,您可以更轻松地实现模块尝试执行的操作:

lists:max/1