如何为F#中不能为空的列表定义数据结构?

时间:2017-04-18 13:03:21

标签: list data-structures f#

最近,我开始学习F#,我正在努力与歧视的工会合作,列出结构。我找到了一个练习,我需要做以下事情:

'定义NonEmptyList<' a>数据结构,可以表示永远不会为空的列表

尝试

let NonEmptyList (input : List<'a>) = 
   match input with
   | [] -> failwith "List canot be empty"
   | [x] -> x

不确定是否使用let-keyword正确实现了这一点。我......我是否需要这种结构:

type NonEmptyList<'a> = struct
    | List

编辑1

type NonEmptyList<'T> = 
  | Cons of 'T * List<'T>
  | Single of List<'T>

编辑2

let list1 : NonEmptyList<'T> = Single[1..10]
let list2 : NonEmptyList<'T> = Cons([1..3],[1..3]) 

我收到一个解析器错误:此构造导致代码不如类型注释所指示的那样通用。类型变量&t; T已经被改为类型&#39;列表。

2 个答案:

答案 0 :(得分:5)

首先,我对任务的阅读是给出一个类型定义(使用type)而不是一个检查列表是否为空的函数。

要解决此问题,最好先了解普通的F#列表:

type List<'T> = 
  | Cons of 'T * List<'T>
  | Empty 

此处,列表为空(由Empty值表示)或包含类型'T的值,后跟另一个由List<'T>表示的列表。这样,您就可以创建:

let nop = Empty                      // Empty list
let oneTwo = Cons(1, Cons(2, Empty)) // List containing 1 and 2 

因此,要回答这个问题,您需要一个与List<'T>非常相似的定义,但不应该创建Empty列表。你可以从这样的事情开始:

type NonEmptyList<'T> = 
  | Cons of 'T * NonEmptyList<'T>

现在我删除了Empty,我们再也无法创建空列表了 - 但这并不能解决问题,因为现在你永远不能结束任何列表。我不会给出一个完整的答案,以避免剧透,因为我认为解决这个问题是练习的重点,但你需要这样的东西:

type NonEmptyList<'T> = 
  | Cons of 'T * NonEmptyList<'T>
  | // One more case here

最后一种情况可以是什么,以便它不包含另一个List<'T>(从而让我们结束一个列表),但不是Empty,所以我们不能创建空列表?

答案 1 :(得分:3)

非空列表由头部和可选的非空尾部组成。您可以将此类型表示为单个案例联合:

type NEL<'a> = NEL of 'a * NEL<'a> option
let l = NEL(1, Some(NEL(2, None)))

或记录类型:

type NEL<'a> = {head : 'a; tail : NEL<'a> option}
let l = { head = 1; tail = Some({head = 2; tail = None})}