2009-12-02 53 views
2

說我在Erlang中有一些函數fn1(),如果函數成功執行則返回{ok, Result},如果有錯誤返回{error, "ErrorReason"}Erlang:在這種情況下更有效率 - 使用try catch還是case語句?

現在在另一個函數fn2()中我調用fn1(),我需要檢查fn1的結果並且只有在它爲{ok, Result}時才繼續。

我想,我可以使用這兩種情況下做到這一點或嘗試抓住。但效率是我最關心的,我想知道這下面的兩種方法更有效:

try-catch方法

fn2() -> 
    try 
     {ok, Result} = fn1(), 
     %Do something with Result 
     ok 
    catch 
     throw:Term -> Term; 
     exit:Reason -> {exit, Reason}; 
     error:Reason -> {error,{Reason,erlang:get_stacktrace()}} 
    end. 

case方法

fn2() -> 
    Res = fn1(), 
    case Res of 
     {ok, Result} -> 
     %Do something with Result 
     ok; 
     {error, Reason} -> 
     Reason 
    end. 

回答

8

案例方法會更有效率,因爲它只是模式匹配,並且不涉及b建立一個調用堆棧和東西。

在這兩個例子中,你將要處理的「錯誤」在本地,所以在嘗試catch.What你有時可能會看到沒有點是一樣的東西:

fn2() -> 
    {ok, Result} = fn1(), 
    %Do stuff with Result 
    ok. 

這樣做的目的是你如果fn1()沒有返回正常,則使fn2()拋出一個不匹配。你讓別人在「上方」處理問題。例如。這可能會扼殺你的過程,並讓你的主管創建一個新的。

+0

謝謝,這是振聾發聵! :) – ErJab 2009-12-02 10:51:05

4

你應該經常測量,找出這樣的事情。

你的代碼也不會做你認爲它的作用。

 
-module(glurk). 
-compile(export_all). 

fn2() -> 
    try 
     {ok, Result} = fn1(), 
     %Do something with Result 
     ok 
    catch 
     throw:Term -> Term; 
     exit:Reason -> {exit, Reason}; 
     error:Reason -> {error,{Reason,erlang:get_stacktrace()}} 
    end. 

fn1() -> 
    {error, a}. 

嘗試了這一點:

 
c(glurk). 
./glurk.erl:6: Warning: variable 'Result' is unused 
{ok,glurk} 
16> glurk:fn2(). 
{error,{{badmatch,{error,a}}, 
     [{glurk,fn2,0}, 
     {erl_eval,do_apply,5}, 
     {shell,exprs,6}, 
     {shell,eval_exprs,6}, 
     {shell,eval_loop,3}]}} 

這是因爲FN1沒有引發異常 它gebnerated正常retyurn值{錯誤,一個}這 不圖案對陣{OK,結果}

你的代碼的第一個版本的作品與或者返回正常值 或引發異常的函數 - 你必須把它寫這樣的:

 
fn1(....) -> 
    ... 
    %% success case 
    Val; 

    %% failure case 
    throw(...) | exit(...) | error(...) 

您不能只將相同的功能泵入fn1和fn2。

如果你有在那裏被調用函數必須從深度遞歸 逃脫那麼第一種方法會比第二個更有效的情況下 - 因爲你可以立即從深遞歸說擲( 退出... )。

所以答案取決於你打電話的功能的性質。

代碼應始終對美,而不是效率優化的 - 因爲你必須 保持的東西 - 那麼它應該只在極少數情況下 它不夠快優化。有什麼需要優化的,應認定 通過測量程序(你會一直在這裏感到驚訝:-)

我,我會寫

{ok,Result} = ... 

其實你的第一個代碼有一個更微妙的錯誤

 
fn2() -> 
    try 
     {ok, Result} = fn1(), 
     %Do something with Result 
     ok 
    catch 
     throw:Term -> Term; 
     exit:Reason -> {exit, Reason}; 
     error:Reason -> {error,{Reason,erlang:get_stacktrace()}} 
    end. 

想想這個。被捕獲的錯誤情況本身並不處理錯誤 他們只返回像{exit,Reason}或{error,Reason}這樣的元組,這意味着 下一層(即fn2的調用者)也將不得不圍繞檢查 錯誤返回 - 如果在所有級別重複該代碼將是一團糟。

「erlang」的方式是在程序的頂部有一個try-catch,並且在出現錯誤時突然終止 並退出(爲什麼)。

事實上,您甚至不應該這樣做 - 您應該將您的流程鏈接到另一個流程 ,那麼違規流程將會死亡,並且「其他流程將修復錯誤」。

該異常向上傳播調用堆棧,並且飛到處理的鏈接進程 。所以我們有兩種類型的進程 - 沒有內置錯誤處理的進程 以及只進行錯誤處理的進程。

+0

是的,總是基準。 – Christian 2009-12-02 17:44:24

9

你真的想嘗試避免像瘟疫一樣的嘗試/捕捉。它是在二郎山一個非常罕見的成語 - 真的只是一對夫婦的特殊情況下使用:

  • 你在哪裏檢查用戶提供 輸入,你有沒有保證 這將是「正確的」
  • 在這裏你有一些東西,是 深度嵌套和 展開它的錯誤條件 成本過於昂貴
    • 像mensia交易
    • 或語法/詞法分析器的

的try/catch如C語言是必不可少++其中應用程序中是否存在或錯誤不穩定,但Erlang是在這種情況下穩定 - 的過程中死機,但好好嘗試將系統下。

你應該編程快樂路徑,匹配返回值,如果應用程序對你的期望是什麼,然後偏離讓它崩潰。崩潰告訴你你有一個問題,並告訴你修復它。

try/catch的問題在於,它可以簡單地掩蓋問題,甚至更糟糕的是,將最終的崩潰從應該發生的位置(在您包裝的表達式內部)移開,並使其出現在別處 - 您的編程邏輯期望它已經suceeded =這使得調試更加困難。

編程快樂路徑,讓它崩潰是非常令人不安的你第一次做到這一點,那感覺就像不穿衣服出門,但實際上你習慣了它真正的快:)

2

在這種情況下, ,不管什麼效率更高,你都應該使用case替代方案,因爲它更加簡潔地描述了發生了什麼。您的fn1()此處返回一個值,指示是否有成功的值或錯誤。在case版本中,您直接與此匹配,而在try版本中,您匹配的成功值將在返回錯誤時生成錯誤。這是不必要的複雜和隱藏發生了什麼,所以這是不好的編程風格,應該避免。

正如戈登已經指出,有try會有捕到更多的錯誤,可能比你打算並可能因此掩蓋了其他真正錯誤,你應該看到的。

這裏case也會更快,但差別可能很小。清晰,簡潔和良好的編程風格更重要!

0

這種情況下總是會更有效率,但差異很小,更重要的是在您的特定情況下更有意義。特別是,哪種方法會產生更多可理解的代碼。看到這個基準:

%% Results: 
%% 7> errors:run_normal(100). 
%% {9,ok} 
%% 8> errors:run_normal(1000). 
%% {107,ok} 
%% 9> errors:run_normal(10000). 
%% {856,ok} 
%% 10> errors:run_normal(1000, 10). 
%% {263,ok} 
%% 11> errors:run_wcatch(10000). 
%% {2379,ok} 
%% 12> errors:run_wcatch(1000, 10). 
%% {401,ok} 
%% 18> errors:run_normal_cplx(10000, 50). 
%% {7910,ok} 
%% 19> errors:run_wcatch_cplx(10000, 50). 
%% {10222,ok} 
-module(errors). 

-compile(export_all). 

run_normal(Iterations) -> 
    get_result(Iterations, fun() -> normal() end). 

run_normal(Iterations, Level) -> 
    get_result(Iterations, fun() -> deepnormal(Level) end). 

run_wcatch(Iterations) -> 
    get_result(Iterations, fun() -> wcatch() end). 

run_wcatch(Iterations, Level) -> 
    get_result(Iterations, fun() -> deepwcatch(Level) end). 

run_normal_cplx(Iterations) -> 
    get_result(Iterations, fun() -> normal_complex() end). 

run_normal_cplx(Iterations, Level) -> 
    get_result(Iterations, fun() -> deepnormal_complex(Level) end). 

run_wcatch_cplx(Iterations) -> 
    get_result(Iterations, fun() -> wcatch_complex() end). 

run_wcatch_cplx(Iterations, Level) -> 
    get_result(Iterations, fun() -> deepwcatch_complex(Level) end). 

%%------------------------------------------------------------------------------ 

get_result(Iterations, Fun) -> 
    timer:tc(fun() -> run(Iterations, Fun) end). 

run(0, _Fun) -> 
    ok; 
run(Iterations, Fun) -> 
    Fun(), 
    run(Iterations - 1, Fun). 

%%------------------------------------------------------------------------------ 

normal() -> 
    case foo(atom) of 
     {ok, atom} -> ok; 
     {error, atom} -> ok 
    end. 

normal_complex() -> 
    case foo_cplx() of 
     {ok, Res} -> Res; 
     {error, Res} -> Res 
    end. 

deepnormal(Level) -> 
    case deepfoo(atom, Level) of 
     {ok, atom} -> ok; 
     {error, atom} -> ok 
    end. 

deepnormal_complex(Level) -> 
    case deepfoo_cplx(Level) of 
     {ok, Res} -> Res; 
     {error, Res} -> Res 
    end. 

wcatch() -> 
    try 
     {ok, atom} = foothrow(atom) 
    catch 
     throw:{error, atom} -> ok 
    end. 

wcatch_complex() -> 
    try 
     {ok, _Res} = foothrow_cplx() 
    catch 
     throw:{error, Res} -> Res 
    end. 

deepwcatch(Level) -> 
    try 
     {ok, atom} = deepfoothrow(atom, Level) 
    catch 
     throw:{error, atom} -> ok 
    end. 

deepwcatch_complex(Level) -> 
    try 
     {ok, _Res} = deepfoothrow_cplx(Level) 
    catch 
     throw:{error, Res} -> Res 
    end. 

%%------------------------------------------------------------------------------ 

foo(Arg) -> {error, Arg}. 

foothrow(Arg) -> throw({error, Arg}). 

deepfoo(Arg, 0) -> {error, Arg}; 
deepfoo(Arg, Level) -> deepfoo(Arg, Level - 1). 

deepfoothrow(Arg, 0) -> throw({error, Arg}); 
deepfoothrow(Arg, Level) -> deepfoothrow(Arg, Level - 1). 


foo_cplx() -> {error, {<<"Some">>, "Complex", data}}. 

foothrow_cplx() -> throw({error, {<<"Some">>, "Complex", data}}). 

deepfoo_cplx(0) -> {error, {<<"Some">>, "Complex", data}}; 
deepfoo_cplx(Level) -> deepfoo_cplx(Level - 1). 

deepfoothrow_cplx(0) -> throw({error, {<<"Some">>, "Complex", data}}); 
deepfoothrow_cplx(Level) -> deepfoothrow_cplx(Level - 1). 
相關問題