将布尔值分配给 Erlang 中的原子或变量

Assign boolean to a atom or variable in Erlang

我刚刚学会了编程并且对 Java 感觉很舒服......到目前为止。但现在我在学校有一项作业,我们使用 Erlang。我有一些问题。

所以,我有 3 个简单的(我认为)问题:

  1. 我想像您在 Java 中那样将布尔值分配给变量(或原子?)。这可能吗?

  2. 在我下面的代码中,checkTheExistance 函数 returns 一个元组 {State, boolean}。接下来我将把它分配给另一个元组:{NextState, DoExist} 这对吗?我可以把布尔值放在 DoExist 中吗?

  3. 然后,我想检查 case 语句中的布尔值。并基于布尔值做一些事情。

希望你能理解我的问题。谢谢你。

handle(State, {join, Pid, Channel}) ->

      {NextState, DoExist} = checkTheExistance(Channel, State),
      case {NextState, DoExist} of 
         {_,false} -> startChannel(Channel),
         {_,true} ->  genserver:request(Channel, {join, Pid}) 
      end
     {reply, ok, NextState};
  • 你可以给一个变量赋一个布尔值,但是你不能"assign"一个原子,一个原子已经是一个原子类型的值。顺便说一下,Erlang 没有布尔类型,true 和 false 是用于此目的的 2 个普通原子。

  • {State, Boolean} 赋值给 {NextState, DoExist} 是合法的。这是模式匹配的一个很好的例子。这样做你验证答案是一个 2 项元组,并且你分配变量 NexState 和 DoExist。

  • case语句也是合法的,但是由于你忽略了case中的NexState这个词,你可以这样写:

    case DoExist of 
       false -> startChannel(Channel),
       true ->  genserver:request(Channel, {join, Pid}) 
    end
    

您的问题和摘要同时涉及几个方面。下面的解释很曲折,最终在最后得到答案,到时候你可能已经知道答案了......

第一:风格

我们在 Erlang 中不使用驼峰命名法,因为大写和小写名称之间存在具体的语义区别。所以你的 Java 函数命名习惯 someFunctionName 变成了 some_function_name.

二:状态改变

通常我们不会在检查期间更改任何状态。支票就是支票,状态改变就是状态改变。两者不应混用。这就是为什么我们将单一赋值作为语言规则的部分原因——这样当您在一个范围内看到一个变量名时,您是在查看标签而不是指向存储位置的指针 .在单个作用域内(一般来说,在函数定义、lambda 或列表推导中——不能屏蔽外部作用域标签)标签的含义与您所说的在整个函数中的作用完全相同,没有什么不同。

如果你想检查一些东西,就检查一些东西,并为检查函数提供它完成其工作所需的确切内容,(通常)仅此而已:

case check_existence(Channel, State) of
    exists         -> something(State);
    does_not_exist -> something_else(State)
end,

或者,比较差异:

case check_existence(Channel, State) of
    true  -> something(State);
    false -> something_else(State)
end,

这里发生了什么?我们 return 编辑了 原子 。这有点像 return 字符串,但 效率更高 并且在类型规范方面完全明确。实际上,原子是它们自己的类型,它们的意思完全是它们所说的,在程序中仅此而已。

所以 Erlang 中的布尔值是 truefalse -- 它们是原子。它们的意思恰好是 "true" 和 "false",就像 has_a_fuzzy_nose 恰好意味着 "has a fuzzy nose" 如果这在程序中有意义的话。有某些函数接受原子 "true" 和 "false",布尔运算符当然也接受这些值,但是没有什么使它们与 任何其他原子 在语言中。在 Erlang 中,您可以创建更多的复数逻辑布尔值(有时这非常有用),例如 maybehaslacksincompletenextend等等。

第三:=赋值断言

Erlang 程序的每一行通常都是对值的赋值或断言(匹配)。这里的重点是始终知道您正在处理好的数据,如果不是,则崩溃。一个很好的例子是 wrapped values,您会在各处看到它。典型的例子是任何有副作用的函数,通常 return {ok, Value}{error, Reason}.

在代码中处理此问题的典型方法是执行以下操作:

{ok, Data} = file:read_file(FileName),

这会在元组形状 returned 上强制断言匹配,然后在原子ok,然后将读取文件的内容赋值给变量名(标签)Data——假设Data在当前执行范围内还没有赋值。在这种情况下,如果 file:read_file/1 已 returned {error, Reason} 进程将立即崩溃。

为什么?

因为在标准情况下,我们完全无法控制为什么 外部资源(如文件)可能不可用——这实际上需要数千行代码正确处理错误。因此,我们只是在那里崩溃,知道我们没有继续处理错误数据。

关于那些作业...

我们刚刚看到了赋值,同时也看到了断言。完全可以像这样做更复杂的事情:

Value = file:read_file(FileName),
case Value of
    {ok, Data}      -> do_stuff(Data);
    {error, Reason} -> report_error_and_die(Reason)
end.

这会以更大的复杂性成本实现基本相同的效果。将此(无用的复杂)版本与以下版本(语义相同但更无用的复杂)进行比较:

{Outcome, Value} = file:read_file(FileName),
case Outcome of
    ok    -> do_stuff(Value);
    error -> report_error_and_die(Value)
end.

现在,记住布尔值 truefalse 只是原子,将 lists:member/2 的布尔值 return 分配给变量是完全可以接受的像这样命名 Result:

MyList = [1,2,3,4],
Result = lists:member(2, MyList),
case Result of
    true  -> io:format("In the list.~n");
    false -> io:format("Not in the list.~n")
end.

但做起来更简洁:

MyList = [1,2,3,4],
case lists:member(2, MyList) of
    true  -> io:format("In the list.~n");
    false -> io:format("Not in the list.~n")
end.

因为我们实际上不需要记住任何地方的名字。 There is, of course, a strong balance between use of labels as implicit documentation (good variable names convey your intent to the next reader of your code) and writing overly verbose code.

收盘中...

希望这能解释多于混淆。您现在要做的最重要的事情可能是在 vim 和 erl shell 中游手好闲,看看什么有意义,什么没有。 LYSE 是一本非常好的介绍性指南和参考资料——我强烈建议您通读前几章,以便快速掌握该语言的顺序部分。别害怕,作为一种语言,Erlang 实际上 非常简单 、小巧且可读性强。 (不过,人们使用 Erlang 解决的大规模并发问题本质上是非常庞大的,但这与顺序语言本身无关。)