2009-12-02 2 views
2

함수가 성공적으로 실행되면 {ok, Result}을 반환하고 오류가있는 경우 {error, "ErrorReason"}을 반환하는 Erlang 함수 fn1()이 있다고 가정 해보십시오.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

case 메소드는 패턴 일치 만 수행하고 b를 포함하지 않으므로보다 효율적입니다. 호출 스택을 채워 넣는 것. 의도는 것입니다 여기에

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

: 당신은 당신이 가끔 볼 수 있습니다 catch.What은 try 아무 문제가 없다, 그래서 로컬 "오류"를 처리하려고 두 예에서

뭔가처럼 fn1()이 ok를 반환하지 않으면 fn2()가 badmatch를 throw합니다. 당신은 다른 사람이 문제를 다루는 "위"에있게합니다. 예 : 그러면 프로세스가 중단되고 관리자가 새 프로세스를 만들 수 있습니다.

+0

감사합니다, 즉 계몽이었다! :) – ErJab

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}. 

이 밖으로 시도 :을 Fn1 예외 를 제기하지 않았기 때문에

 
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}]}} 

이 그것을 gebnerated되는 일반 retyurn 값 {오류, A}에 대해 패턴 일치를하지 않는 {확인, 결과 }

코드의 첫 번째 버전 중 하나를 정상적인 값 을 반환하거나 예외를 발생시키는 기능을 작동 -이처럼 작성해야 :

 
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를 두어 오류가 발생하면 갑자기 exit (이유)를 사용하여 종료합니다.

사실 종종이 작업을 수행해서는 안됩니다. 프로세스를 다른 프로세스 에 연결해야합니다. 그러면 문제가되는 프로세스가 종료되고 "다른 프로세스가 오류를 수정합니다".

예외는 호출 스택을 전파하고 처리를 위해 연결된 프로세스 으로 이동합니다. 따라서 두 가지 유형의 프로세스가 있습니다. 즉, 내장 된 오류 처리가없는 프로세스와 오류 처리 만 수행하는 프로세스가 있습니다.

+0

예, 항상 벤치 마크. – Christian

9

당신은 실제로 전염병처럼 try/catch하지 않으려 고 노력하고 싶습니다. 그것은 얼랑에서 매우 드문 관용구이다 - 정말 특별한 경우에만 몇에서 사용 :

  • 는 사용자가 제공 한 입력을 확인하고 당신은 그것은 '올바른'이 될 것이라는 보장이 없다
  • 당신이 뭔가를 어디 중첩 및 오류 상태 에 풀어서 의 비용은 mensia 거래
  • 또는 같은
    • 너무 비싸 파서/렉서의

시도/캐치 C와 같은 언어에 필수적인 ++ 응용 프로그램이 존재 또는 오류가 불안정하지만, 얼랑 그 상황에서 안정적이다 - 프로세스 충돌하지만 시스템을 가지고 넣은 사람은 아니다 하위.

당신은 행복한 경로 일치 반환 값을 프로그램해야하며, 응용 프로그램은 당신이 다음 무엇을 기대에서 벗어나는 경우 충돌 할 수 있습니다. 크래시는 문제가 있음을 알려주고 문제를 해결하라고 알려줍니다.

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). 
관련 문제