2011-02-10 17 views
6

Por ejemplo tengo registro de Erlang:Erlang lista de elementos de registro

-record(state, {clients 
      }). 

puedo llamar desde la lista de campos clientes?

¿Que podría mantener en el cliente archivado como en la lista normal? ¿Y cómo puedo agregar algunos valores en esta lista?

Gracias.

Respuesta

7

Tal vez quiere decir algo así como:

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

prueba:

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()] significa que el tipo del campo es una lista de valores enteros positivos, a partir de 1; es la sugerencia para la herramienta de análisis dialyzer, cuando realiza verificación de tipos.

Erlang también permite que utilice la coincidencia de patrones en los registros:

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

Más información:


@JUST MI OPINIÓN correcta de answer me hizo recordar que me encanta cómo Haskell trata de obtener los valores de los campos en el tipo de datos.

Aquí es una definición de tipo de datos, robado de Learn You a Haskell for Great Good!, que aprovecha la sintaxis de registro:

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

Se crea funciones company, model y year, que los campos de búsqueda en el tipo de datos. Primero hacemos un nuevo coche:

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

O, usando la sintaxis de registro (el orden de los campos no importa):

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 

Incluso podemos utilizar la coincidencia de modelos:

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" 

Recuerdo que hubo intentos de implementar algo similar a la sintaxis de registros de Haskell en Erlang, pero no estoy seguro de si tuvieron éxito.

Algunos de los puestos, con respecto a estos intentos:

Parece que LFE utiliza macros, que son similares a lo que ofrece el esquema (Racket, por ejemplo), cuando se quiere crear un nuevo valor de algún tipo de estructura:

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

espero que' Tendré algo cercano a la sintaxis de registro de Haskell en el futuro, que sería realmente útil y práctico.

+0

ewps, es 'dializador'. Más sobre Erlang [Records] (http://www.erlang.org/doc/programming_examples/records.html). –

+0

Oh, debería haber sido 'clients = [] :: [pos_integer()],' –

+0

¿No puedes editar tu propia publicación para actualizarla con tus 2600 puntos de crédito? – ndim

1

Si solo está agregando o eliminando elementos individuales de la lista de clientes en el estado, podría reducir el tipeo con una macro.

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

Aquí es una prueba que demuestra 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]). 

Resultado:

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

En este tipo de casos, prefiero usar una función en lugar de una macro. Hace que el código sea más fácil de seguir, refactorizar y probar. –

3

Yasir's answer es la correcta, pero voy a mostrar por qué funciona el camino funciona para que pueda entender los registros un poco mejor.

Los registros en Erlang son un truco (y uno bastante feo). Utilizando la definición de registro de la respuesta de Yasir ...

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

... cuando usted instancia esto con #state{} (como se hizo en Yasir empty_state/0 función), lo que realmente se obtiene es la siguiente:

{state, [], undefined} 

Es decir, su "registro" es solo una tupla etiquetada con el nombre del registro (state en este caso) seguido del contenido del registro. Dentro de BEAM sí mismo no hay registro. Es solo otra tupla con los tipos de datos Erlang que contiene. Esta es la clave para entender cómo funcionan las cosas (y las limitaciones de los registros para arrancar).

Ahora, cuando Yasir hizo esto ...

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

... el bit S#state.clients se traduce en un código interno que se parece a element(2,S). Está utilizando, en otras palabras, funciones estándar de manipulación de tuplas. S#state.clients es solo una forma simbólica de decir lo mismo, pero de una manera que le permite saber qué elemento 2 realmente es. Es la sacarina sintáctica que es una mejora sobre el seguimiento de los campos individuales en sus tuplas de una manera propensa a errores.

Ahora, para la última bit de S#state{clients = [Client|C]}, no estoy absolutamente seguro de qué código se genera detrás de las escenas, pero es probable que solo sea algo sencillo que haga el equivalente de {state, [Client|C], element(3,S)}. Es:

  • etiqueta una nueva tupla con el nombre del registro (en forma de #state),
  • copia los elementos de S (dictadas por la parte S#),
  • a excepción de la pieza clients anulado por {clients = [Client|C]}.

Toda esta magia se realiza a través de un hack de preprocesamiento detrás de escena.

Comprender cómo funcionan los registros entre bastidores es beneficioso tanto para entender el código escrito utilizando registros como para comprender cómo usarlos usted mismo (sin mencionar que las cosas que parecen "tener sentido" no funcionan con registros) porque todavía no existen en la máquina abstracta ... todavía).

Cuestiones relacionadas