![](/img/trans.png)
[英]How do I debug rebar3 erlang in vscode with the erlang plugin?
[英]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/2
和divide/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/2
, H
的值为[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的跟踪功能而言,我在这里展示的内容几乎没有触及表面,但是发现并解决问题已绰绰有余。
最后,请注意,使用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.