Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 667dd6358824d78c97070d439c5776992f98fe3e @ianxm committed Feb 22, 2012
Showing with 369 additions and 0 deletions.
  1. +164 −0 DeepHash.hx
  2. +27 −0 LICENSE
  3. +28 −0 README
  4. +130 −0 TestDeepHash.hx
  5. +11 −0 TestSuite.hx
  6. +6 −0 haxelib.xml
  7. +3 −0 test.hxml
164 DeepHash.hx
@@ -0,0 +1,164 @@
+/**
+ DeepHash is a tree data structure made up of nested hashes. Items
+ within the tree are located using a path, which is a list of keys.
+ DeepHash can compute values for non-leaf nodes based on their children.
+ */
+class DeepHash<K,V> extends Node<K,V>
+{
+ public var accum :V->V->V; // accumulator function
+
+ /**
+ create a new tree
+ */
+ public function new()
+ { // root doesn't have a parent or a key
+ super(null, null);
+ val = null;
+ children = null;
+ accum = function(a,b){ return a; }
+ }
+
+ /**
+ set a value at the specified path. create nodes as needed
+ path variable is modified!
+ */
+ override public function set(path :List<K>, newVal :V, ?accum :V->V->V )
+ {
+ super.set(path, newVal, this.accum);
+ }
+
+ /*
+ pre-order traversal of paths, not lazy
+ */
+ public function getPathIterator()
+ {
+ var paths = new List<List<K>>();
+ if( children != null )
+ for( child in children )
+ child.getPaths(paths);
+ return paths.iterator();
+ }
+
+ /*
+ pre-order traversal of values, not lazy
+ */
+ public function getValuesIterator()
+ {
+ var nodes = new List<V>();
+ if( children != null )
+ for( child in children )
+ child.getValues(nodes);
+ return nodes.iterator();
+ }
+}
+
+
+private class Node<K,V>
+{
+ private var key :K; // paths are made up of keys
+ private var val :V; // node value
+ private var parent :Node<K,V>; // this nodes parent, null for root
+ private var children :List<Node<K,V>>; // list of child nodes, may be null
+
+ /**
+ create a new tree node
+ */
+ public function new(p, k)
+ {
+ parent = p;
+ key = k;
+ val = null;
+ children = null;
+ }
+
+ /**
+ set a value at the specified path. create nodes as needed
+ path variable is modified!
+ */
+ public function set(path :List<K>, newVal :V, ?accum :V->V->V)
+ {
+ if( path.isEmpty() )
+ {
+ val = newVal;
+ return;
+ }
+ val = accum(val, newVal);
+
+ if( children == null )
+ children = new List<Node<K,V>>();
+ var key = path.pop();
+ var child = first(children, function(ii) return ii.key==key);
+ if( child == null )
+ {
+ child = new Node<K,V>(this, key);
+ children.add(child);
+ }
+ child.set(path, newVal, accum);
+ }
+
+ /*
+ get the value at the specified path. return null if not found
+ path variable is modified!
+ */
+ public function get(path :List<K>)
+ {
+ if( path.isEmpty() )
+ return val;
+
+ if( children == null )
+ return null;
+
+ var key = path.pop();
+ var child = first(children, function(ii) return ii.key==key);
+ return if( child==null )
+ null;
+ else
+ child.get(path);
+ }
+
+ /**
+ builds the path from a node
+ */
+ private function getPath(?path :List<K>)
+ {
+ if( path == null )
+ path = new List<K>();
+ path.push(key);
+ if( !Std.is(parent,DeepHash) ) // ignore root
+ parent.getPath(path);
+ return path;
+ }
+
+ /*
+ pre-order traversal of paths, not lazy
+ */
+ private function getPaths(paths :List<List<K>>)
+ {
+ paths.add(getPath());
+ if( children != null )
+ for( child in children )
+ child.getPaths(paths);
+ }
+
+ /*
+ pre-order traversal of values, not lazy
+ */
+ private function getValues(?nodes)
+ {
+ nodes.add(val);
+ if( children != null )
+ for( child in children )
+ child.getValues(nodes);
+ }
+
+ /*
+ utility to find the first item that matches in a list
+ */
+ private static function first<A>(it:Iterable<A>, f:A->Bool)
+ {
+ for( ii in it )
+ if( f(ii) )
+ return ii;
+ return null;
+ }
+}
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012, Ian Martins
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 README
@@ -0,0 +1,28 @@
+DeepHash
+--------
+
+DeepHash is a tree data structure made up of nested hashes. Items
+within the tree are located using a path, which is a list of keys.
+DeepHash can compute values for non-leaf nodes based on their children.
+See tests for examples of use.
+
+Interface
+---------
+
+new()
+ create a new deep hash
+
+set(path :List<K>, newVal :V)
+ set a value at the specified path. create nodes as needed
+ path variable is modified!
+
+get(path :List<K>)
+ get the value at the specified path. return null if not found
+ path variable is modified!
+
+getPathIterator()
+ pre-order traversal of paths, not lazy
+
+getValuesIterator()
+ pre-order traversal of values, not lazy
+
130 TestDeepHash.hx
@@ -0,0 +1,130 @@
+using Lambda;
+
+class TestDeepHash extends haxe.unit.TestCase
+{
+ public function testSetRoot()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([].list(), "root");
+ assertEquals("root", tree.get([].list()));
+ }
+
+ public function testSetChild()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([1].list(), "one");
+ assertEquals("one", tree.get([1].list()));
+ }
+
+ public function testSetChildTwice()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([1].list(), "one");
+ tree.set([2].list(), "two");
+ assertEquals("one", tree.get([1].list()));
+ assertEquals("two", tree.get([2].list()));
+ }
+
+ public function testSetTwoDeep()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([1,2].list(), "one.two");
+ assertEquals("one.two", tree.get([1,2].list()));
+ }
+
+ public function testSiblings()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([1].list(), "one");
+ tree.set([2].list(), "two");
+ assertEquals("one", tree.get([1].list()));
+ assertEquals("two", tree.get([2].list()));
+ }
+
+ public function testSetTwoDeepExistingPath()
+ {
+ var tree = new DeepHash<Int, String>();
+ tree.set([1].list(), "one");
+ tree.set([1,2].list(), "one.two");
+ assertEquals("one", tree.get([1].list()));
+ assertEquals("one.two", tree.get([1,2].list()));
+ }
+
+ public function testSetOtherTypes()
+ {
+ var tree = new DeepHash<String, Float>();
+ tree.set(["one"].list(), 1.1);
+ tree.set(["one","two"].list(), 1.2);
+ assertEquals(1.1, tree.get(["one"].list()));
+ assertEquals(1.2, tree.get(["one","two"].list()));
+ }
+
+ public function testAccum()
+ {
+ var tree = new DeepHash<String, Float>();
+ tree.accum = function(a,b){ return (a==null) ? b : a+b; }
+ tree.set(["one"].list(), 1.1);
+ tree.set(["one","two"].list(), 1.2);
+ assertEquals(2.3, tree.get(["one"].list()));
+ assertEquals(1.2, tree.get(["one","two"].list()));
+ }
+
+ public function testIteratePaths()
+ {
+ var tree = new DeepHash<String, Float>();
+ tree.set(["one"].list(), 1.1);
+ tree.set(["one","two"].list(), 1.2);
+ var iter = tree.getPathIterator();
+ assertEquals('{one}', iter.next().toString());
+ assertEquals('{one, two}', iter.next().toString());
+ assertFalse(iter.hasNext());
+ }
+
+ public function testIterateValues()
+ {
+ var tree = new DeepHash<Int, Float>();
+ tree.set([1].list(), 1.1);
+ tree.set([1,2].list(), 1.2);
+ tree.set([1,3].list(), 1.3);
+ var iter = tree.getValuesIterator();
+ assertEquals(1.1, iter.next());
+ assertEquals(1.2, iter.next());
+ assertEquals(1.3, iter.next());
+ assertFalse(iter.hasNext());
+ }
+
+ public function testIterateValuesEmptyParent()
+ {
+ var tree = new DeepHash<Int, Float>();
+ tree.set([1,2].list(), 1.2);
+ tree.set([1,3].list(), 1.3);
+ var iter = tree.getValuesIterator();
+ assertEquals(null, iter.next());
+ assertEquals(1.2, iter.next());
+ assertEquals(1.3, iter.next());
+ assertFalse(iter.hasNext());
+ }
+
+ public function testIteratorValuesAccum()
+ {
+ var tree = new DeepHash<String, Float>();
+ tree.accum = function(a,b){ return (a==null) ? b : a+b; }
+ tree.set(["one"].list(), 1.1);
+ tree.set(["one","two"].list(), 1.2);
+ var iter = tree.getValuesIterator();
+ assertEquals(2.3, iter.next());
+ assertEquals(1.2, iter.next());
+ assertFalse(iter.hasNext());
+ }
+
+ public function testIteratorValuesAccumEmptyParent()
+ {
+ var tree = new DeepHash<String, Float>();
+ tree.accum = function(a,b){ return (a==null) ? b : a+b; }
+ tree.set(["one","two"].list(), 1.2);
+ var iter = tree.getValuesIterator();
+ assertEquals(1.2, iter.next());
+ assertEquals(1.2, iter.next());
+ assertFalse(iter.hasNext());
+ }
+}
11 TestSuite.hx
@@ -0,0 +1,11 @@
+import TestDeepHash;
+
+class TestSuite
+{
+ static function main()
+ {
+ var r = new haxe.unit.TestRunner();
+ r.add(new TestDeepHash());
+ r.run();
+ }
+}
6 haxelib.xml
@@ -0,0 +1,6 @@
+<project name="DeepHash" url="https://github.com/ianxm/DeepHash" license="BSD">
+ <tag v="cross" />
+ <user name="ianxm"/>
+ <description>Data structure made up of nested hashes</description>
+ <version name="1.0">initial haxelib release</version>
+</project>
3 test.hxml
@@ -0,0 +1,3 @@
+-neko test.n
+-main TestSuite
+-cmd neko test.n

0 comments on commit 667dd63

Please sign in to comment.