Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Created May 26, 2010 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/414082 to your computer and use it in GitHub Desktop.
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
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)
#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