Erlang 记录问题

6

我在其中一个模块的记录方面遇到了困难。

我在代码顶部定义了一个记录,如下:

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

简单来说,每个用户都将被表示为具有自己pid和其他字段的进程。

稍后在模块中,我将执行以下操作:

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

运行这段代码,我得到了以下结果:
** exception error: {badrecord,user}

但如果我这样做:

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

它打印出来

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

有人能指出我缺少什么吗?

谢谢

4个回答

11
问题在于 lists:filter 返回的是另一个列表,而不是单个元素。因此,您基本上正在尝试将列表视为记录。如果您仔细查看输出结果,则会发现
io:format("User ~p~n",[User])
%% User [{user,<0.33.0>,name1,nick1}]

你会注意到该语句被包裹在 [] 中。这是一个列表。如果只需要第一个用户,请使用

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

如果你只想要 pid,可以使用 lists:map

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

现在Pids是一个包含pid用户的pid列表。


2
请注意,[First,Rest]将匹配仅具有两个元素的列表。您可能想要:[First | Rest]。或者,如果您只期望一个结果:[First]。 - Roberto Aloi

7

Emil关于lists:filter函数的回答是正确的。

这是我会重写你的代码的方式:

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

我假设您可以有多个pid。如果没有,您可以省略foreach。
我认为在这种情况下使用列表推导会使代码更易读。此外,以下内容:

Pid = UserPid,

对我来说看起来没有什么用...

3
我会执行 [U || U <- Users, U#user.pid =:= P] - Adam Lindberg
非常感谢大家!事实上,你们提供的例子非常适合我的需求...关于PID,我实际上没有使用Pid = UserPid,只是为了澄清我想要实现什么。 - user601836

4

正如其他人指出的那样,lists:filter/2返回一个列表,即使它只是一个单独的元素。你要找的函数是lists:keyfind/3(在Erlang R14B03中,对于R13B04及更早版本,请使用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 更简单,因此更受欢迎。

仅使用#user.pid会返回#user记录中pid字段的位置:

6> #user.pid.
2

+1 for #user.pid 返回 #user 记录中 pid 字段的位置。 - justin

0

你试图显示的变量不是一个记录,而是一个只包含一个元素的列表。这个列表中的元素就是你想要的记录。考虑像这样在 case 语句中模式匹配你的 lists:filter 结果:

case lists:filter(GetUser, Users) of
   [] -> 
       %% 没有找到符合条件的用户.....
   [User] when is_record(User,user) -> 
     %% 找到了用户。在这里执行操作........
   List -> %% 是否有多个符合条件的?
end,
...

此外,记得 if 语句的 true 子句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接