搜索具有重复元素的记录列表

Searching through lists of records with repeating elements

我需要用 Erlang 制作一个地址簿。除了一个给我带来问题的功能外,我几乎完成了所有工作。

我的记录是:

-record(contact, {fname, lname, phone=[], mail=[], city=[], street=[]}).

我必须编写一个函数来搜索 contact 的实例并找到所有具有特定 city 名称的实例,并且对于这些实例 return {fname,lname} 元组.不同的联系人当然可以有相同的城市。

当我需要 mailphone 字段的类似功能时,我是这样做的:

findByPhone(_,[]) -> {error,"not found"};
findByPhone(Phone,[H|T]) ->
  case findPhoneForUser(Phone, H#contact.phone) of
    true -> {H#contact.fname, H#contact.lname};
    false -> findByPhone(Phone, T)
  end.
findPhoneForUser(_,[]) -> false;
findPhoneForUser(Phone, [Phone|_]) -> true;
findPhoneForUser(Phone, [_|T]) -> findPhoneForUser(Phone, T).

但是 mailphone 都是唯一值,所以只要找到一个,函数就完成了。对于 city,搜索可以产生多个 return 值,因此它必须收集所有匹配项。

如何处理这个问题?我考虑过列表理解,例如:

{X,Y} || X<-H#contact.fname, Y<-H#contact.lname, City=:=H#contact.city

但它将 return 来自单个 ASCII 代码的元组:/

您可以使用列表理解。假设您的地址簿存储在一个名为 AddressBook 的变量中,而您要匹配的城市存储在一个名为 City 的变量中,以下将起作用:

[{C#contact.fname, C#contact.lname} || C <- AddressBook, C#contact.city == City].

另请注意,您可以使用 lists:keyfind/3 function:

简化 findByPhone 函数
findByPhone(Phone, AddressBook) ->
    case lists:keyfind(Phone, #contact.phone, AddressBook) of
        #contact{fname=Fname, lname=Lname} -> {Fname, Lname};
        false -> {error, not_found}
    end.

之所以可行,是因为记录实际上是元组。用作 lists:keyfind/3 的第二个参数的构造 #contact.phone 提供基础元组的 phone 字段的元素编号。事实上,您可以使用这种方法编写支持记录中任何唯一字段的单个非导出 find 函数,然后为每个此类可搜索字段编写导出函数:

find(Value, Field, AddressBook) ->
    case lists:keyfind(Value, Field, AddressBook) of
        #contact{fname=Fname, lname=Lname} -> {Fname, Lname};
        false -> {error, not_found}
    end.

findByPhone(Phone, AddressBook) -> find(Phone, #contact.phone, AddressBook).
findByMail(Mail, AddressBook) -> find(Mail, #contact.mail, AddressBook).