Плохая запись при вызове списка: keyfind () в erlang - PullRequest
0 голосов
/ 19 октября 2019

Я новичок в erlang, и у меня возникла ошибка с записями в одном из моих модулей. Я эмулирую корабли внутри состояния shipping_state и хочу создать простую функцию, которая будет печатать идентификатор корабля, имя и крышку контейнера определенного корабля на основе его идентификатора. Я использовал list: keyfind, так как считаю, что это поможет, но, возможно, я не правильно его использую. У меня есть файл .hrl, который содержит объявления записей, и файл .erl с функцией и инициализацией моего # shipping_state.

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, containsercap}, что-то не так ссинтаксис есть, но если мне нужно полностью переосмыслить, как мне поступить с этой проблемой, любая помощь будет принята с благодарностью.

Правка. Я изменил свой код, следуя советам Алексея, но, похоже, все еще возникает та же ошибка. Есть еще идеи?

 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]).

Ответы [ 3 ]

3 голосов
/ 19 октября 2019
  1. См. Внутреннее представление записей : #ship{id=1,name="Santa Maria",container_cap=20} становится {ship, 1, "Santa Maria", 20}, поэтому id является вторым элементом, а не первым.

  2. {id, name, containercap} = ...
    

    должно быть

    #ship{id=Id, ...} = ...
    

    или

    {ship, Id, Name, Containercap} = ...
    

    Ваш текущий код будет успешным, только если keyfind вернет кортеж3 атома.

  3. Ошибка {badrecord,shipping_state} говорит вам, что код get_ship ожидает, что его первый аргумент будет #shipping_state, но вы передаете {ok, #shipping_state{...}} (результатinit).

2 голосов
/ 19 октября 2019

Записи были добавлены в язык 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 предоставляет номер поля для поля id записи ship. Это продолжит работать правильно, даже если вы добавите поля в запись, и в отличие от необработанного числа, это вызовет ошибку компиляции, если вы решите удалить это поле из записи в какой-то момент.

Если вы загрузитеОпределения записей в вашей оболочке с помощью команды rr, вы можете видеть, что #ship.id возвращает ожидаемый номер поля:

1> rr("shipping.hrl").
[container,port,ship,shipping_state]
2> #ship.id.
2

С дополнительными исправлениями, приведенными выше для правильной обработки возвращенной записи, онатеперь работает как положено, как показывает этот сеанс оболочки:

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
1 голос
/ 19 октября 2019

Ответ Алексея отвечает на ваш вопрос, в частности 3-й пункт. Я просто хочу предложить улучшение вашего keyfind звонка. Вам нужно передать ему индекс кортежа, но вы можете использовать синтаксис записи, чтобы получить этот индекс без его жесткого кодирования, например:

 list:keyfind(Ship_ID, #ship.id, Shipping_State#shipping_state.ships),

#ship.id возвращает индекс поля id, в данном случае 2. Это облегчает чтение кода - не нужно удивляться, для чего нужна константа 2. Кроме того, если по какой-либо причине вы измените порядок полей в записи ship, этот код все равно будет скомпилирован и будет работать правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...