Erlang:在这种情况下,使用try-catch或case语句哪个更有效?

Erlang:在这种情况下,使用try-catch或case语句哪个更有效?,erlang,performance,Erlang,Performance,假设我在Erlang中有一个函数fn1(),如果函数执行成功,它返回{ok,Result},如果有错误,它返回{error,“ErrorReason”} 现在,在另一个函数fn2()中,我调用fn1(),我需要检查fn1的结果,并且仅当它是{ok,result}时才继续 我想,我可以使用任何一种情况或尝试捕获。但是效率是我最关心的问题,我想知道下面两种方法中哪一种更有效: 尝试捕获方法 fn2() -> try {ok, Result} = fn1(), %D

假设我在Erlang中有一个函数fn1(),如果函数执行成功,它返回
{ok,Result}
,如果有错误,它返回
{error,“ErrorReason”}

现在,在另一个函数fn2()中,我调用fn1(),我需要检查fn1的结果,并且仅当它是
{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.
fn2() ->
   Res = fn1(),
   case Res of
      {ok, Result} -> 
         %Do something with Result
         ok;
      {error, Reason} ->
         Reason
   end.
案例
方法

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

case方法将更有效,因为它只是模式匹配,不涉及构建调用堆栈和其他内容

在这两个示例中,您都将在本地处理“错误”,因此在try-catch中没有任何意义。有时您可能会看到如下情况:

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

这里的意图是,如果fn1()没有返回ok,则让fn2()抛出一个坏匹配。你让其他“上级”处理问题。例如,这可能会终止您的流程,并使您的主管创建一个新流程。

您应该始终进行测量,以找出类似的问题

您的代码也不做您认为它做的事情

-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}. 实际上,您的第一个代码有一个更微妙的错误

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. fn2()-> 尝试 {ok,Result}=fn1(), %做一些有结果的事情 好啊 抓住 抛出:术语->术语; 退出:原因->{exit,Reason}; 错误:Reason->{error,{Reason,erlang:get_stacktrace()} 结束。 想想这个。捕获的错误案例本身并不处理错误 它们只返回元组,如{exit,Reason}或{error,Reason}。这意味着 下一层(即fn2的调用方)也将不得不进行检查 返回错误-如果在所有级别重复此操作,代码将变得一团糟

“erlang”方法是在程序的顶部有一个try-catch,然后终止 如果发生错误,则突然退出(为什么)

事实上,您甚至不应该这样做-您应该将您的流程链接到另一个流程 然后,有问题的进程将死亡,“其他进程将修复错误”

异常会向上传播到调用堆栈并传递到链接的进程 治疗。因此,我们有两种类型的进程——没有内置错误处理的进程
以及只进行错误处理的进程。

您确实希望尝试避免像瘟疫一样的尝试/捕获。这在Erlang中是一个非常罕见的习惯用法-实际上只在几个特殊情况下使用:

  • 您正在检查用户提供的 你不能保证 它将是“正确的”
  • 在那里你有一些 深度嵌套和 在错误条件下展开它 太贵了
    • 像门西亚交易
    • 或者在解析器/词法分析器中

Test/catch在C++等语言中是必不可少的,在应用程序中存在或错误时不稳定,但在这种情况下Erlang稳定,进程崩溃但不会使系统下降。 您应该对快乐路径进行编程,匹配返回值,如果应用程序偏离预期,则让它崩溃。崩溃告诉您有问题,并告诉您修复它

try/catch的问题在于,它可以简单地掩盖问题,甚至更糟糕的是,将最终崩溃从应该发生的地方(在您包装的表达式中)移开,并使它出现在其他地方-您的编程逻辑期望它成功的地方=这使得调试更加困难


为快乐之路编程并让它崩溃在你第一次这么做时是非常令人不安的,这感觉就像是不穿衣服出门,但实际上你很快就习惯了:)

在这种情况下,不管什么更有效,您肯定应该使用
案例
选项,因为它更简洁地描述了正在发生的事情。您的
fn1()
在此返回一个值,指示是否存在成功值或错误。在
案例
版本中,您直接与此匹配,而在
尝试
版本中,您与成功值匹配,如果返回错误,成功值将生成错误。它是不必要的复杂,隐藏了正在发生的事情,因此它是一种糟糕的编程风格,应该避免

正如Gordon已经指出的那样,进行
try
会捕捉到比您可能想要的更多的错误,从而掩盖您应该看到的其他真实错误


案例
在这里也会更快,但差异可能很小。清晰、简洁和良好的编程风格更为重要

案例总是更有效,但差异很小,更重要的是,在您的特定案例中,什么更有意义。特别是,哪种方法产生的代码更容易理解。请参见此基准:

%% 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}
-模块(错误)。
-编译(全部导出)。
正常运行(迭代)->
获取结果(迭代,fun()->normal()结束)。
正常运行(迭代,级别)->
获取结果(迭代,fun()->deepnormal(Level)end)。
运行(迭代)->
获取结果(迭代,fun()->wcatch()结束)。
运行(迭代,级别)->
获取_结果(迭代,fun()->deepwcatch(级别)结束)。
运行\u正常\u cplx(迭代)->
获取结果(迭代,fun()->normal\u complex()结束)。
运行\u正常\u cplx(迭代,级别)->
获取结果(迭代,fun()->deepnormal_复杂(级别)结束)。
运行\u wcatch\u cplx(迭代)->
获取结果(迭代,fun()->wcatch_complex()结束)。
运行\u wcatch\u cplx(迭代,级别)->
获取结果(迭代,fun()->deepwcatch_复杂(级别)结束)。
%%------------------------------------------------------------------------------
获取结果(迭代、乐趣)->
计时器:tc(fun()->运行(迭代,fun)结束)。
跑步(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}