4

我是 F# 新手,正在学习基础知识。

我有两个模块。一种用于树数据结构的通用结构,称为Tree

module Tree

let rec getDescendants getChildren node  = 
    seq { yield node
          for child in getChildren node do 
            yield! getDescendants getChildren child }

let isLeaf getChildren node = Seq.isEmpty (getChildren node)

let getLeaves getChildren node = getDescendants getChildren node  
                               |> Seq.filter (isLeaf getChildren)

如您所见,所有函数都有一个getChildren参数,该参数是一个枚举给定类型节点的子节点的函数。

第二个模块处理 XML 树的更具体的情况:

module XmlTree

open System.Xml.Linq

let getXmlChildren (node : XElement) = node.Elements()

let getDescendants = Tree.getDescendants getXmlChildren 
let getLeaves = Tree.getLeaves getXmlChildren 
let isLeaf = Tree.isLeaf getXmlChildren 

getXmlChildren为 XML 节点定义了一个特定的函数并将其传递给 curriedTree函数。

现在有大量的代码重复。

有没有可能做以下事情?(伪代码)

module XmlTree = Tree with getChildren = fun (node : XElement) -> node.Elements()
4

2 回答 2

11

F# 不支持仿函数,因此您不能将参数传递给 F# 模块。在您的示例中,将生成节点子节点的函数传递给对象构造函数就足够了:

type Tree<'T>(childFn: 'T -> 'T seq) =
    let getChildren = childFn

    member x.getDescendants node  = 
        seq { yield node
              for child in getChildren node do 
                yield! x.getDescendants child }

    member x.isLeaf node = node |> getChildren |> Seq.isEmpty
    member x.getLeaves node = node |> x.getDescendants |> Seq.filter x.isLeaf

// Type usage
open System.Xml.Linq
let xmlTree = new Tree<XElement>(fun x -> x.Elements())

对于更复杂的情况,继承是要走的路。特别是,您可以Tree<'T>使用抽象成员声明为抽象类getChildren,并在子类中覆盖该方法XmlTree

于 2012-05-14T11:22:12.480 回答
5

例如,您不用模块执行此操作,而是使用泛型执行此操作

编辑

type tree<'t>(Children:seq<tree<'t>>)=

    member x.isLeaf()  = Seq.isEmpty (Children )

    member x.getLeaves() = 
        getDescendants Children
        |> Seq.filter (fun x -> x.isLeaf())

我省略了 getdescendants,但这应该足够了。此外,某些类型注释不是必需的,但会显示正在发生的事情

于 2012-05-14T09:58:42.380 回答