2011-02-10 8 views
6

Na przykład mam erlang rekordu:Erlang lista poz rekord

-record(state, {clients 
      }). 

mogę zrobić z listy pól klienci?

Którą mogę przechowywać w kliencie złożonym jak na zwykłej liście? I jak mogę dodać niektóre wartości na tej liście?

Dziękuję.

Odpowiedz

7

Może masz na myśli coś takiego:

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

Test:

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()] oznacza, że ​​typ pola znajduje się lista dodatnich liczb całkowitych, począwszy od 1; jest to wskazówka dla narzędzia analitycznego dialyzer, gdy wykonuje sprawdzanie typu.

Erlang pozwala również użyć dopasowywania wzoru na wyniki:

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

dalszego czytania:


@JUST MOJA poprawna OPINIA answer zmusiła mnie do zapamiętania, że ​​uwielbiam jak Haskell pobiera wartości pól w typie danych.

Oto definicja typu danych, skradzionym z Learn You a Haskell for Great Good!, który wykorzystuje zapis składni:

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

Tworzy funkcje company, model i year, że pola odnośnika w typie danych. My najpierw dokonać nowego samochodu:

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

Albo, stosując zapis składni (kolejność pól nie ma znaczenia):

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 

Możemy nawet użyć dopasowywania wzoru:

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" 

Pamiętam, że były próby wprowadzenia czegoś podobnego do składni Haskella w Erlangu, ale nie byłem pewien, czy udało im się.

niektórych stanowisk, dotyczące tych prób:

Wydaje się, że LFE używa makra, które są podobne do tego, co zapewnia schematem (Racket, na przykład), gdy chcesz utworzyć nową wartość pewnej strukturze:

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

Mam nadzieję, że” Będą mieć coś zbliżonego do składni Haskella w przyszłości, co byłoby naprawdę praktyczne i przydatne.

+0

ewps, to "dializator". Więcej na temat Erlanga [Records] (http://www.erlang.org/doc/programming_examples/records.html). –

+0

Och, powinno to być 'clients = [] :: [pos_integer()],' –

+0

Nie możesz edytować swojego posta, aby zaktualizować go za pomocą 2600 punktów? – ndim

1

Jeśli dodajesz lub usuwasz tylko pojedyncze pozycje z listy klientów w stanie, w którym można ograniczyć pisanie za pomocą makra.

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

Oto escript test, który pokazuje:

#!/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]). 

Wynik:

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

W tego rodzaju przypadkach wolę używać funkcji zamiast makra. Sprawia, że ​​kod jest łatwiejszy do naśladowania, refaktoryzacji i testowania. –

3

Yasir's answer jest prawidłowa, ale mam zamiar pokazać, dlaczego to działa w ten sposób, działa tak, że możesz lepiej zrozumieć rekordy.

Rejestry w Erlang są hackerem (i dość brzydkim). Stosując definicję rekord z odpowiedzią Jaser za ...

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

... kiedy instancję to z #state{} (jak Jaser zrobił w empty_state/0 funkcji), czego naprawdę wrócić to:

{state, [], undefined} 

Oznacza to, że twój "rekord" jest po prostu krotką oznaczoną nazwą rekordu (state w tym przypadku), po której następuje zawartość rekordu. Wewnątrz samego BEAM nie ma zapisu. To tylko kolejna krotka z zawartymi w niej typami danych Erlanga. Jest to klucz do zrozumienia, jak działają rzeczy (i ograniczenia dotyczące uruchamiania rekordów).

Teraz kiedy Jaser to zrobił ...

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

... The S#state.clients nieco przekłada się wewnętrznie kodu, który wygląda jak element(2,S). Używasz, innymi słowy, standardowych funkcji manipulacji krotką. S#state.clients to tylko symboliczny sposób powiedzenia tego samego, ale w sposób, który pozwala stwierdzić, który element 2 jest w rzeczywistości jest. To syntaktyczna sacharyna, która jest lepszym sposobem na śledzenie poszczególnych pól w twoich krotkach w sposób podatny na błędy.

Trochę za ostatnie ostatnie S#state{clients = [Client|C]} trochę, nie jestem absolutnie pozytywny co do tego, jaki kod jest generowany za kulisami, ale najprawdopodobniej jest to prosta rzecz, która ma odpowiednik {state, [Client|C], element(3,S)}. To:

  • znaczniki nowy krotki o nazwie rekordu (dostarczony jako #state)
  • kopii elementów z S (dyktowane przez część S#)
  • wyjątkiem clients elementu zastępowane {clients = [Client|C]}.

Cała ta magia odbywa się za pomocą przerywanego hackowania za kulisami.

Zrozumienie, jak rekordy działają za kulisami, jest korzystne zarówno dla zrozumienia kodu napisanego przy użyciu rekordów, jak i dla zrozumienia, jak z nich korzystać (nie wspominając o zrozumieniu, dlaczego rzeczy, które wydają się "mieć sens", nie działają z rekordami - ponieważ tak naprawdę nie istnieją w abstrakcyjnej maszynie ... jeszcze).