2014-02-21 41 views
4

Erlang在出現問題時可以產生良好的堆棧跟蹤,當程序員想要弄清楚爲什麼出錯時,這很有用。然而,在存在高階函數的情況下,產生堆棧軌跡的機制似乎不足。例如,比較下面的兩個示例(我在代碼的註釋中添加了生成的堆棧跟蹤)。Erlang爲什麼放棄在高階函數的情況下生成堆棧跟蹤?

我讀過關於生成堆棧跟蹤以便延遲評估(例如,在Haskell中)之前的困難。但是由於Erlang是嚴格評估的,我期望在這裏有更好的結果。

我的問題是:是什麼讓高階函數成爲Erlang用來產生堆棧跟蹤的機制的問題?還有其他已知的技術會產生更好的結果嗎?

1 -module(test). 
    2 -export([f/1,a/1]). 
    3 
    4 % Auxilary function to print stack trace (by throwing an exception). 
    5 
    6 s(X) when X < 0 -> 0. 
    7 
    8 % Example 1: Stack trace in presence of a higher order function. 
    9 % 
10 % > test:f(1). 
11 % ** exception error: no function clause matching test:s(1) (test.erl, line 6) 
12 %  in function test:g/2 (test.erl, line 15) 
13 
14 f(X) -> h(fun g/2,X). 
15 g(X,Y) -> s(X) - Y. 
16 h(I,X) -> I(X,3). 
17 
18 % Example 2: Stack trace for chain of 1st order function applications. 
19 % 
20 % > test:a(1).   
21 % ** exception error: no function clause matching test:s(1) (test.erl, line 6) 
22 %  in function test:c/1 (test.erl, line 28) 
23 %  in call from test:b/1 (test.erl, line 27) 
24 %  in call from test:a/1 (test.erl, line 26) 
25 
26 a(X) -> b(X) + 1. 
27 b(X) -> c(X) + 2. 
28 c(X) -> s(X) + 3. 

回答

9

這不是使用高階函數本身,而是使用尾部調用優化的結果。如果函數在返回之前做的最後一件事情是調用另一個函數,那麼Erlang虛擬機優化掉堆棧幀,因爲它不再需要。 (這就是爲什麼你可以在沒有溢出堆棧的情況下進行遞歸。)

在你的例子中,fh都做尾部調用,因此它們的堆棧幀在堆棧跟蹤中不可見。另一方面,a,bc不會執行尾調用,因爲它們必須在返回調用方之前對其調用的函數的結果執行附加操作。

相關問題