簡體   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