如何从列表中查找类型的值

时间:2014-09-21 13:34:06

标签: list recursion f#

所以我在学习如何使用函数式编程的早期阶段,当我尝试将字符串与列表中的字符串进行比较时遇到了这个问题,这样我就可以得到匹配的模式。

这是我的代码:

F#代码

type name = string;;
type number = string;;
type sex = string;;
type year = int;;
type interest = string list;;
type criteria = (sex * year * interest) list;;
type Register = (name * number * criteria) list;;

let reg = [("Lars","28551086",("male",1992,["soccer";"golf"])); ("Hanne","28598653",("female",1989,["cooking";"jewelry"]));
           ("Viktor","26587297",("male",1973,["clothes";"soccer"])); ("Henrik","22157864",("male",1985,["internet";"facebook"]));
           ("Lotte","23589462",("female",1997,["bombing";"internet"])); ("Susanne","25896742",("female",1923,["soccer";"cooking"]));
           ("Marie","22658943",("female",1975,["clothes";"jewelry"])) ];;

let rec findYear n = function
    | [] -> failwith("No person with that year is registrered")
    | (name,_,(_,n',_))  when n = n' -> name
    | (name,_,(_,n',_))::tail  when n <> n' -> findYear(tail);;

我想要做的是检索 reg 中与我搜索的名称相同的所有人。 所以F#Interactive调用可以是:

findYear 1992;;

然后它应该给我那一年的人的详细信息。我不知道如何搜索我的 reg

2 个答案:

答案 0 :(得分:3)

我想你在这里忘记了n(以及列表的尾部):

let rec findYear n = function
    | [] -> failwith("No person with that year is registrered")
    | (name,_,(_,n',_))  when n = n' -> name // forgot tail
    | (name,_,(_,n',_))::tail  when n <> n' -> findYear(tail) // forgot n here

(应该有错误

试试这个:

let rec findYear n = function
    | [] -> failwith("No person with that year is registrered")
    | ((name,_,(_,n',_))::_)  when n = n' -> name
    | ((_,_,(_,n',_))::tail)  when n <> n' -> findYear n tail

使这更好一点

您不需要再次检查

如果第二种模式不匹配,你不需要重新检查年份:

let rec findYear n = function
    | [] -> failwith("No person with that year is registrered")
    | ((name,_,(_,n',_))::_)  when n = n' -> name
    | (_::tail) -> findYear n tail

option优于异常

你处理今年没有找到一个人的情况的方式告诉我们你的功能是&#34;部分&#34; (不会为每个输入返回) - 所以只需使用option

再次使其完全合并
let rec findYear n = function
    | [] -> None
    | ((name,_,(_,n',_))::_) when n = n' -> Some name
    | (_::tail) -> findYear n tail

这不会抛出并告诉用户:&#34;嘿,我可能会失败,所以更好地处理这个!&#34;

使用记录/ ADT

虽然你的元组很好,但它们并不是真的可读(很难检查你的模式是否正常) - 为什么不使用记录和代数数据类型:

type Name = string
type Number = string
type Gender = Male | Female // add more if you need
type Year = int
type Interests = string list
type Criteria = { gender : Gender; year : Year; interests : Interests }
type Register = { name : Name; number : Number; criteria : Criteria }

let reg = 
  [ { name = "Lars"
    ; number = "28551086"
    ; criteria = { gender = Male; year = 1992; interests = ["soccer";"golf"] }
    }
    // ...
]

并使用此:

let rec findYear n = 
    function
    | [] -> None
    | (reg::_) when reg.criteria.year = n' 
       -> Some reg
    | (_::regs) 
       -> findYear n regs

使用List模块

你在这里做的是一个非常的常见模式,它已经实现了(List.tryFind) - 那么为什么不使用呢?

let findYear n = 
    let hasYear (reg : Register) = reg.criteria.year = n
    List.tryFind hasYear

当然,如果您还没有真正了解部分应用程序,则可以添加缺少的参数:

let findYear n regs = 
    let hasYear (reg : Register) = reg.criteria.year = n
    List.tryFind hasYear regs

最后让我们给这个更好的名字

这当然只是我不喜欢findYear如果你真的找到了注册

// rest is the same
type Registration = { name : Name; number : Number; criteria : Criteria }

let firstRegistrationWithYear year = 
    let hasYear (reg : Register) = reg.criteria.year = year
    List.tryFind hasYear

查找一年的所有注册

let filterRegistrationWithYear year = 
    let hasYear (reg : Register) = reg.criteria.year = year
    List.filter hasYear

或者如果你想要一个(尾递归)实现使用延续传递样式(另一个答案有累加器aproach):

let filterYear n regs = 
    let rec filter regs cont =
       match regs with
       | [] -> cont []
       | (reg::regs) when reg.criteria.year = n' 
          -> filter regs (fun res -> reg::res |> cont)
       | (_::regs) 
          -> filter regs cont
    filter regs id 
备注:

我不建议您自己实施这类内容 - 最好使用List中提供的内容(例如,它更高效,因为我试图向您展示怎么做CPS式)

答案 1 :(得分:1)

如果要使用递归,可以添加其他参数(累加器),以收集结果:

let rec findYear n acc = function
    | [] -> acc
    | ((name,_,(_,n',_)) as h)::tail  when n = n' -> findYear n (h::acc) tail 
    | h::tail -> findYear n acc tail

并以这种方式称呼它:

findYear 1973 [] reg

或者您可以使用&#39;过滤器&#39;列表库函数中的函数:

let findYear' n lst =
    lst |> List.filter (fun (name,_,(_,n',_)) -> n = n')

并以这种方式称呼它:

findYear' 1973 reg