Compare commits

...

13 Commits

Author SHA1 Message Date
Tristan B. Velloza Kildaire 04cc5f076d
Merge 9610689e14 into b6106883da 2024-05-04 08:42:23 +00:00
Tristan B. Velloza Kildaire 9610689e14 Graph (unittests)
- Added tests for `opIndex(size_t)`
2024-05-04 10:42:12 +02:00
Tristan B. Velloza Kildaire 0525bfb95e Graph
-  Documented `toString()`
2024-05-04 10:39:17 +02:00
Tristan B. Velloza Kildaire c3287bf474 graph
-  Documented `dfs(...)`
2024-05-04 10:38:41 +02:00
Tristan B. Velloza Kildaire 6b91fb0f20 Graph
- Typo fix
2024-05-04 10:35:56 +02:00
Tristan B. Velloza Kildaire b83cadce82 Graph
- Documented helper methods
2024-05-04 10:35:29 +02:00
Tristan B. Velloza Kildaire c8eacc75a6 Graph
- Renamed
2024-05-04 10:34:02 +02:00
Tristan B. Velloza Kildaire a448fe3665 Graph
- Clean up
2024-05-04 10:33:29 +02:00
Tristan B. Velloza Kildaire 293e3960e1 Graph
- Typo fix
2024-05-04 10:33:14 +02:00
Tristan B. Velloza Kildaire fb61d42b5c Tree
- Renamed `Tree!(T)` to `Graph!(T)`
2024-05-04 10:32:53 +02:00
Tristan B. Velloza Kildaire 83c3894c9a Tree
- Adde doc
2024-05-04 10:28:20 +02:00
Tristan B. Velloza Kildaire 92d753f3c1 Tree
- Added `opDollar()` and `@property`'d `length()`
2024-05-03 15:12:54 +02:00
Tristan B. Velloza Kildaire 236d857c49 Always(T)
- Documented
2024-05-03 15:10:17 +02:00
1 changed files with 179 additions and 53 deletions

View File

@ -23,9 +23,9 @@ version(unittest)
{
import std.functional : toDelegate;
private void DebugTouch(T)(Tree!(T) node)
private void DebugTouch(T)(Graph!(T) node)
{
writeln("Touching tree node ", node);
writeln("Touching graph node ", node);
}
}
@ -611,9 +611,23 @@ unittest
destroy(map);
}
/**
* A visitation stratergy
* which always returns
* `true`
*/
public template Always(T)
{
public bool Always(Tree!(T) treeNode)
/**
* Whatever graph node is
* provided always accept
* a visitation to it
*
* Params:
* treeNode = the node
* Returns: `true` always
*/
public bool Always(Graph!(T) treeNode)
{
version(unittest)
{
@ -631,33 +645,33 @@ public template Always(T)
public template Nothing(T)
{
/**
* Consumes a tree node
* Consumes a graph node
* and does zilch with it
*
* Params:
* treeNode = the node
*/
public void Nothing(Tree!(T));
public void Nothing(Graph!(T));
}
/**
* The inclusion stratergy which
* will be called upon the tree
* will be called upon the graph
* node prior to it being visited
* during a dfs operation.
*
* It is a predicate to determine
* whether or not the tree node
* whether or not the graph node
* in concern should be recursed
* upon.
*/
public template InclusionStratergy(T)
{
public alias InclusionStratergy = bool delegate(Tree!(T) item);
public alias InclusionStratergy = bool delegate(Graph!(T) item);
}
/**
* This is called on a tree node
* This is called on a graph node
* as part of the first action
* that takes place during the
* visitation of said node during
@ -665,22 +679,63 @@ public template InclusionStratergy(T)
*/
public template TouchStratergy(T)
{
public alias TouchStratergy = void delegate(Tree!(T) item);
public alias TouchStratergy = void delegate(Graph!(T) item);
}
// TODO: Technically this is a graph
public class Tree(T)
/**
* A graph of nodes.
*
* These nodes are comprised of
* two components. The first of
* which is their associated value
* of type `T`, then the second
* are their children nodes
* (if any). The latter are of
* type `Graph!(T)` and therefore
* when constructing one such node
* it can also be added as a child
* of another node, therefore
* allowing you to build your
* graph as you see fit.
*
* Some notable functionality,
* other than the obvious,
* is the pluggable dfs method
* which let's you perform
* a recursive search on
* the graph, parameterized
* by two stratergies. The first
* is the so-called `TouchStratergy`
* which specifies the function
* to be called on the current node
* when `dfs` is called on it -
* this is the first thing that
* is done. The other parameter
* is the `VisitationStratergy`
* which is a predicate that
* will be called BEFORE
* entering the dfs (recursing)
* of a candidate child node.
* With this things like trees
* can be built or rather
* _derived_ from a graph.
* This is infact what the visitation
* tree type does.
*
* See_Also: `VisitationTree`
*/
public class Graph(T)
{
private T value;
private Tree!(T)[] children;
private Graph!(T)[] children;
/**
* Constructs a new tree with
* Constructs a new graph with
* the given value to set
*
* Params:
* value = the value of
* this tree node
* this graph node
*/
this(T value)
{
@ -688,7 +743,7 @@ public class Tree(T)
}
/**
* Creates a new tree without
* Creates a new graph without
* associating any value with
* itself
*/
@ -698,7 +753,7 @@ public class Tree(T)
}
/**
* Sets the tree node's
* Sets the graph node's
* associated value
*
* Params:
@ -710,7 +765,7 @@ public class Tree(T)
}
/**
* Appends another tree node
* Appends another graph node
* to the array of children
* of this node's
*
@ -718,24 +773,24 @@ public class Tree(T)
* node = the tree node
* to append
*/
public void appendNode(Tree!(T) node)
public void appendNode(Graph!(T) node)
{
this.children ~= node;
}
/**
* Removes a given tree node
* Removes a given graph node
* from th array of children
* of thie node's
*
* Params:
* node = the tree node to
* node = the graph node to
* remove
* Returns: `true` if the node
* was found and then removed,
* otherwise `false`
*/
public bool removeNode(Tree!(T) node)
public bool removeNode(Graph!(T) node)
{
bool found = false;
size_t idx;
@ -758,19 +813,34 @@ public class Tree(T)
return false;
}
private static bool isTreeNodeType(E)()
/**
* Checks if the given type is
* that of a graph node
*
* Returns: `true` if so, `false`
* otherwise
*/
private static bool isGraphNodeType(E)()
{
return __traits(isSame, E, Tree!(T));
return __traits(isSame, E, Graph!(T));
}
private static bool isTreeValueType(E)()
/**
* Checks if the given type is
* that of a graph node's value
* type
*
* Returns: `true` if so, `false`
* otherwise
*/
private static bool isGraphValueType(E)()
{
return __traits(isSame, E, T);
}
/**
* Returns a slice of the requested
* type. This is either `Tree!(T)`
* type. This is either `Graph!(T)`
* or `T` itself, therefore returning
* an array of either
*
@ -778,24 +848,22 @@ public class Tree(T)
* type of children
*/
public E[] opSlice(E)()
if(isTreeNodeType!(E) || isTreeValueType!(E))
if(isGraphNodeType!(E) || isGraphValueType!(E))
{
// If the children as tree nodes is requested
static if(isTreeNodeType!(E))
// If the children as graph nodes is requested
static if(isGraphNodeType!(E))
{
return this.children;
}
// If the children as values themselves is requested
else static if(isTreeValueType!(E))
else static if(isGraphValueType!(E))
{
T[] slice;
foreach(Tree!(T) tnode; this.children)
foreach(Graph!(T) tnode; this.children)
{
slice ~= tnode.value;
}
return slice;
// import std.algorithm.iteration : map;
// return map!(getValue)(this.children)[];
}
}
@ -815,10 +883,10 @@ public class Tree(T)
* at the given index.
*
* The type `E` can be specified
* as either `Tree!(T)` or `T`
* as either `Graph!(T)` or `T`
* which will hence return a node
* from the children array at the
* given index of that tyope (either
* given index of that type (either
* the child node or the child node's
* value).
*
@ -827,15 +895,15 @@ public class Tree(T)
* Returns: the type `E`
*/
public E opIndex(E)(size_t idx)
if(isTreeNodeType!(E) || isTreeValueType!(E))
if(isGraphNodeType!(E) || isGraphValueType!(E))
{
// If the cjild as a tree node is requested
static if(isTreeNodeType!(E))
// If the child as a graph node is requested
static if(isGraphNodeType!(E))
{
return this.children[idx];
}
// If the child as a value itself is requested
else static if(isTreeValueType!(E))
else static if(isGraphValueType!(E))
{
return this.children[idx].value;
}
@ -855,6 +923,55 @@ public class Tree(T)
return opIndex!(T)(idx);
}
/**
* Returns the number
* of children attached
* to this node
*
* Returns: the count
*/
@property
public size_t length()
{
return this.children.length;
}
/**
* Returns the number
* of children attached
* to this node
*
* Returns: the count
*/
public size_t opDollar()
{
return this.length;
}
/**
* Performs a depth first search
* on the graph by firstly calling
* the `TouchStratergy` on the current
* node and then iterating over all
* of its children and only recursing
* on each of them if the `InclusionStratergy`
* allows it.
*
* The touch stratergy is called
* as part of the first line of code
* in the call to the dfs on a
* given graph node.
*
* Note that is you don't have a good
* inclusion stratergy and touch startergy
* then you may have a stack overflow
* occur if your graph has cycles
*
* Params:
* strat = the `InclusionStratergy`
* touch = the `TouchStratergy`
* Returns: a `T[]`
*/
public T[] dfs
(
InclusionStratergy!(T) strat = toDelegate(&Always!(T)),
@ -878,7 +995,7 @@ public class Tree(T)
// Touch
touch(this); // root[x]
foreach(Tree!(T) child; this.children) // subtree[x],
foreach(Graph!(T) child; this.children) // subtree[x],
{
if(strat(child))
{
@ -906,27 +1023,36 @@ public class Tree(T)
return collected;
}
/**
* Returns a string representation
* of this node and its value
*
* Returns: a `string`
*/
public override string toString()
{
return format("TreeNode [val: %s]", this.value);
return format("GraphNode [val: %s]", this.value);
}
}
/**
* Test out usage of the tree
* Test out usage of the `Graph!(T)`
*/
unittest
{
Tree!(string) treeOfStrings = new Tree!(string)("Top");
Graph!(string) treeOfStrings = new Graph!(string)("Top");
Tree!(string) subtree_1 = new Tree!(string)("1");
Tree!(string) subtree_2 = new Tree!(string)("2");
Tree!(string) subtree_3 = new Tree!(string)("3");
Graph!(string) subtree_1 = new Graph!(string)("1");
Graph!(string) subtree_2 = new Graph!(string)("2");
Graph!(string) subtree_3 = new Graph!(string)("3");
treeOfStrings.appendNode(subtree_1);
treeOfStrings.appendNode(subtree_2);
treeOfStrings.appendNode(subtree_3);
assert(treeOfStrings.opIndex!(Graph!(string))(0) == subtree_1);
assert(treeOfStrings.opIndex!(Graph!(string))(1) == subtree_2);
assert(treeOfStrings.opIndex!(Graph!(string))(2) == subtree_3);
InclusionStratergy!(string) strat = toDelegate(&Always!(string));
TouchStratergy!(string) touch = toDelegate(&DebugTouch!(string));
@ -940,7 +1066,7 @@ unittest
assert(result[3] == "Top");
auto i = treeOfStrings.opSlice!(Tree!(string))();
auto i = treeOfStrings.opSlice!(Graph!(string))();
writeln("Siblings: ", i);
assert(i[0] == subtree_1);
assert(i[1] == subtree_2);
@ -956,7 +1082,7 @@ unittest
}
/**
* A kind-of a tree which has the ability
* A kind-of a graph which has the ability
* to linearize all of its nodes which
* results in performing a depth first
* search resulting in the collection of
@ -976,7 +1102,7 @@ unittest
* relations can be flattened into an
* array.
*/
public class VisitationTree(T) : Tree!(T)
public class VisitationTree(T) : Graph!(T)
{
private bool visisted;
@ -1005,12 +1131,12 @@ public class VisitationTree(T) : Tree!(T)
* The inclusion startergy
*
* Params:
* tnode = the tree node
* tnode = the graph node
* Returns: `true` if not
* yet visited or incompatible
* node type
*/
private static bool _shouldVisit(Tree!(T) tnode)
private static bool _shouldVisit(Graph!(T) tnode)
{
VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode;
return vnode && !vnode.isVisited();
@ -1020,12 +1146,12 @@ public class VisitationTree(T) : Tree!(T)
* The touching stratergy
*
* Only works on compatible
* tree nodes
* graph nodes
*
* Params:
* tnode = the tree node
*/
private static void _touch(Tree!(T) tnode)
private static void _touch(Graph!(T) tnode)
{
VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode;
if(vnode)