As always, no warranties or implied fitness; use at your own risk. Formatted for blog width.
Code:
module SemanticNet
open System.Collections.Generic
// I'm a fool for syntactic sugar.
type internal Rel = string*string*string
type internal RelHash = HashSet<Rel>
type internal RelDict = Dictionary<string,RelHash>
type internal FinderVal = bool*(RelHash option)
// Holds the relationships.
type Graph () =
// Holds all the relationships.
let relHash = new RelHash()
// Maps a node in each position
// to the relationships for that node.
let n0ToRelHash = new RelDict()
let n1ToRelHash = new RelDict()
let n2ToRelHash = new RelDict()
// Computation expression builder for finds.
let relFinder = RelFinder()
// Internal function for finds.
let relFinderComp (d:RelDict)
(so:string option)
(bhi:FinderVal) =
match so with
| None -> bhi
| Some(s) ->
match d.TryGetValue s with
| (true,h) ->
match bhi with
// Copy the first hash found.
| (true,None) -> (true,Some(RelHash(h)))
| (true,Some(hi)) ->
hi.IntersectWith(h)
(true,Some(hi))
| _ -> failwith "Internal program error."
| _ -> (false,None)
// // For a slightly more efficient first call,
// // this can be used.
//
// let relFinderCompFst (d:RelDict)
// (so:string option) =
// match so with
// | None -> (true,None)
// | Some(s) ->
// match d.TryGetValue s with
// | (true,h) -> (true,Some(RelHash(h)))
// | _ -> (false,None)
// Add a relationship to a dictionary.
let tryAddRel (d:RelDict) k r =
match d.TryGetValue k with
| (true,h) ->
h.Add r |> ignore
| _ ->
let h = new RelHash()
h.Add r |> ignore
d.Add(k,h)
// Remove a relationship from a dictionary.
let tryRemoveRel (d:RelDict) k r =
match d.TryGetValue k with
| (true,h) ->
let rmv = h.Remove r
// Clean up empty hash tables.
if (h.Count=0) then d.Remove k |> ignore
rmv
| _ -> false
// Add a relationship.
member this.Add r =
match relHash.Add r with
| false -> r
| _ ->
let (s0,s1,s2) = r
tryAddRel n0ToRelHash s0 r
tryAddRel n1ToRelHash s1 r
tryAddRel n2ToRelHash s1 r
r
// Add a bunch of relationships.
member this.Add (e:IEnumerable<Rel>) =
for r in e do
this.Add r |> ignore
// Return a hash of all relationships.
member this.All =
RelHash(relHash)
// Return true if a relationship exists.
member this.Exists r =
relHash.Contains r
// Return a hash option of the matched relationships.
// Note: Uses string option; None acts as a wildcard in Find.
member this.Find (s0,s1,s2) =
match
(relFinder {
// // See note above.
// let! r0 = relFinderCompFst n0ToRelHash s0
let! r0 = relFinderComp n0ToRelHash s0 (true,None)
let! r1 = relFinderComp n1ToRelHash s1 r0
return relFinderComp n2ToRelHash s2 r1 }) with
// Three wildcards.
| (true,None) -> Some(RelHash(relHash))
// Something found.
| (true,h) -> h
// Nothing found.
| _ -> None
// Return a hash option of the matched relationships.
// Note: Uses strings; explicit wildcard.
member this.FindW w (s0,s1,s2) =
this.Find (
(if s0=w then None else Some(s0)),
(if s1=w then None else Some(s1)),
(if s2=w then None else Some(s2)))
// Remove a relationship.
member this.Remove r =
match relHash.Remove r with
| true ->
let (s0,s1,s2) = r
tryRemoveRel n0ToRelHash s0 r |> ignore
tryRemoveRel n1ToRelHash s1 r |> ignore
tryRemoveRel n2ToRelHash s2 r |> ignore
true
| _ -> false
// Remove a bunch of relationships.
member this.Remove (e:IEnumerable<Rel>) =
for r in e do
this.Remove r |> ignore
// + operator adds a relationship, returns graph.
static member (+) ((g:Graph),(r:Rel)) =
g.Add r |> ignore
g
// - operator removes a relationship, returns graph.
static member (-) ((g:Graph),(r:Rel)) =
g.Remove r |> ignore
g
// Computation expression builder for finds.
and internal RelFinder () =
// "let!" function.
member this.Bind (((b,h):FinderVal),
(f:FinderVal->FinderVal)) =
match b with
| true -> f (b,h)
| _ -> (b,h)
// "return" function.
member this.Return bh = bh
And test:
open SemanticNet
let g0 = Graph()
// Add some relationships.
g0 +
("mammal","isA", "animal") +
("dog", "isA", "mammal") +
("dog", "isA", "pet") +
("dog", "hasA","tag") +
("cat", "isA", "pet") |> ignore
// Sundry experiments.
let h0 = g0.Find(None,(Some "isA"),(Some "pet"))
let h1 = g0.Find((Some "dog"),None,None)
g0 - ("cat","isA","pet") |> ignore
let h2 = g0.Find(None,(Some "isA"),(Some "pet"))
g0 + ("cat","isA","pet") |> ignore
let h3 = g0.Find(None,(Some "isA"),(Some "pet"))
let b0 = g0.Exists("cat","isA","pet")
let b1 = g0.Exists("tiger","isA","pet")
let find = g0.FindW ""
let h4 = find("dog","","")
if h4.IsSome then g0.Remove h4.Value
let h5 = find("dog","","")
printfn "All done!"
No comments:
Post a Comment