【问题标题】:Erlang. Records. Existing of field二郎。记录。现有领域
【发布时间】:2015-01-31 02:45:28
【问题描述】:

当记录具有特定字段时,我需要返回 true 的函数,反之亦然。 示例:

-record(robot, {name,
            type=industrial,
            hobbies,
            details=[]
            }).

Crusher = #robot{name="Crusher", hobbies=["Crushing people","petting cats"]}.

SomeFunction(Crusher, hobbies). %% returns true
SomeFunction(Crusher, fantasy). %% returns false

对不起我的英语。 这是我大学的函数式编程练习。它是为 Lisp 编写的,这里有简单的解决方案。但对我来说,这是 Erlang 的要求。非常感谢您的帮助。我写了一些丑陋的功能。这不是我第一个问题的解决方案。我的老师就够了。

searchArray(_, []) -> false;
searchArray(X, [X|_]) -> true;
searchArray(X, [_|T]) -> searchArray(X, T).

existField(Field) ->
searchArray(Field, record_info(fields, robot)).

我也认为,地图在这里更有用。

【问题讨论】:

  • 记录就像一个宏,它们被转换为元组,因此您无法在运行时检查字段的存在(但在编译时您可以通过将记录视为自己的类型来检查某些内容)。记录不是 Perl/Python/Ruby 中的“哈希”或“字典”。相反,您可以使用 {Key,Value} 类型并检查特定键。但我认为这是一个 X-Y 问题。你想在程序中做什么?可能有更好的方法来处理您的一般问题,从程序架构中的更高级别开始,而不是让您了解这种特定的技术细节。

标签: erlang field exists records


【解决方案1】:

您可以使用record_info(fields, Record) 获取记录字段列表。 你需要知道记录,这个函数有点棘手。在编译过程中将记录转换为固定大小的元组,并添加伪函数record_info。

#robot{name="Crusher", hobbies=["Crushing people","petting cats"]}

变成:

{robot, "Crusher", industrial, ["Crushing people","petting cats"], []}

如果你想共享记录,你应该把它放在 .hrl 文件中,并将它包含在你希望能够使用“#record_name{}”构建它的每个模块中,使用点运算符按字段提取元素或使用 record_info。

自第 17 版以来,我们拥有 maps,它应该也适用于您并且不会损害您的大脑。

【讨论】:

    【解决方案2】:

    记录不是erlang中的特殊类型,而是一个简单的元组,第一项是元组的名称。一切都在编译时解决,因此在运行时测试字段的存在是没有意义的,因为它不应该通过编译:我不知道在不显式使用字段名称的情况下访问记录字段的任何方法。

    正如 Lukasz 所说,您可以使用 map,R17 附带的新 erlang 类型。

    如果你真的想做测试,lists:member(Field,record_info(fields,Record)) 就可以了。

    【讨论】:

      【解决方案3】:

      Erlang 记录揭秘

      您应该明白,erlang 中的记录只是语法糖,最终将成为一个常规元组。

      我确定您已经完成了作业并查看了the official reference here

      让我们详细说明您在那里找到的内容,并以您的 robot 记录为例。如果您在模块中的某处添加同伴代码

      io:format("My happy robot is ~p", #robot{name="foo", type=some_atom}).
      

      希望您会看到与此类似的内容

      My happy robot is {robot, "foo", some_atom, undefined, []}
      

      请注意,记录是大小为NumberOfField + 1 的元组。以记录名称作为第一个元素(守卫)。您还会注意到没有对您声明的字段的引用。这是因为记录符号只是一种通过名称而不是位置访问元组元素的方法。

      好的,你已经打开了一个erlang shell,尝试了命令,但它没有工作,这很正常。外壳只是不知道记录(较新的版本可以,但有一些魔力)。如果您在模块中尝试该命令,它将正常工作。你应该明白,一旦你的模块编译完成,其中定义的所有记录都会被扩展。生成的编译模块不再引用记录,既没有属性也没有代码。

      Robot = #robot{name="foo", type=some_atom},
      Robot#robot.type
      %% = "foo"
      %% Internally, sometime during the parsing, the function
      %% erl_expand_records:module/2 will be called and transform the code to
      %% something equivalent to this:
      Robot = {robot, "foo", some_type, undefined, []},
      element(3, Robot).
      

      当然,现实中发生的事情要复杂一些,因为erl_expand_records:module/2 适用于 AbstractForms,但你明白了。

      现在回答你的问题。您有几种不同的选择,您应该选择最适合您需求的一种:

      方案一:掌握默认值

      当您定义记录字段时,您可以给它们一个可选的默认值。如果您不指定该值,erlang 将使用undefined

      例子:

      -record(record_1, {foo=bar,baz}).
      io:format("~p", #record_1{}).
      %% Will print
      {record_1, bar, undefined}
      

      注意第二个字段如何设置为undefined。从这里开始,解决您的问题非常简单

      is_defined(undefined)->false;
      is_defined(_)->true.
      
      %% And call it like this
      is_defined(Crusher#robot.hobbies).
      

      好的,好的。它看起来与您要求的完全不同。事实上,您可以完全跳过该函数,直接在函数和 case 子句匹配模式中使用记录符号。

      选项 2:使用宏进行黑客攻击

      您还可以定义一个魔术宏来实现选项 1 中定义的想法,如下所示:

      -define(IS_DEFINED(rec, inst, field), (undefined==inst#rec.field)).
      %% and then use it like this
      ?IS_DEFINED(robot, Crusher, hobbies)
      

      也不完全是一个函数,但它看起来像一个。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-13
        相关资源
        最近更新 更多