Можно ли передать параметры модулям F#?
Я новичок в 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 определена и передана карри Tree
функции.
Сейчас очень много дублирования кода.
Возможно ли это как-то сделать? (Псевдокод)
module XmlTree = Tree with getChildren = fun (node : XElement) -> node.Elements()
2 ответа
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
подкласс.
Вы делаете это не с модулями, а с дженериками, например
РЕДАКТИРОВАТЬ:
type tree<'t>(Children:seq<tree<'t>>)=
member x.isLeaf() = Seq.isEmpty (Children )
member x.getLeaves() =
getDescendants Children
|> Seq.filter (fun x -> x.isLeaf())
Я не учил опекунов, но этого должно быть достаточно. Кроме того, некоторые аннотации типов не требуются, но показывают, что происходит