在 erlang 中调用 list:keyfind() 时的错误记录
Bad Record when calling list:keyfind() in erlang
我是 erlang 的新手,运行 我的一个模块中的记录出现错误。我在 shipping_state 内模拟船只,我想创建一个简单的函数,根据它的 ID 打印某艘船的船号、名称和集装箱盖。我使用了 list:keyfind,因为我相信它会有所帮助,但也许我没有正确使用它。我有一个包含记录声明的 .hrl 文件,以及一个包含我的 #shipping_state.
函数和初始化的 .erl 文件
shipping.erl
:
-module(shipping).
-compile(export_all).
-include_lib("./shipping.hrl").
get_ship(Shipping_State, Ship_ID) ->
{id, name, containercap} = list:keyfind(Ship_ID, 1, Shipping_State#shipping_state.ships).
shipping.hrl
:
-record(ship, {id, name, container_cap}).
-record(container, {id, weight}).
-record(shipping_state,
{
ships = [],
containers = [],
ports = [],
ship_locations = [],
ship_inventory = maps:new(),
port_inventory = maps:new()
}
).
-record(port, {id, name, docks = [], container_cap}).
结果:
shipping:get_ship(shipping:init(),1).
** exception error: {badrecord,shipping_state}
in function shipping:get_ship/2 (shipping.erl, line 18)
我想说 keyfind 应该可以工作,也许当我创建元组 {id, name, containercap} 时,那里的语法有问题,但如果我需要完全重新考虑我会怎么做关于做这个问题,任何帮助将不胜感激。
编辑,
我已经修改了我的代码以遵循 Alexey 的建议,但是,似乎仍然存在相同的错误。任何进一步的见解?
get_ship(Shipping_State, Ship_ID) ->
{ship, Id, Name, Containercap} = list:keyfind(Ship_ID, 2,
Shipping_State#shipping_state.ships),
io:format("id = ~w, name = ~s, container cap = ~w",[Id, Name, Containercap]).
见Internal Representation of Records:#ship{id=1,name="Santa Maria",container_cap=20}
变为{ship, 1, "Santa Maria", 20}
,所以id是第二个元素,不是第一个。
{id, name, containercap} = ...
应该是
#ship{id=Id, ...} = ...
或
{ship, Id, Name, Containercap} = ...
只有 keyfind
返回 3 个原子的元组,您当前的代码才会成功。
错误 {badrecord,shipping_state}
告诉你 get_ship
的代码期望它的第一个参数是 #shipping_state
,但你传递 {ok, #shipping_state{...}}
(init
的结果)。
回答了你的问题,特别是第三点。我只是想建议改进您的 keyfind
通话。您需要将元组索引传递给它,但您可以使用记录语法来获取该索引而无需对其进行硬编码,如下所示:
list:keyfind(Ship_ID, #ship.id, Shipping_State#shipping_state.ships),
#ship.id
returns id
字段的索引,在本例中为 2。这使得代码更易于阅读 - 无需思考常量 2 的用途.此外,如果出于某种原因您更改了 ship
记录中字段的顺序,此代码仍将编译并执行正确的操作。
记录被添加到 Erlang 语言中,因为按数字处理元组字段很容易出错,尤其是在开发过程中更改代码以及添加、更改或删除元组字段时。不要使用数字来标识记录字段,也不要使用其底层元组表示来处理记录,因为这两者都违背了记录的目的并且是不必要的。
在您的代码中,不要使用带有 lists:keyfind/3
的记录字段编号,而是使用记录名称本身。我已经修改了您的 get_ship/2
函数来执行此操作:
get_ship(Shipping_State, Ship_ID) ->
#ship{id=ID, name=Name, container_cap=ContainerCap} = lists:keyfind(Ship_ID, #ship.id, Shipping_State#shipping_state.ships),
io:format("id = ~w, name = ~s, container cap = ~w~n",[ID, Name, ContainerCap]).
语法#<record_name>.<record_field_name>
提供基础记录字段编号。在上面的 lists:keyfind/3
调用中,#ship.id
为 ship
记录的 id
字段提供了字段编号。即使您将字段添加到记录中,这也将继续正常工作,并且与原始数字不同,如果您决定在某个时候从记录中删除该字段,它将导致编译错误。
如果使用 rr
命令将记录定义加载到 shell 中,您可以看到 #ship.id
returns 预期的字段编号:
1> rr("shipping.hrl").
[container,port,ship,shipping_state]
2> #ship.id.
2
通过对上述函数进行额外修复以正确处理返回的记录,它现在可以按预期工作,如本 shell 会话所示:
3> {ok, ShippingState} = shipping:init().
{ok,{shipping_state,[{ship,1,"Santa Maria",20},
{ship,2,"Nina",20},
{ship,3,"Pinta",20},
{ship,4,"SS Minnow",20},
{ship,5,"Sir Leaks-A-Lot",20}],
[{container,1,200},
...
4> shipping:get_ship(ShippingState, 1).
id = 1, name = Santa Maria, container cap = 20
ok
我是 erlang 的新手,运行 我的一个模块中的记录出现错误。我在 shipping_state 内模拟船只,我想创建一个简单的函数,根据它的 ID 打印某艘船的船号、名称和集装箱盖。我使用了 list:keyfind,因为我相信它会有所帮助,但也许我没有正确使用它。我有一个包含记录声明的 .hrl 文件,以及一个包含我的 #shipping_state.
函数和初始化的 .erl 文件shipping.erl
:
-module(shipping).
-compile(export_all).
-include_lib("./shipping.hrl").
get_ship(Shipping_State, Ship_ID) ->
{id, name, containercap} = list:keyfind(Ship_ID, 1, Shipping_State#shipping_state.ships).
shipping.hrl
:
-record(ship, {id, name, container_cap}).
-record(container, {id, weight}).
-record(shipping_state,
{
ships = [],
containers = [],
ports = [],
ship_locations = [],
ship_inventory = maps:new(),
port_inventory = maps:new()
}
).
-record(port, {id, name, docks = [], container_cap}).
结果:
shipping:get_ship(shipping:init(),1).
** exception error: {badrecord,shipping_state}
in function shipping:get_ship/2 (shipping.erl, line 18)
我想说 keyfind 应该可以工作,也许当我创建元组 {id, name, containercap} 时,那里的语法有问题,但如果我需要完全重新考虑我会怎么做关于做这个问题,任何帮助将不胜感激。
编辑, 我已经修改了我的代码以遵循 Alexey 的建议,但是,似乎仍然存在相同的错误。任何进一步的见解?
get_ship(Shipping_State, Ship_ID) ->
{ship, Id, Name, Containercap} = list:keyfind(Ship_ID, 2,
Shipping_State#shipping_state.ships),
io:format("id = ~w, name = ~s, container cap = ~w",[Id, Name, Containercap]).
见Internal Representation of Records:
#ship{id=1,name="Santa Maria",container_cap=20}
变为{ship, 1, "Santa Maria", 20}
,所以id是第二个元素,不是第一个。{id, name, containercap} = ...
应该是
#ship{id=Id, ...} = ...
或
{ship, Id, Name, Containercap} = ...
只有
keyfind
返回 3 个原子的元组,您当前的代码才会成功。错误
{badrecord,shipping_state}
告诉你get_ship
的代码期望它的第一个参数是#shipping_state
,但你传递{ok, #shipping_state{...}}
(init
的结果)。
keyfind
通话。您需要将元组索引传递给它,但您可以使用记录语法来获取该索引而无需对其进行硬编码,如下所示:
list:keyfind(Ship_ID, #ship.id, Shipping_State#shipping_state.ships),
#ship.id
returns id
字段的索引,在本例中为 2。这使得代码更易于阅读 - 无需思考常量 2 的用途.此外,如果出于某种原因您更改了 ship
记录中字段的顺序,此代码仍将编译并执行正确的操作。
记录被添加到 Erlang 语言中,因为按数字处理元组字段很容易出错,尤其是在开发过程中更改代码以及添加、更改或删除元组字段时。不要使用数字来标识记录字段,也不要使用其底层元组表示来处理记录,因为这两者都违背了记录的目的并且是不必要的。
在您的代码中,不要使用带有 lists:keyfind/3
的记录字段编号,而是使用记录名称本身。我已经修改了您的 get_ship/2
函数来执行此操作:
get_ship(Shipping_State, Ship_ID) ->
#ship{id=ID, name=Name, container_cap=ContainerCap} = lists:keyfind(Ship_ID, #ship.id, Shipping_State#shipping_state.ships),
io:format("id = ~w, name = ~s, container cap = ~w~n",[ID, Name, ContainerCap]).
语法#<record_name>.<record_field_name>
提供基础记录字段编号。在上面的 lists:keyfind/3
调用中,#ship.id
为 ship
记录的 id
字段提供了字段编号。即使您将字段添加到记录中,这也将继续正常工作,并且与原始数字不同,如果您决定在某个时候从记录中删除该字段,它将导致编译错误。
如果使用 rr
命令将记录定义加载到 shell 中,您可以看到 #ship.id
returns 预期的字段编号:
1> rr("shipping.hrl").
[container,port,ship,shipping_state]
2> #ship.id.
2
通过对上述函数进行额外修复以正确处理返回的记录,它现在可以按预期工作,如本 shell 会话所示:
3> {ok, ShippingState} = shipping:init().
{ok,{shipping_state,[{ship,1,"Santa Maria",20},
{ship,2,"Nina",20},
{ship,3,"Pinta",20},
{ship,4,"SS Minnow",20},
{ship,5,"Sir Leaks-A-Lot",20}],
[{container,1,200},
...
4> shipping:get_ship(ShippingState, 1).
id = 1, name = Santa Maria, container cap = 20
ok