我正在尝试在F#中实现BST。自从我开始使用F#以来,我想寻求帮助。
我有一个简单的测试;
[<Fact>]
let ``Data is retained`` () =
let treeData = create [4]
treeData |> data |> should equal 4
treeData |> left |> should equal None
treeData |> right |> should equal None
使用区别联合的树类型
type Tree<'T> =
| Leaf
| Node of value: 'T * left: Tree<'T> * right: Tree<'T>
将数据节点插入树中的递归函数
let rec insert newValue (targetTree: Tree<'T>) =
match targetTree with
| Leaf -> Node(newValue, Leaf, Leaf)
| Node (value, left, right) when newValue < value ->
let left' = insert newValue left
Node(value, left', right)
| Node (value, left, right) when newValue > value ->
let right' = insert newValue right
Node(value, left, right')
| _ -> targetTree
现在我在创建功能时遇到问题。我有这个:
let create items =
List.fold insert Leaf items
和由此产生的错误:
FS0001类型不匹配。期待一个 ''a->树<'a>->'a'但给定一个 ''a-> Tree <'a>-> Tree <'a>'类型'a'和'Tree <'a>'无法统一。
答案 0 :(得分:4)
List.fold
documentation的类型签名显示为:
List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
让我们打开包装。第一个参数是类型'State -> 'T -> 'State
的函数。这意味着它需要一个状态和一个T类型的参数,并返回一个新状态。在这里,状态是您的Tree
类型:从基本的Leaf
开始,您正在逐步构建树。 List.fold
的第二个参数是初始状态(在这种情况下为Leaf
),第三个参数是要折叠的T类型的项目列表。
您的第二个和第三个参数是正确的,但是您的第一个参数与List.fold
期望的签名不一致。 List.fold
想要的类型为'State -> 'T -> 'State
,在您的情况下为Tree<'a> -> 'a -> Tree<'a>
。也就是说,该函数将树作为其 first 参数,并将单个项目作为其 second 参数。但是您的insert
函数以相反的方式获取参数(项目作为第一个参数,树作为第二个参数)。
我将在此处暂停说明,根据惯用F#的样式规则,您的insert
函数正确,并且您不应更改顺序其参数。在编写用于处理集合的函数时,您始终希望将集合作为最后一个参数,以便可以编写类似tree |> insert 5
的内容。因此,我强烈建议您不要更改insert
函数采用的参数的顺序。
因此,如果您不应该更改insert
函数的参数顺序,但是它们与List.fold
的使用顺序不正确,您会怎么做?简单:您可以创建一个匿名函数,其参数会翻转,以便可以将insert
与List.fold
一起使用:
let create items =
List.fold (fun tree item -> insert item tree) Leaf items
现在,我们将进一步进行概括。实际上,在F#编程中很常见的是,发现两参数函数的参数在大多数情况下都是正确的方式,而在一个特定的用例中却是错误的方式。为了解决该问题,有时创建一个名为flip
的通用函数很有用:
let flip f = fun a b -> f b a
然后,您可以像这样编写create
函数:
let create items =
List.fold (flip insert) Leaf items
有时使用flip
会使代码更多令人困惑,而不是使 less 令人困惑,因此,我不建议您一直使用它。 (这也是为什么F#标准库中没有flip
函数的原因:因为它并不总是最好的解决方案。而且由于编写自己很琐碎,因此标准库中的不足并不重要) 。但是有时使用flip
会使代码更简单,我认为这是其中一种情况。
P.S。 flip
函数也可以这样写:
let flip f a b = f b a
此定义与我在主要示例中使用的let flip f = fun a b -> f b a
定义相同。你知道为什么吗?