Erlang records problems

I am struggling with entries in one of my modules.

I defined on top of my code an entry as:

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

in a few words, each user will be presented as a process with its own pid and other fields.

Later in the module I do the following:

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

Running this code I get:

 ** exception error: {badrecord,user} 

But if I do this:

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

He is typing

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

Can someone point out what I am missing?

thanks

+6
source share
4 answers

Emil's answer about the lists:filter function is correct.

This is how I would rewrite your code:

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

I assume you may have several pids. If you do not, you can save foreach.

I believe that using lists in this case makes the code more readable. In addition, the following:

Pid = UserPid,

not very useful for me ...

+7
source

The problem is that lists:filter returns another list, not one item. Thus, you are basically trying to treat the list as a record. If you look carefully at the exit

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

You will notice that the operator is wrapped in [] . This is a list. If you need only the first user,

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

If you want pids to use lists:map :

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

Now Pids is a list with user numbers using pid.

+11
source

As others noted, lists:filter/2 returns a list, even if it is only one item. The function you are looking for is lists:keyfind/3 (in Erlang R14B03, for R13B04 and earlier, use 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 is preferred because it is simpler.

Using only #user.pid returns the position of the pid field in the #user :

 6> #user.pid. 2 
+4
source

the variable you are trying to display is not a record, but rather a list with one element inside it. The item inside this list is the entry you want. Consider the template corresponding to the result of your lists:filter in the case, for example:

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

Also, remember the true if statement clause.

0
source

Source: https://habr.com/ru/post/893244/


All Articles