Created
May 26, 2010 04:54
-
-
Save panesofglass/414082 to your computer and use it in GitHub Desktop.
F# port of simplegraph.py from Chapter 2 of O'Reilly's Programming the Semantic Web
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace SemanticWeb | |
open System | |
open System.Collections.Generic | |
open System.IO | |
open System.Text | |
open Kent.Boogaart.KBCsv | |
/// A simple triple store | |
type SimpleGraph(spo: Map<string, Map<string, Set<string>>>, | |
pos: Map<string, Map<string, Set<string>>>, | |
osp: Map<string, Map<string, Set<string>>>) = | |
/// Adds a triple to the specified index. | |
let addToIndex index (a, b, c) = | |
// TODO: Could this be simpler with Active Patterns? | |
match Map.tryFind a index with | |
| None -> index |> Map.add a (Map.ofList [(b, Set.ofList [c])]) | |
| Some(indexA) -> match Map.tryFind b indexA with | |
| None -> index |> Map.add a (indexA |> Map.add b (Set.ofList [c])) | |
| Some(indexB) -> index |> Map.add a (indexA |> Map.add b (indexB |> Set.add c)) | |
/// Removes a triple from the specified index. | |
let removeFromIndex (index: Map<string, Map<string, Set<string>>>) (a, b, c) = | |
try | |
let bs = index.[a] | |
let cset = bs.[b] | |
let newCset = cset |> Set.remove c | |
let newBs = bs |> if Set.isEmpty newCset then Map.remove b else Map.add b newCset | |
index |> if Map.isEmpty newBs then Map.remove a else Map.add a newBs | |
with | |
| :? KeyNotFoundException -> index | |
/// Retrieves tripes based on a filter. (None, None, None) returns all. | |
let triples (sub, pred, obj) = | |
// TODO: Could this be simpler with Active Patterns? | |
try | |
seq { match sub with | |
| Some(s) -> match pred with | |
| Some(p) -> match obj with | |
| Some(o) -> if spo.[s].[p].Contains(o) then yield (s, p, o) | |
| None -> for retObj in spo.[s].[p] do yield (s, p, retObj) | |
| None -> match obj with | |
| Some(o) -> for retPred in osp.[o].[s] do yield (s, retPred, o) | |
| None -> for KeyValue(retPred, objSet) in spo.[s] do | |
for retObj in objSet do | |
yield (s, retPred, retObj) | |
| None -> match pred with | |
| Some(p) -> match obj with | |
| Some(o) -> for retSub in pos.[p].[o] do yield (retSub, p, o) | |
| None -> for KeyValue(retObj, subSet) in pos.[p] do | |
for retSub in subSet do | |
yield (retSub, p, retObj) | |
| None -> match obj with | |
| Some(o) -> for KeyValue(retSub, predSet) in osp.[o] do | |
for retPred in predSet do | |
yield (retSub, retPred, o) | |
| None -> for KeyValue(retSub, predSet) in spo do | |
for KeyValue(retPred, objSet) in predSet do | |
for retObj in objSet do | |
yield (retSub, retPred, retObj) | |
} | |
with | |
| :? KeyNotFoundException -> Seq.empty | |
/// A simple triple store | |
new() = SimpleGraph(Map.empty, Map.empty, Map.empty) | |
/// Add a new triple | |
member this.Add(sub, pred, obj) = | |
let newSpo = addToIndex spo (sub, pred, obj) | |
let newPos = addToIndex pos (pred, obj, sub) | |
let newOsp = addToIndex osp (obj, sub, pred) | |
SimpleGraph(newSpo, newPos, newOsp) | |
/// Remove a triple | |
member this.Remove(sub, pred, obj) = | |
let removeFromIndices (oldSpo, oldPos, oldOsp) (delSub, delPred, delObj) = | |
let newSpo = removeFromIndex oldSpo (delSub, delPred, delObj) | |
let newPos = removeFromIndex oldPos (delPred, delObj, delSub) | |
let newOsp = removeFromIndex oldOsp (delObj, delSub, delPred) | |
(newSpo, newPos, newOsp) | |
let trips = triples (sub, pred, obj) | |
let newSpo, newPos, newOsp = trips |> Seq.fold (removeFromIndices) (spo, pos, osp) | |
SimpleGraph(newSpo, newPos, newOsp) | |
/// Load triples from a csv file | |
member this.Load(filename: string) = | |
let loader (graph: SimpleGraph) (line: string []) = | |
graph.Add(line.[0], line.[1], line.[2]) | |
use rdr = new CsvReader(filename, Encoding.UTF8) | |
rdr.DataRecordsAsStrings |> Seq.fold loader (SimpleGraph()) | |
/// Save triples back to a csv file | |
member this.Save(filename: string) = | |
let trips = triples (None, None, None) | |
|> Seq.map (fun (sub, pred, obj) -> [| sub; pred; obj |]) | |
use wtr = new CsvWriter(filename, Encoding.UTF8) | |
trips |> Seq.iter (wtr.WriteDataRecord) | |
/// Queries triples based on options. | |
member this.Triples(sub, pred, obj) = triples(sub, pred, obj) | |
/// Retrieves a single value from a triple using None. All other values must be specified. | |
member this.Value(sub, pred, obj) = | |
triples (sub, pred, obj) | |
|> Seq.pick (fun (retSub, retPred, retObj) -> | |
if Option.isNone sub then Some(retSub) | |
elif Option.isNone pred then Some(retPred) | |
elif Option.isNone obj then Some(retObj) | |
else None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#r "..\KBCsv-1.2.0.0-bin\Kent.Boogaart.KBCsv.dll" | |
#load "SimpleGraph.fs" | |
open SemanticWeb;; | |
printfn "*** SimpleGraph tests ***";; | |
let mutable g = SimpleGraph();; | |
g <- g.Add("blade_runner", "name", "Blade Runner");; | |
g <- g.Add("blade_runner", "name", "Blade Runner");; | |
g <- g.Add("blade_runner", "release_date", "June 25, 1982");; | |
g <- g.Add("blade_runner", "directed_by", "Ridley Scott");; | |
g.Triples(None, None, None) |> Seq.toList |> printfn "%A";; | |
g.Triples(Some("blade_runner"), None, None) |> Seq.toList |> printfn "%A";; | |
g.Triples(Some("blade_runner"), Some("name"), None) |> Seq.toList |> printfn "%A";; | |
g.Triples(Some("blade_runner"), Some("name"), Some("Blade Runner")) |> Seq.toList |> printfn "%A";; | |
g.Triples(None, Some("name"), Some("Blade Runner")) |> Seq.toList |> printfn "%A";; | |
g.Triples(None, None, Some("Blade Runner")) |> Seq.toList |> printfn "%A";; | |
printfn "\n*** Business Triples ***";; | |
let bg = SimpleGraph().Load(@"C:\Users\ryan\dev\SemanticWeb\business_triples.csv");; | |
let ibanks = bg.Triples(None, Some("industry"), Some("Investment Banking")) |> Seq.take 10 |> Seq.toList;; | |
printfn "%A" ibanks;; | |
printfn "\n*** Take one value ***";; | |
let labIndustry = bg.Value(Some("LAB"), Some("industry"), None);; | |
printfn "%A" labIndustry;; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment