2011-07-21 11 views
6

Mam problemy z rekordami w jednym z moich modułów.Erlang records records

I zdefiniowane na szczycie mojego kodu zapis jako:

-record(user, {pid, 
       name, 
       nick}). 

w kilku słowach każdy użytkownik będzie reprezentowana jako proces z własnymi polami PID i innych.

Później w module robie co następuje:

Pid = UserPid, 
GetUser = fun(X) -> 
       if X#user.pid =:= Pid -> true; 
        X#user.pid=/= Pid -> false 
       end 
     end, 
User = lists:filter(GetUser, Users), 
io:format("User pid is ~p~n",[User#user.pid]). 

Uruchomienie tego kodu uzyskać:

** exception error: {badrecord,user} 

Ale jeśli to zrobię:

io:format("User ~p~n",[User]).  

Drukuje

User [{user,<0.33.0>,name1,nick1}] 

Czy ktoś może wskazać, czego mi brakuje?

Dzięki

Odpowiedz

7

Odpowiedź Emila na temat funkcji the lists:filter jest poprawna.

ten sposób chciałbym przepisać kod, choć:

-module(com). 

-record(user, {pid, 
       name, 
       nick}). 

-export([lookup/1]). 

lookup(Pid) -> 
    Users = users(), 
    FilteredUsers = [User || #user{pid = P} = User <- Users, Pid =:= P], 
    lists:foreach(fun display/1, FilteredUsers). 

display(User) -> 
    io:format("User name is ~p~n",[User#user.name]). 

users() -> 
    User1 = #user{pid = 1, name = "Bob", nick = "bob"}, 
    User2 = #user{pid = 2, name = "Alice", nick = "alice"}, 
    User3 = #user{pid = 1, name = "Charlie", nick = "charlie"}, 
    [User1, User2, User3]. 

Jestem zakładając, można mieć wiele PID. Jeśli tego nie zrobisz, możesz uratować sobie foreach.

Wierzę, że używanie list comprehensions w tym przypadku sprawia, że ​​kod jest znacznie bardziej czytelny.Ponadto, co następuje:

PID = UserPid,

nie wygląda bardzo użyteczne dla mnie ...

+0

+1 Bardzo schludny przykład idiomatycznego Erlanga. –

+3

Zrobiłbym "[U || U <- Users, U # user.pid =: = P] '. –

+0

@Adam Lindberg: +1. Jeszcze bardziej czytelny. –

11

problemów jest to, że lists:filter zwraca inną listę, a nie pojedynczy element. Więc zasadniczo próbujesz traktować listę jako rekord. Jeśli przyjrzeć się dokładnie na wyjściu

io:format("User ~p~n",[User]) 
%% User [{user,<0.33.0>,name1,nick1}] 

można zauważyć, że oświadczenie jest owinięty w []. To jest lista. Jeśli trzeba po prostu użyć pierwszy użytkownik

[First | Rest] = lists:filter(GetUser, Users) 

Jeśli chcesz tylko PIDs używać lists:map:

UsersWithPid = lists:filter(GetUser, Users), 
Pids = lists:map(fun(U) -> U#user.pid end, UsersWithPid). 

Teraz Pids jest lista z PID użytkowników z PID.

+2

Zauważ, że [pierwsze, Rest] będzie pasował na liście z dokładnie dwa elementy. Prawdopodobnie chcesz: [First | Rest]. Lub, jeśli oczekujesz dokładnie jednego wyniku: [Po pierwsze]. –

+1

@Roberto - Dokładnie, dziękuję. –

0

zmienna, którą próbujesz wyświetlić, nie jest rekordem, ale raczej Listą z jednym elementem w środku. Element na tej liście jest rekordem, który chcesz. Rozważmy wzorzec dopasowania wynik swojej lists:filter w przypadku rachunku tak:

 
case lists:filter(GetUser, Users) of 
    [] -> 
     %% user not found against that criteria..... 
    [User] when is_record(User,user) -> 
    %% user found. Do things here........ 
    List -> %% are many things matching your criteria ? 
end, 
... 

również pamiętać o klauzuli if statementtrue.

4

Jak inni zwrócili uwagę, lists:filter/2 zwraca listę, nawet jeśli jest to tylko jeden element. Funkcja szukasz jest lists:keyfind/3 (w Erlang R14B03 dla R13B04 i wcześniej, użyj lists:keysearch/3):

Eshell V5.8.4 (abort with ^G) 
1> rd(user, {pid, name, nick}). 
user 

2> Users = [#user{pid = spawn(fun() -> ok end), name = name1, nick = nick1}, 
2>   #user{pid = spawn(fun() -> ok end), name = name2, nick = nick2}, 
2>   #user{pid = spawn(fun() -> ok end), name = name3, nick = nick3}]. 
[#user{pid = <0.34.0>,name = name1,nick = nick1}, 
#user{pid = <0.35.0>,name = name2,nick = nick2}, 
#user{pid = <0.36.0>,name = name3,nick = nick3}] 

3> lists:keysearch(pid(0,35,0), #user.pid, Users). 
{value,#user{pid = <0.35.0>,name = name2,nick = nick2}} 

4> lists:keyfind(pid(0,35,0), #user.pid, Users). 
#user{pid = <0.35.0>,name = name2,nick = nick2} 

5> lists:keyfind(pid(0,99,0), #user.pid, Users). 
false 

lists:keyfind/3 jest korzystny, ponieważ jest to prostsze.

Korzystanie tylko #user.pid zwraca pozycję pola w rekordzie pid#user:

6> #user.pid. 
2 
+0

+1 dla # user.pid zwraca pozycję pola pid w #user rekordu. – justin