erlang妙用try..catch写出优雅的代码

版权声明:本文为Sunface(孙飞)原创文章,转载请注明出处 https://blog.csdn.net/abv123456789/article/details/30748305


1. catch 的用法非常简单:

复制代码
catch case do_check(Test) of
       {ok,Result} -> do_thing(Result);
       {error,ErrReason} -> do_error(ErrReason)
end.

do_check(Test) ->
   case Test of
        1 -> erlang:throw({error,to_small});
        2 –> {ok,2};
        9 -> erlang:throw({error,too_big})
   end,
复制代码
以上用法的优点是:非常简单&&do_check/1里面如果有nest_case也可以处理掉,
缺点也非常明显:
1.1 如果Test是除1,2,9以外的值时会发生case_clause,catch也不能匹配;
1.2 do_check/1要在外层用Catch[这个check要由调用者来确定用法],这就对调用者有更高的要求。
2. try catch用法:
复制代码
%% 首先对FuncOrExpressionSequence求值
%% 如果没有产生异常则顺序进行Patterm匹配, 匹配成功后执行后面的表达式
%% 如果有异常抛出, 则顺序匹配ExPattern(ExceptionType是throw、exit、error中的一个, 默认为throw)
%% after块中的代码用于清理工作,绝对会执行
try FuncOrExpressionSequence of
    Pattern1 [when Guard1] ->Expressions1;
    Pattern2 [when Guard2] ->Expressions2;
    ...
catch
    ExceptionType: ExPattern1 [when ExGuard1] ->ExExpressions1;
    ExceptionType: ExPattern2 [when ExGuard2] ->ExExpressions2;
    ...
after
    AfterExpressions
end.
复制代码
也可以把after后面的去掉:
复制代码
%% 将三种异常方式依次捕获
try F
catch
    throw: X ->ExExpressions1;
    exit: X  ->ExExpressions2;
    error: X ->ExExpressions3
end.
复制代码

那么根据这用法:可以把Catch的用法改写会:

复制代码
case do_check(Test) of
       {ok,Result} -> do_thing(Result);
       {_,ErrReason} -> do_error(ErrReason)
end.

do_check(Test) ->
 try 
      case Test of
          1 -> erlang:throw({error,to_small});
          2 –> {ok,2};
          9 -> erlang:throw({error,too_big})
     end
catch 
     throw:X -> {throw,X};
     exit:X -> {exit,X};
    error:X -> {error,X}
end.
复制代码

从上面可以看出:所有的异常都被正确的处理了且do_check/2对调用者也是透明的!真是太棒了!

在stack overflow上这个答案才能是最佳实践:

how to write_elegantly_check_function

从上面测试结果来看,try catch确定会消耗一点性能,所以如果条件不复杂且频繁调用的check,就不要用try catch了:)

2014年5月22日 12:10:16 补充:

1.不用try cathc怎么写一个复杂的Case :

复制代码
case {check_name(Name),check_passwd(Passwd),check_url(Url) } of
    {true ,true ,true} ->
                     do_next_work();
    {false,_,_} ->
                     check_error(name);
    {_,false,_} ->
                   check_error(passwd);
    {_,_,false } ->
                  check_error(url)
      end.
复制代码

2. 有时会在一个重要的进程 时最底层做了try catch ,以防止carsh ,那我们在写上层逻辑时,也会做复杂的判定,又加了一个try catch,

例如:

复制代码
%%在try catch中再套try catch.
init_data(Data) ->
    try
        Data1 = init_data2(Data),
        after_init(Data1)
    catch _:_ -> error
    end.
init_data2(Data) ->
    try
        check_init_data(Data)
    catch _:_ -> init_erro
    end.
复制代码

  值得优化:只在最底层用try catch把所有的错误都放到这里统一处理。

展开阅读全文

没有更多推荐了,返回首页