Erlang:在这种情况下,使用 try catch 或 case 语句哪个更有效?
假设我在 Erlang 中有一些函数 fn1() ,如果函数执行成功,则返回 {ok, Result}
;如果出现错误,则返回 {error, "ErrorReason"}
。
现在,在另一个函数 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.
案例
方法
fn2() ->
Res = fn1(),
case Res of
{ok, Result} ->
%Do something with Result
ok;
{error, Reason} ->
Reason
end.
Say I have some function fn1() in Erlang which returns {ok, Result}
if the function was executed successfully and {error, "ErrorReason"}
if there was an error.
Now in another function fn2() I call fn1() and I need to check the result of fn1 and proceed only if it is {ok, Result}
.
I figured, I can do this using either case or try catch. But Efficiency is my main concern and I'd like to know which of the two methods below is more efficient:
try-catch
Method
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
Method
fn2() ->
Res = fn1(),
case Res of
{ok, Result} ->
%Do something with Result
ok;
{error, Reason} ->
Reason
end.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你真的想像瘟疫一样尝试和避免 try/catch。这是 Erlang 中非常不常见的习惯用法 - 实际上只在几个特殊情况下使用:
输入并且您不能保证
它就会是“正确的”
深度嵌套和成本
在错误情况下展开它
太贵了
Try/catch 在像 C++ 这样的语言中是必不可少的,其中应用程序在存在或错误的情况下不稳定,但是 Erlang 在这些情况下是稳定的 - 进程崩溃但不会使系统崩溃向下。
您应该对快乐路径进行编程,匹配返回值,如果应用程序偏离您的预期,则让它崩溃。崩溃告诉您遇到了问题并告诉您修复它。
try/catch 的问题在于它可以简单地掩盖问题,或者更糟糕的是,将最终的崩溃移离它应该发生的地方(在您所包装的表达式内)并使其出现在其他地方 - 您的编程逻辑期望它发生的地方已经成功=这使得调试变得更加困难。
第一次编写快乐路径并让它崩溃是非常令人不安的,感觉就像光着身子出去一样,但实际上你很快就习惯了:)
You really want to try and avoid try/catch like the plague. It is a very uncommon idiom in Erlang - really only used in a couple of special cases:
input and you have no guarantees that
it will be 'correct'
deeply nested and the cost of
unrolling it on an error condition
is too expensive
Try/catch is essential in languages like C++ where the application is unstable in the presence or errors, but Erlang is stable in those circumstances - the process crashes but doens't bring the system down.
You should programme the happy path, match return values and if the application deviates from what you expect then let it crash. The crash tells you you have a problem and tells you to fix it.
The problem with try/catch is that it can simply mask the problem, or even worse, move the eventual crash away from where it should happen (inside the expression you have wrapped) and make it appear elsewhere - where your programming logic expects it to have suceeded = which makes debugging much harder.
Programming the happy path and letting it crash is very disconcerting the first time you do it, it feels like going out with no clothes on, but actually you get used to it real quick :)
case 方法会更有效,因为它只是模式匹配,并且不涉及构建调用堆栈和其他内容。
在这两个示例中,您都将在本地处理“错误”,因此 try catch 没有任何意义。您有时可能会看到类似以下内容:
这里的意图是让 fn2() 抛出错误匹配,如果 fn1( )没有返回确定。你让“上面”的其他人来处理这个问题。例如,这可能会终止您的进程,并让您的主管创建一个新进程。
The case method will be more efficient, as it simply pattern matches, and does not involve building a call stack and stuff.
In both examples you are about to handle the "error" locally, so there is no point in the try catch.What you might see sometimes is something like:
Here the intention is that you make fn2() throw a badmatch, if fn1() did not return ok. You let someone else "above" handle the problem. E.g. this might kill your process, and make your supervisor create a new one.
您应该始终进行测量以找出此类问题。
您的代码也没有按照您的想法进行操作。
试试这个:
这是因为 fn1 没有引发异常
它生成了一个正常的返回值 {error, a}
与 {ok, Result} 模式不匹配
您的代码的第一个版本使用返回正常值的函数
或者引发异常 - 你必须这样写:
你不能只将相同的函数注入 fn1 和 fn2 中。
如果遇到被调用函数必须逃离深度递归的情况
那么第一种方法会比第二种方法更有效 - 因为你可以
通过调用 throw(...) 立即退出深度递归。
因此,答案取决于您所调用的函数的性质。
代码应该始终针对美观而非效率进行优化 - 因为您已经
维护这些东西 - 那么它应该只在极少数情况下进行优化
速度不够快的地方。应该确定需要优化的内容
通过测量程序(你总是会在这里感到惊讶:-)
我,我会写
实际上你的第一个代码有一个更微妙的错误
想想这个。捕获的错误情况本身并不处理错误
他们只是返回像 {exit, Reason} 或 {error, Reason} 这样的元组,这意味着
下一层(即 fn2 的调用者)也必须搞乱检查
错误返回 - 如果在所有级别上重复此操作,代码将变得一团糟。
“erlang”方式是在程序顶部有一个 try-catch,然后终止
如果发生错误,突然退出(为什么)。
事实上,通常您甚至不应该这样做 - 您应该将您的流程链接到另一个流程
那么有问题的进程将会死亡,并且“其他进程将修复错误”。
异常向上传播调用堆栈并飞到链接的进程
进行治疗。所以我们有两种类型的进程 - 没有内置错误处理的进程
以及只进行错误处理的进程。
You should always measure to find things like this out.
Your code also does not do what you think it does.
Try this out:
This is because fn1 did not raise an exception
it gebnerated a normal retyurn value {error, a} which
does not pattern match against {ok, Result}
The first version of your code works with a function that either returns a normal value
or raises an exception - you have to write it like this:
You can't just pump the same function into fn1 and fn2.
If you had the case where the called function had to escape from a deep recursion
then the first method would be more efficient than the second - since you could
immediately exit from a deep recursion by saying throw(...).
So the answer depends upon the nature of the function that you are calling.
Code should always be optimised for beauty and not efficiency - since you have
to maintain the stuff - then it should only be optimised in the rare cases
where it is not fast enough. What needs to be optimised should be identified
by measuring the program (you will always be surprised here :-)
Me, I'd write
Actually your first code has a more subtle error
Think about this. The caught error cases do not themselves handle the error
they just return tuples like {exit, Reason} or {error, Reason} this means that the
next layer up (ie the caller of fn2) will also have to mess around checking
error returns - if this is repeated at all levels the code will be a mess.
The "erlang" way is to have one try-catch at the top of the program and just terminate
abruptly with exit(Why) if an error occurs.
In fact often you should not even do this - you should link your process to another process
then the offending process will die and "the other processes will fix the error".
The exception propagates up the call stack and flies over to the linked processes
for treatment. So we have two types of processes - ones that have no inbuilt error handling
and processes that only do error handling.
在这种情况下,无论哪种方法更有效,您绝对应该使用
case
替代方案,因为它更简洁地描述了正在发生的情况。您的fn1()
此处返回一个值,指示是否存在成功值或错误。在case
版本中,您直接与此匹配,而在try
版本中,您与 success 值匹配,如果返回错误,则会生成错误。它不必要地令人费解,并且隐藏了正在发生的事情,因此这是一种糟糕的编程风格,应该避免。正如 Gordon 已经指出的那样,尝试一下会捕获比您预期更多的错误,并且可能会掩盖您应该看到的其他真实错误。
这里的
case
也会更快,但差异可能很小。清晰、简洁和良好的编程风格更为重要!In this case, irrespective of what is more efficient, you should definitely use the
case
alternative as it more succinctly describes what is going on. Yourfn1()
here return a value indicating if there is a successful value or an error. In thecase
version you directly match against this, while in thetry
version you match against the success value which will generate an error if an error was returned. It is unnecessarily convoluted and hides what is going on so it is bad programming style and should be avoided.And as Gordon has already pointed out having a
try
there will catch more errors than you probably intend and may so mask other real errors which you should see.The
case
will also be faster here, but the difference is probably small. The clarity, succinctness and good programming style is much more important!案例总是会更有效,但差异很小,更重要的是在您的特定案例中更有意义。特别是,哪种方法可以生成更易于理解的代码。请参阅此基准:
The case will always be more efficient, but the difference is small and it's more important what makes more sense in your particular case. Especially, which approach produces more understandable code. See this benchmark: