如何优雅地检查 Erlang 中的许多条件?

发布于 2024-07-15 04:49:05 字数 458 浏览 6 评论 0原文

因此,当用户发送注册帐户的请求时,他们会发送用户名、密码、电子邮件和其他信息。 注册功能必须验证其所有数据。 一个例子是:

  • 验证电子邮件未在使用中
  • 验证用户名未在使用中
  • 验证用户名是字母数字
  • 验证所有字段的长度均高于 X 个字符
  • 验证所有字段的长度均小于 Y 个字符

现在我不想有 5 级深度,如果或案例陈述,但我还有什么其他选择? 将其拆分为单独的函数听起来是个好主意,但随后我只需以某种条件检查函数的返回值,然后又回到了原来的问题。

我可以将它们分成函数,然后调用一个 if 语句,将所有条件或运算在一起,但这不会给我我想要的结果,因为我需要能够告诉用户具体的错误(如果有)。

在erlang中如何处理这种情况? 是否存在与 return 语句等效的语句,或者它是否必须是函数中的最后一个可执行行才能作为返回值?

So when a user sends a request to register an account, they send their username, password, email, and other info. The registration function must verify all of their data. An example would be:

  • verify email not in use
  • verify username not in use
  • verify username is alphanumeric
  • verify all fields are above X characters long
  • verify all fields are less than Y characters long

Now I don't want to have a 5 level deep if or case statement, but what other options do I have? Splitting it into separate functions sounds like a good idea, but then I just have to check the return value of the functions in some sort of conditional and it's back to the original problem.

I could separate them into functions and then call an if statement with all of the conditionals OR'd together, but that wouldn't give me what I want because I need to be able to tell the user the specific error if there was one.

How does one handle this kind of situation in erlang? Is there an equivalent of a return statement, or does it have to be the last executable line in a function to be a return value?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

站稳脚跟 2024-07-22 04:49:05

Joe Armstrong 的建议之一:将程序成功案例代码与错误处理分开。 您可以通过这种方式进行

create_user(Email, UserName, Password) ->
  try
    ok = new_email(Email),
    ok = valid_user_name(UserName),
    ok = new_user(UserName),
    ok = strong_password(Password),
    ...
    _create_user(Email, UserName, Password)
  catch
    error:{badmatch, email_in_use} -> do_something();
    error:{badmatch, invalid_user_name} -> do_something();
    error:{badmatch, user_exists} -> do_something();
    error:{badmatch, weak_password} -> do_something();
    ...
  end.

注意,您可以从 create_user 函数中捕获所有错误,这是更好的。

create_user(Email, UserName, Password) ->
    ok = new_email(Email),
    ok = valid_user_name(UserName),
    ok = new_user(UserName),
    ok = strong_password(Password),
    ...
    _create_user(Email, UserName, Password).

main() ->
  try
    ...
    some_function_where_create_user_is_called(),
    ...
  catch
    ...
    error:{badmatch, email_in_use} -> do_something();
    error:{badmatch, invalid_user_name} -> do_something();
    error:{badmatch, user_exists} -> do_something();
    error:{badmatch, weak_password} -> do_something();
    ...
  end.

模式匹配是 Erlang 中最酷的事情之一。 请注意,您也可以将标记包含到错误匹配错误

{my_tag, ok} = {my_tag, my_call(X)}

和自定义数据

{my_tag, ok, X} = {my_tag, my_call(X), X}

中,如果异常对您来说足够快,则取决于您的期望。 我的 2.2GHz Core2 Duo Intel 上的速度:
一秒内大约有 200 万个异常 (0.47us),而 600 万个成功(外部)函数调用 (0.146us) 则可以猜测异常处理大约需要 0.32us。
在本机代码中,速度为每秒 6.8 vs 4700 万次,处理时间约为 0.125us。 try-catch 构造可能会产生一些额外的成本,在本机代码和字节码中,成功调用函数大约需要 5-10% 的成本。

One of Joe Armstrong's suggestion: program success case code separated from error handling. You can make it in this way

create_user(Email, UserName, Password) ->
  try
    ok = new_email(Email),
    ok = valid_user_name(UserName),
    ok = new_user(UserName),
    ok = strong_password(Password),
    ...
    _create_user(Email, UserName, Password)
  catch
    error:{badmatch, email_in_use} -> do_something();
    error:{badmatch, invalid_user_name} -> do_something();
    error:{badmatch, user_exists} -> do_something();
    error:{badmatch, weak_password} -> do_something();
    ...
  end.

note that you can do all errors catches out of create_user function which is better.

create_user(Email, UserName, Password) ->
    ok = new_email(Email),
    ok = valid_user_name(UserName),
    ok = new_user(UserName),
    ok = strong_password(Password),
    ...
    _create_user(Email, UserName, Password).

main() ->
  try
    ...
    some_function_where_create_user_is_called(),
    ...
  catch
    ...
    error:{badmatch, email_in_use} -> do_something();
    error:{badmatch, invalid_user_name} -> do_something();
    error:{badmatch, user_exists} -> do_something();
    error:{badmatch, weak_password} -> do_something();
    ...
  end.

Pattern match is one of coolest things in Erlang. Note that you can involve your tag to badmatch error

{my_tag, ok} = {my_tag, my_call(X)}

and custom data too

{my_tag, ok, X} = {my_tag, my_call(X), X}

If exception is fast enough for you depends of your expectations. Speed on my 2.2GHz Core2 Duo Intel:
about 2 millions exceptions in one second (0.47us) compared to 6 millions success (external) function calls (0.146us) - one can guess that exception handling takes about 0.32us.
In native code it is 6.8 vs 47 millions per second and handling can take about 0.125us. There can be some additional cost for try-catch construct which is about 5-10% to success function call in both native and byte-code.

时光礼记 2024-07-22 04:49:05
User = get_user(),

Check_email=fun(User) -> not is_valid_email(User#user.email) end,
Check_username=fun(User) -> is_invalid_username(User#user.name) end,

case lists:any(fun(Checking_function) -> Checking_function(User) end, 
[Check_email, Check_username, ... ]) of
 true -> % we have problem in some field
   do_panic();
 false -> % every check was fine
   do_action()
 end

所以它不再是 5 层深了。 对于真实的程序,我想你应该使用lists:foldl来累积来自每个检查函数的错误消息。 因为现在它简单地说“一切都好”或“有些问题”。

请注意,通过这种方式添加或删除检查条件并不是什么大问题

,并且对于“是否存在与 return 语句等效的内容...” - 看看 try catch throw 语句,在这种情况下, throw 的作用类似于 return 。

User = get_user(),

Check_email=fun(User) -> not is_valid_email(User#user.email) end,
Check_username=fun(User) -> is_invalid_username(User#user.name) end,

case lists:any(fun(Checking_function) -> Checking_function(User) end, 
[Check_email, Check_username, ... ]) of
 true -> % we have problem in some field
   do_panic();
 false -> % every check was fine
   do_action()
 end

So it isn't 5 level deep any more. For real program i guess you should use lists:foldl for accumulate error message from every checking function. Because for now it simple says 'all fine' or 'some problem'.

Note that in this way add or remove checking condition isn't a big deal

And for "Is there an equivalent of a return statement..." - look at try catch throw statement, throw acts like return in this case.

兰花执着 2024-07-22 04:49:05

在 @JLarky 的回答的基础上,这是我想出的一些东西。 它还从 Haskell 的 monad 中汲取了一些灵感。

-record(user,
    {name :: binary(), 
     email :: binary(), 
     password :: binary()}
).
-type user() :: #user{}.
-type bind_res() :: {ok, term()} | {error, term()} | term().
-type bind_fun() :: fun((term()) -> bind_res()).


-spec validate(term(), [bind_fun()]) -> bind_res().
validate(Init, Functions) ->
    lists:foldl(fun '|>'/2, Init, Functions).

-spec '|>'(bind_fun(), bind_res())-> bind_res().
'|>'(F, {ok, Result}) -> F(Result);
'|>'(F, {error, What} = Error) -> Error;
'|>'(F, Result) -> F(Result).

-spec validate_email(user()) -> {ok, user()} | {error, term()}. 
validate_email(#user{email = Email}) ->
...
-spec validate_username(user()) -> {ok, user()} | {error, term()}.
validate_username(#user{name = Name}) ->
...
-spec validate_password(user()) -> {ok, user()} | {error, term()}.    
validate_password(#user{password = Password}) ->
...

validate(#user{...}, [
    fun validate_email/1,
    fun validate_username/1,
    fun validate_password/1
]).

Building up on @JLarky's answer, here's something that i came up with. It also draws some inspiration from Haskell's monads.

-record(user,
    {name :: binary(), 
     email :: binary(), 
     password :: binary()}
).
-type user() :: #user{}.
-type bind_res() :: {ok, term()} | {error, term()} | term().
-type bind_fun() :: fun((term()) -> bind_res()).


-spec validate(term(), [bind_fun()]) -> bind_res().
validate(Init, Functions) ->
    lists:foldl(fun '|>'/2, Init, Functions).

-spec '|>'(bind_fun(), bind_res())-> bind_res().
'|>'(F, {ok, Result}) -> F(Result);
'|>'(F, {error, What} = Error) -> Error;
'|>'(F, Result) -> F(Result).

-spec validate_email(user()) -> {ok, user()} | {error, term()}. 
validate_email(#user{email = Email}) ->
...
-spec validate_username(user()) -> {ok, user()} | {error, term()}.
validate_username(#user{name = Name}) ->
...
-spec validate_password(user()) -> {ok, user()} | {error, term()}.    
validate_password(#user{password = Password}) ->
...

validate(#user{...}, [
    fun validate_email/1,
    fun validate_username/1,
    fun validate_password/1
]).
余罪 2024-07-22 04:49:05

也许你需要使用 of

receive
    message1 -> code1;
    message2 -> code2;
    ...
end.

但是,当然会有spawn() 方法。

Maybe you will need using of

receive
    message1 -> code1;
    message2 -> code2;
    ...
end.

But, of course, there will be spawn() methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文