有没有人拥有或知道F#中可用的持久前缀trie?

时间:2012-07-09 13:21:36

标签: f# ocaml trie prefix-tree persistent-data

F#的Map和Set的性能非常缺乏我的特定应用程序。看起来好的前缀trie会在我的解释器中提升性能,特别是在名称符号查找方面。唯一需要注意的是,它必须对添加和查找操作非常有效(特别是当键是字符串时),并且对于持久性是不可变的(意味着非破坏性更新)。

如果没有这样的野兽,OCaml或Haskell的参考实现将帮助我开始使用它。

非常感谢你!

3 个答案:

答案 0 :(得分:3)

关闭此帖子(见问题评论):

Haskell implementation

OCaml implementation

答案 1 :(得分:2)

  

看起来好的前缀trie会在我的解释器中提升性能,特别是在名称符号查找方面。唯一需要注意的是,它必须对添加和查找操作非常有效(特别是当键是字符串时),并且对于持久性是不可变的(意味着非破坏性更新)。

你的资格赛"高效率"和#34;坚持不懈"相互排斥。持久性数据结构(通常)本身效率非常低,通常比命令式数据结构慢10倍。

如果你想要一个带有符号键的快速字典,那么你需要一个符号表。您的公共API使用符号作为字符串,但这些符号通过哈希表在内部转换为小正整数并返回。然后,将符号作为键的字典表示为用于表示符号的整数索引的数组。

我发表了一篇关于符号表here的文章。

答案 2 :(得分:0)

所以,我刚从OCaml移植了一个。不幸的是,就tryFind而言,它比标准Map运行得慢。我在这个帖子中问为什么 - Why is my Trie lookup slower than that of the standard F# Map's?

这是代码 -

[<RequireQualifiedAccess>]
module Trie

type Node<'k, 'v when 'k : comparison> =
    { TrieMap : Map<'k, Node<'k, 'v>>
      TrieKvp : ('k list * 'v) option }
    member inline x.IsEmpty = x.TrieKvp.IsNone && x.TrieMap.IsEmpty

let inline make map kvp =
    { TrieMap = map
      TrieKvp = kvp }

let inline makeEmpty () : Node<'k, 'v> = make Map.empty None

let inline isEmpty (node : Node<'k, 'v>) = node.IsEmpty

let rec tryFind (key : 'k list) node =
    match key with
    | [] ->
        match node.TrieKvp with
        | Some (_, value) -> Some value
        | None -> None
    | keyHead :: keyTail ->
        let optSubNode = Map.tryFind keyHead node.TrieMap
        match optSubNode with
        | Some subNode -> tryFind keyTail subNode
        | None -> None

let inline containsKey key node =
    (tryFind key node).IsSome

let rec addInternal (key : 'k list) value node =
    match key with
    | [] -> make node.TrieMap (Some (key, value))
    | keyHead :: keyTail ->
        let newTrie =
            match Map.tryFind keyHead node.TrieMap with
            | Some subTrie -> subTrie
            | None -> makeEmpty ()
        let newTrie2 = addInternal keyTail value newTrie
        make (Map.add keyHead newTrie2 node.TrieMap) node.TrieKvp

let inline add key value node =
    addInternal key value node

let rec addMany kvps node =
    if Seq.isEmpty kvps then node
    else
        let kvpHead = Seq.head kvps
        let kvpTail = Seq.skip 1 kvps
        let newTrie = add (fst kvpHead) (snd kvpHead) node
        addMany kvpTail newTrie

let inline ofList kvps =
    addMany kvps (makeEmpty ())

let inline ofListBy by kvps =
    let pairs = List.map by kvps
    ofList pairs

let rec foldInternal folder rev node state =
    match node.TrieKvp with
    | Some (_, value) -> folder (Map.fold (fun state key value -> foldInternal folder (key :: rev) value state) state node.TrieMap) (List.rev rev) value
    | None -> Map.fold (fun state key value -> foldInternal folder (key :: rev) value state) state node.TrieMap

let inline fold folder state node =
    foldInternal folder [] node state

let rec map (mapper : 'k list -> 'v -> 'a) (node : Node<'k, 'v>) : Node<'k, 'a> =
    match node.TrieKvp with
    | Some (key, value) -> make (Map.map (fun _ value -> map mapper value) node.TrieMap) (Some (key, mapper key value))
    | None -> make (Map.map (fun _ value -> map mapper value) node.TrieMap) None

let inline toValueList node =
    fold (fun state _ value -> value :: state) [] node

let inline singleton (key, value) =
    add key value (makeEmpty ())