F#嵌套列表问题

时间:2015-03-19 19:22:34

标签: list f# nested

我有一个列表,例如

let stdCourseList = [(10,[100;101;102]); (11,[101]); (14,[]); (12,[100;101])];;

这是一个包含学生ID和课程ID列表的列表

我想实现一个能够计算学生正在学习的课程数量的功能。 比如我这样做

numbOfCourse(stdCourseList,10) 

我会得到3,因为身份证10的学生需要3门课程

到目前为止,我有这段代码:

let rec numbOfCourse = function
|(([], id)) -> 0
|(((id2:int,(y:int)::ys)::xs),id) ->
      if id=id2 then 1 //should a code to count the list of the course
      else numbOfCourse(xs,id);;

但是当我运行它时我得到了这个错误

   let rec numbOfCourse = function
  ------------------------^^^^^^^^

  stdin(1471,25): warning FS0025: Incomplete pattern matches on this
  expression. For example, the value '([(_,[])],_)' may indicate a case not 
  covered by the pattern(s).

我真的不明白为什么我得到这个错误以及错误意味着什么。有人可以帮我解释一下吗?处理嵌套列表的最佳方法是什么?

这部分是错的吗?

(((id2:int,(y:int)::ys)::xs),id)

感谢。

2 个答案:

答案 0 :(得分:1)

你可以这样做:

let getCourseCount idx l =
    l
    |> Seq.filter (fun (i, _) -> i = idx)
    |> Seq.map (fun (_, courses) -> courses |> Seq.length)
    |> Seq.sum

getCourseCount函数具有签名'a -> seq<'a * #seq<'c>> -> int when 'a : equality,因此它非常通用。它将任何序列作为输入,只要每个元素是一个元组,第二个元素是另一个序列。

stdCourseList值符合输入类型,因为它是(int * int list) list,而listseq

filter函数仅选择第一个元素等于输入idx的元组。

可能有多个元素通过过滤器(例如,如果 10 有两个条目),但对于每个元素,map函数计算课程数量使用Seq.length

由于可能存在重复的条目,因此可以使用Seq.sum将这些数字添加到一起。

FSI样本会议:

> stdCourseList |> getCourseCount 10;;
val it : int = 3
> stdCourseList |> getCourseCount 11;;
val it : int = 1
> stdCourseList |> getCourseCount 14;;
val it : int = 0
> stdCourseList |> getCourseCount 12;;
val it : int = 2

答案 1 :(得分:1)

错误消息表明您的模式并非详尽无遗。示例([(_,[])],_)表示您的函数不处理课程列表由单个元素组成的情况,并且课程列表中的单个元素具有空列表作为其第二个成员。

整个模式是一个元组。元组模式的第二部分将匹配任何值;这就是下划线的含义。元组的第一部分是带有单个元素的列表:

[(_,[])]

单个元素是一个元组:

(_,[])

该元组的第一部分将匹配任何值;第二部分与空列表匹配。

如果你知道你永远不会得到这样的课程列表,你可以忽略警告,但我发现当编译器找到一个不完整的模式匹配时添加失败案例会更好,因为编译器会通知我我未能涵盖的其他案例。例如,如果您将此行添加到您的函数中,您将收到另一个警告:

| ([(_,[])],_) -> failwith "unexpected pattern"

警告是

  

此表达式上的不完整模式匹配。例如,值'([; ],_)'可能表示模式未涵盖的情况。

一般来说,我发现尝试编写分解复杂结构的复杂模式(如列表元组列表)是危险的。最好一次处理一个层:

let rec f = function
            | [], _ -> //your base case here
            | (studentId, courseList) :: tail, idParameter when studentId = idParameter -> //one recursive case here
            | _ :: tail, id -> // another recursive case here

如果您需要分解课程ID列表,则可以使用单独的match表达式来执行此操作,作为替换one recursive case here的表达式的一部分。 “分而治之”将使您更容易推理您的代码。

但是,如果由于您的课程要求而不是一个选项,那么在一个列表中分解此结构应该是易于处理的。只需将每个列表视为具有两种可能的状态:空和非空。这将减少排列的数量。

最后,您可能希望阅读有关辅助函数以及如何编写尾递归函数的内容,尽管您可能仍然处于课程中不应该使用它们的位置。一个重要的概念是“累加器”。