【问题标题】:compilation error: record replacement of macro in Erlang编译错误:记录替换 Erlang 中的宏
【发布时间】:2018-02-07 14:35:05
【问题描述】:

这是目录结构。

src/
动物.hrl
people.hrl
data_animal.erl
data_people.erl
test.erl
test_macro.erl

动物.hrl

%% The record definition of animal.

-ifndef(ANIMAL).
-define(ANIMAL,true).

-record(animal,{
  id,
  animal_name,
  age
}).

-endif.

people.hrl

%% The record definition of people.

-ifndef(PEOPLE).
-define(PEOPLE,true).

-record(people,{
  id,
  people_name,
  age
}).

-endif.

data_animal.erl

%% The data file of animal.

-module(data_animal).
-include("animal.hrl").

%% API
-export([get/1,get_ids/0]).

get(1)->
  #animal{
    id=1,
    animal_name="cat",
    age=23
  };
get(2)->
  #animal{
    id=2,
    animal_name="dog",
    age=19
  };
get(3)->
  #animal{
    id=3,
    animal_name="tiger",
    age=23
  };
get(4)->
  #animal{
    id=4,
    animal_name="pig",
    age=19
  };
get(_)->
  undefined.

get_ids()->
  [1,2,3,4].

data_people.erl

%% The data file of people.

-module(data_people).
-include("people.hrl").

%% API
-export([get/1,get_ids/0]).

get(1)->
  #people{
    id=1,
    people_name="John",
    age=23
  };
get(2)->
  #people{
    id=2,
    people_name="Ken",
    age=19
  };
get(3)->
  #people{
    id=3,
    people_name="Tom",
    age=23
  };
get(4)->
  #people{
    id=4,
    people_name="Healthy",
    age=19
  };
get(_)->
  undefined.

get_ids()->
  [1,2,3,4].

注意,对于data_animal.erldata_people.erlget/1的参数是返回值的记录id,get_ids/0的返回值是get/1的参数列表。

test.erl

-module(test).

%% API
-export([get_animal_list/1,get_people_list/1]).

-include("animal.hrl").
-include("people.hrl").

get_animal_list(Age)->
  Fun=fun(Id,Acc)->
    case data_animal:get(Id) of
      #animal{age=Age}=Conf->
        [Conf|Acc];
      _->
        Acc
    end
      end,
  lists:foldl(Fun,[],data_animal:get_ids()).

get_people_list(Age)->
  Fun=fun(Id,Acc)->
    case data_people:get(Id) of
      #people{age=Age}=Conf->
        [Conf|Acc];
      _->
        Acc
    end
      end,
  lists:foldl(Fun,[],data_people:get_ids()).

我想获取年龄为23岁的动物和人的数据,所以我写了2个函数,get_animal_list/1, get_people_list/1

我跑

1> c(data_animal),c(data_people),c(test).
{ok,test}
2> test:get_people_list(23).
[{people,3,"Tom",23},{people,1,"John",23}]
3> test:get_animal_list(23).
[{animal,3,"tiger",23},{animal,1,"cat",23}]

突然间,我发现这两个函数共享相同的模式。然后我尝试编写一个宏get_list,然后进行 2 次调用。

test_macro.erl

-module(test_macro).

%% API
-export([get_animal_list/1,get_people_list/1]).

-include("animal.hrl").
-include("people.hrl").

-define(get_list(DataMod,Record,Age),(
  Fun=fun(Id,Acc)->
    case DataMod:get(Id) of
      #Record{age=Age}=Conf->
        [Conf|Acc];
      _->
        Acc
    end
  end,
  lists:foldl(Fun,[],DataMod:get_ids())
)).

get_animal_list(Age)->
  ?get_list(data_animal,animal,Age).

get_people_list(Age)->
  ?get_list(data_people,people,Age).

但是我得到了编译错误:

4> c(test_macro).
test_macro.erl:22: syntax error before: ','
test_macro.erl:25: syntax error before: ','
test_macro.erl:4: function get_animal_list/1 undefined
test_macro.erl:4: function get_people_list/1 undefined
error

告诉我为什么~y~y~

谢谢大家! 我现在有 3 个问题。

  1. 我的代码真的不像 Erlang 吗?它是从我公司的项目中提取的。我还在考虑面向对象编程吗?或者我公司的编程人员也是如此?
  2. 感谢@mlambichs 的建议。它有效,但我仍然想知道为什么我的代码会出现编译错误?是不是因为 Erlang 预处理器是一次性扫描器,所以无法识别#Record{age=Age}
  3. 根据@mlambichs 的建议,我尝试更改宏

    -define(get_list(DataMod, Record, Age), [P || P DataMod:get(Id) 结束, DataMod:get_ids()), P#Record.age =:= 年龄] )。

进入函数

get_list(DataMod, Record, Age)->
  [P || P <- lists:map(fun(Id) -> DataMod:get(Id) end,
    DataMod:get_ids()),
    P#Record.age =:= Age].

然后我得到编译错误: syntax error before: Record

【问题讨论】:

  • 关于样式的注释(这可能会理所当然地解决您的问题)直接在 .erl 文件中定义记录。只有animal.erlpeople.erl 并将接口函数写入数据,以便它们表现为自己的抽象数据类型。为了在 Erlangdom 中更进一步(并按照 Alan Kay 的意图使其成为 OOP),让 people.erlanimal.erl 模块定义进程,以便每个实例动物或人。将接口函数写入那些进程作为访问器。通过这种方式,游戏服务器之类的东西在 Erlang 中得到了简化。
  • 我想知道的是,为什么会出现编译错误?更重要的是,如果有人可以在不使用 list:foldl/3 的情况下优化我的代码,我将不胜感激。
  • 宏定义的末尾好像缺少一个结束符")"
  • 不,在 Fun 定义之前有一个开头的“(”,应该删除。如果添加额外的“)”,它将不起作用。
  • 所以你需要有人来校对你工作或学校的语法,而不是真正指导你走向更一般意义上的更好的 Erlang。非常:-/

标签: compilation macros erlang record


【解决方案1】:

错误的原因是错误的'('应该被删除:

-define(get_list(DataMod,Record,Age), (
                                     ^^^
   Fun=fun(Id,Acc)->              
     case DataMod:get(Id) of
        #Record{age=Age}=Conf->
           [Conf|Acc];
        _->
           Acc
     end
   end,
   lists:foldl(Fun,[],DataMod:get_ids())
).

编辑 您添加了一些我想现在开始回答的问题。

  • 我的代码真的不像 Erlang 吗?
    • 宏的使用。在您的情况下,没有真正需要使用宏。
    • 一般情况下:您希望隐藏在人和动物中使用了哪些类型的记录这一事实。那是实现,应该被你的接口屏蔽。您可以在正确的模块中定义一个处理该问题的 getter 函数。请。阅读我的重写建议。
  • 它可以工作,但我仍然想知道为什么我的代码会出现编译错误?请参阅答案顶部。
  • ....我尝试更改宏 .... 你是对的,该函数无法编译。显然需要重写。

像这样:

get_list(DataMod, Age) ->
    [ P || P = {_,_,_,A} <- lists:map(fun(Id) -> DataMod:get(Id) end,
           DataMod:get_ids()),
           A =:= Age].

编辑

开始重写。您想要的是功能测试中两个列表的串联(您的测试/0,我的测试/1)。使用逗号不会为您做到这一点。 ;-)

test(X)->
  ?get_list(data_animal,X) ++
  ?get_list(data_people,X).

让我们也修复 get_list 宏。您的宏定义 get_list 有 3 个参数,而它只需要 2 个。为什么在 get_people_list/1 和 get_animal_list/1 中已经使用 Record 作为参数?例如,试试这个:

-define(get_list(DataMod, Y),
   case DataMod of
      data_animal -> get_animal_list(Y);
      data_people -> get_people_list(Y);
      _Else -> []
   end
)

总体而言,您的测试模块中有很多代码复制。作为@yjcdll 建议的后续行动,将接口功能移至动物,将人移至它们自己的模块。

让我们看看您的数据模块及其获取函数。

我建议将所有人员记录放在一个数组中,在你的情况下放在 data_people 模块中。

people() -> [
   #people{ id=1, people_name="John", age=23 },
   #people{ id=2, people_name="Ken", age=19 },
   #people{ id=3, people_name="Tom", age=23 },
   #people{ id=4, people_name="Healthy", age=19 } ].

接下来,您需要一个 getter 函数来仅获取特定年龄的人:

get(Age) ->
   [X || X <- people(), is_age( X, Age )].

is_age/2 函数是:

is_age( Person, Age ) ->
   Person#people.age =:= Age.

所以在模块测试中你的 get_people_list/1 会变得简单很多。

get_people_list(Age) ->
   data_people:get(Age).

等等。始终留意看起来与您已经在某处使用过的代码几乎相同的代码。试着表现得像一个理智的、懒惰的程序员。懒惰=好。 ;-)

编辑:OP 必须坚持给定的模块。所以宏的重写是:

-define(get_list(DataMod, Record, Age),
   [P || P <- lists:map(fun(Id) -> DataMod:get(Id) end, 
                                   DataMod:get_ids()), 
         P#Record.age =:= Age]
).

【讨论】:

  • 可能你误会了。我无法更改代码结构,即data_.erl 和*.hrl 的分布。我想要做的是抽象 get__list 函数的公共部分并制作一个宏。为什么记录名称不能被预处理器替换。
  • 啊哈。也许这会有所帮助:-define(get_list(DataMod, Record, Age), [P || P DataMod:get(Id) end, DataMod:get_ids()), P #Record.age =:= 年龄])。
猜你喜欢
  • 2015-11-29
  • 1970-01-01
  • 2016-02-16
  • 1970-01-01
  • 1970-01-01
  • 2011-08-21
  • 1970-01-01
  • 1970-01-01
  • 2013-09-26
相关资源
最近更新 更多