2011-02-10 2 views
6

Например, я Erlang запись:Erlang список запись пункт

-record(state, {clients 
      }). 

Могу ли я сделать из списка полей клиентов?

Что я могу хранить в клиенте, как в обычном списке? И как я могу добавить некоторые значения в этот список?

Спасибо.

ответ

7

Может быть, вы имеете в виду что-то вроде:

-module(reclist). 
-export([empty_state/0, some_state/0, 
     add_client/1, del_client/1, 
     get_clients/1]). 

-record(state, 
    {  
      clients = [] ::[pos_integer()], 
      dbname   ::char() 
    }).  

empty_state() -> 
    #state{}. 

some_state() -> 
    #state{ 
      clients = [1,2,3], 
      dbname = "QA"}. 

del_client(Client) -> 
    S = some_state(), 
    C = S#state.clients, 
    S#state{clients = lists:delete(Client, C)}. 

add_client(Client) -> 
    S = some_state(), 
    C = S#state.clients, 
    S#state{clients = [Client|C]}. 

get_clients(#state{clients = C, dbname = _D}) -> 
    C. 

Тест:

1> reclist:empty_state(). 
{state,[],undefined} 
2> reclist:some_state(). 
{state,[1,2,3],"QA"} 
3> reclist:add_client(4). 
{state,[4,1,2,3],"QA"} 
4> reclist:del_client(2). 
{state,[1,3],"QA"} 

::[pos_integer()] означает, что тип поля является список положительных целых чисел, начиная с 1; это подсказка для инструмента анализа dialyzer, когда он выполняет проверку типов.

Erlang также позволяет использовать сопоставление с образцом на записи:

5> reclist:get_clients(reclist:some_state()). 
[1,2,3] 

Далее чтение:


@JUST МОЙ правильный ОПИСАНИЕ answer ОПИСАНИЕ заставило меня вспомнить, что мне нравится, как Haskell собирается получать значения полей в типе данных.

Вот определение типа данных, похищенный из Learn You a Haskell for Great Good!, что рычагов синтаксиса записи:

data Car = Car {company :: String 
       ,model :: String 
       ,year :: Int 
       } deriving (Show) 

Это создает функцию company, model и year, что подстановочные поля в типе данных. Сначала сделать новый автомобиль:

ghci> Car "Toyota" "Supra" 2005 
Car {company = "Toyota", model = "Supra", year = 2005} 

Или, используя синтаксис записи (порядок полей не имеет значения):

ghci> Car {model = "Supra", year = 2005, company = "Toyota"} 
Car {company = "Toyota", model = "Supra", year = 2005} 
ghci> let supra = Car {model = "Supra", year = 2005, company = "Toyota"} 
ghci> year supra 
2005 

Мы даже можем использовать сопоставление с образцом:

ghci> let (Car {company = c, model = m, year = y}) = supra 
ghci> "This " ++ C++ " " ++ m ++ " was made in " ++ show y 
"This Toyota Supra was made in 2005" 

Я помню, что были попытки реализовать что-то похожее на синтаксис записи Haskell в Erlang, но не уверены, были ли они успешными.

Некоторые сообщения, касающиеся этих попыток:

Кажется, что LFE использует макросы, которые похожи на то, что обеспечивает Scheme (Racket, например), когда вы хотите создать новое значение некоторой структуры:

> (define-struct car (company model year)) 
> (define supra (make-car "Toyota" "Supra" 2005)) 
> (car-model supra) 
"Supra" 

Я надеюсь, что мы» В будущем у вас будет что-то близкое к синтаксису записи Haskell, это будет действительно практически полезно и удобно.

+0

ewps, это 'диализатор'. Подробнее о Erlang [Records] (http://www.erlang.org/doc/programming_examples/records.html). –

+0

О, это должно быть 'clients = [] :: [pos_integer()],' –

+0

Не можете ли вы отредактировать свой собственный пост, чтобы обновить его с помощью 2600 кредитных баллов? – ndim

1

Если вы добавляете или удаляете отдельные элементы из списка клиентов в состоянии, которое вы можете сократить, набрав макрос.

-record(state, {clients = [] }). 

-define(AddClientToState(Client,State), 
    State#state{clients = lists:append([Client], State#state.clients) }). 

-define(RemoveClientFromState(Client,State), 
    State#state{clients = lists:delete(Client, State#state.clients) }). 

Вот тест, который демонстрирует escript:

#!/usr/bin/env escript 

-record(state, {clients = [] }). 

-define(AddClientToState(Client,State), 
    State#state{clients = lists:append([Client], State#state.clients)} ). 

-define(RemoveClientFromState(Client,State), 
    State#state{clients = lists:delete(Client, State#state.clients)} ). 


main(_) -> 

    %Start with a state with a empty list of clients. 
    State0 = #state{}, 
    io:format("Empty State: ~p~n",[State0]), 

    %Add foo to the list 
    State1 = ?AddClientToState(foo,State0), 
    io:format("State after adding foo: ~p~n",[State1]), 

    %Add bar to the list. 
    State2 = ?AddClientToState(bar,State1), 
    io:format("State after adding bar: ~p~n",[State2]), 

    %Add baz to the list. 
    State3 = ?AddClientToState(baz,State2), 
    io:format("State after adding baz: ~p~n",[State3]), 

    %Remove bar from the list. 
    State4 = ?RemoveClientFromState(bar,State3), 
    io:format("State after removing bar: ~p~n",[State4]). 

Результат:

Empty State: {state,[]} 
State after adding foo: {state,[foo]} 
State after adding bar: {state,[bar,foo]} 
State after adding baz: {state,[baz,bar,foo]} 
State after removing bar: {state,[baz,foo]} 
+1

В таких случаях я предпочитаю использовать функцию вместо макроса. Делает код более легким для последующего использования, рефакторингом и тестированием. –

3

Yasir's answer является правильным, но я собираюсь показать вам, почему это работает так, как он работает, чтобы вы могли лучше понимать записи.

Записи в Эрланге - это взлом (и довольно уродливый). Используя определение записи из ответа Ясир в ...

-record(state, 
    {  
      clients = [] ::[pos_integer()], 
      dbname   ::char() 
    }). 

... При создании экземпляра этого с #state{} (как Ясир в empty_state/0 функции), что вы действительно получите обратно это:

{state, [], undefined} 

То есть ваша «запись» - это всего лишь кортеж, помеченный именем записи (state в этом случае), за которым следует содержимое записи. Внутри BEAM сам нет записи. Это всего лишь еще один кортеж с типами данных Erlang, содержащимися в нем. Это ключ к пониманию того, как все работает (и ограничения записей для загрузки).

Теперь, когда Ясир сделал это ...

add_client(Client) -> 
    S = some_state(), 
    C = S#state.clients, 
    S#state{clients = [Client|C]}. 

... S#state.clients бит преобразуется в код внутренне, который выглядит как element(2,S). Вы используете, другими словами, стандартные функции манипуляции с кортежем. S#state.clients - это просто символический способ сказать одно и то же, но таким образом, чтобы вы знали, какой элемент 2 на самом деле -. Это синтаксический сахарин, который является улучшением по сравнению с отслеживанием отдельных полей в ваших кортежах с ошибкой.

Теперь, за последний S#state{clients = [Client|C]} бит, я не совсем уверен, какой код генерируется за кулисами, но это, скорее всего, простой материал, который делает эквивалент {state, [Client|C], element(3,S)}. Он:

  • теги новый кортеж с именем записи (при условии, как #state),
  • копирует элементы из S (диктуемые S# части),
  • для clients части переопределены кроме {clients = [Client|C]}.

Все это волшебство выполняется посредством предварительной обработки за кулисами.

Понимание того, как записи работают за кулисами, полезно как для понимания кода, написанного с использованием записей, так и для понимания того, как их использовать самостоятельно (не говоря уже о понимании того, почему вещи, которые кажутся «понятными», не работают с записями - потому что они фактически не существуют в абстрактной машине ... пока).

Смежные вопросы