mirror of
https://github.com/sbrl/Pepperminty-Wiki.git
synced 2024-12-22 13:45:02 +00:00
BkTree: Add trace, improve shell, and bugfix delete.
The shell has given me an idea..... We could have a CLI module to Pepperminty Wiki that allow admins to run longer-running tasks without them getting killed 1/2 way through because of a timeout.
This commit is contained in:
parent
2f901642fe
commit
296139049a
3 changed files with 78 additions and 15 deletions
|
@ -104,7 +104,7 @@ class BkTree
|
|||
// if($string == "bunny") echo("\nStart $string\n");
|
||||
|
||||
$next_node = $this->box->get("node|$starting_node_id"); // Grab the root to start with
|
||||
$next_node_id = 0;
|
||||
$next_node_id = $starting_node_id;
|
||||
$depth = 0; $visted = 0;
|
||||
while(true) {
|
||||
$visted++;
|
||||
|
@ -160,10 +160,11 @@ class BkTree
|
|||
|
||||
$node_target_id = $node_target->children->$distance;
|
||||
$node_target = $this->box->get("node|$node_target_id");
|
||||
$stack[] = [ "node" => $node_target, "id" => $node_target->children->$distance ];
|
||||
$stack[] = [ "node" => $node_target, "id" => $node_target_id ];
|
||||
}
|
||||
|
||||
$parent = end($stack); // The last item on the stack is the parent node
|
||||
// The last item but 1 on the stack is the parent node
|
||||
$parent = $stack[count($stack) - 2];
|
||||
|
||||
// 1. Delete the connection from parent -> target
|
||||
foreach($parent["node"]->children as $distance -> $id) {
|
||||
|
@ -180,16 +181,53 @@ class BkTree
|
|||
// 2. Iterate over the target's children (if any) and re-hang them from the parent
|
||||
// NOTE: We need to be careful that the characteristics of the tree are preserved. We should test this by tracing a node's location in the tree and purposefully removing nodes in the chain and see if the results returned as still the same
|
||||
//
|
||||
// Hang the now orphaned children from the parent
|
||||
// Hang the now orphaned children and all their decendants from the parent
|
||||
foreach($node_target->children as $distance -> $id) {
|
||||
$orphan = $this->box->get("node|$id");
|
||||
// TODO: Optimise this
|
||||
$this->box->delete("node|$id"); // Delete the orphan node
|
||||
$this->add($orphan->value, $parent["id"]); // Re-hang it from the parent
|
||||
$substack = [ [ "node" => $orphan, "id" => $id ] ]; $substack_top = 0;
|
||||
while($substack_top >= 0) {
|
||||
$next = $substack[$substack_top];
|
||||
unset($substack[$substack_top]);
|
||||
$substack_top--;
|
||||
|
||||
$this->box->delete("node|{$next["id"]}"); // Delete the orphan node
|
||||
$this->add($next["node"], $parent["id"]); // Re-hang it from the parent
|
||||
|
||||
foreach($next["node"]->children as $distance => $sub_id) {
|
||||
$substack[++$substack_top] = [
|
||||
"node" => $this->box->get("node|$sub_id"),
|
||||
"id" => $sub_id
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the target node
|
||||
$this->box->delete("node|$node_target_id");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function trace(string $string) {
|
||||
$stack = [
|
||||
(object) [ "node" => $this->box->get("node|0"), "id" => 0 ]
|
||||
];
|
||||
$node_target = $stack[0]->node;
|
||||
|
||||
while($node_target->value !== $string) {
|
||||
$distance = levenshtein($string, $node_target->value, $this->cost_insert, $this->cost_replace, $this->cost_delete);
|
||||
|
||||
var_dump($node_target);
|
||||
|
||||
// Failed to recurse to find the node with the value in question
|
||||
if(!isset($node_target->children->$distance))
|
||||
return null;
|
||||
|
||||
$node_target_id = $node_target->children->$distance;
|
||||
$node_target = $this->box->get("node|$node_target_id");
|
||||
$stack[] = (object) [ "node" => $node_target, "id" => $node_target_id ];
|
||||
}
|
||||
return $stack;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,8 +297,7 @@ class BkTree
|
|||
// echo("[lookup] Recursing on child ".$this->box->get("node|$child->id")->value." (distance $child->distance)\n");
|
||||
// Push the node onto the stack
|
||||
// Note that it doesn't actually matter that the stack isn't an accurate representation of ancestor nodes at any given time here. The stack is really a hybrid between a stack and a queue, having features of both.
|
||||
$stack_top++;
|
||||
$stack[$stack_top] = $this->box->get("node|{$node_current->children->$child_distance}");
|
||||
$stack[++$stack_top] = $this->box->get("node|{$node_current->children->$child_distance}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ class JsonStorageBox {
|
|||
/**
|
||||
* Deletes an item from the data store.
|
||||
* @param string $key The key of the item to delete.
|
||||
* @return bool Whether it was really deleted or not. Note that if it doesn't exist, then it can't be deleted.
|
||||
* @return bool Whether it was really deleted or not. Note that if it doesn't exist, then it can't be deleted. Note also that if a node is deleted before being persisted to disk, this will return false when in actuality it was deleted successfully.
|
||||
*/
|
||||
public function delete(string $key) : bool {
|
||||
// Remove it from the cache
|
||||
|
|
|
@ -73,7 +73,7 @@ else
|
|||
echo("Tree created in ".($tree["time"]*1000)."ms\n");
|
||||
$tree = $tree["value"];
|
||||
|
||||
writegraph(); exit();
|
||||
// writegraph(); exit();
|
||||
|
||||
function test_auto() {
|
||||
global $tree;
|
||||
|
@ -98,7 +98,8 @@ while(true) {
|
|||
readline_add_history($line);
|
||||
|
||||
if($line[0] == ".") {
|
||||
switch ($line) {
|
||||
$parts = explode(" ", $line, 2);
|
||||
switch ($parts[0]) {
|
||||
case ".quit":
|
||||
case ".exit":
|
||||
$result = time_callable(function() use ($tree) {
|
||||
|
@ -110,20 +111,45 @@ while(true) {
|
|||
|
||||
case ".help":
|
||||
echo("dot commands:
|
||||
.exit Exit, saving edits to the tree to disk
|
||||
.writegraph Write a graphviz dot file to disk representing the tree in the current directory
|
||||
.stats Compute statistics about the tree
|
||||
.exit Exit, saving edits to the tree to disk
|
||||
.writegraph Write a graphviz dot file to disk representing the tree in the current directory
|
||||
.stats Compute statistics about the tree
|
||||
.trace {{string}} Trace the path through the tree to {{string}}
|
||||
.remove {{string}} Delete {{string}} from the tree
|
||||
.add {{string}} Add {{string}} to the tree
|
||||
");
|
||||
break;
|
||||
|
||||
case ".writegraph":
|
||||
writegraph();
|
||||
break;
|
||||
|
||||
case ".remove":
|
||||
$start_time = microtime(true);
|
||||
$result = $tree->remove($parts[1]);
|
||||
echo("{$parts[1]}".($result?"":" not")." successfully deleted from the tree in ".round((microtime(true) - $start_time)*1000)."ms\n");
|
||||
break;
|
||||
case ".add":
|
||||
$start_time = microtime(true);
|
||||
$depth = $tree->add($parts[1]);
|
||||
echo("{$parts[1]} successfully added to the tree at depth $depth in ".round((microtime(true) - $start_time)*1000)."ms\n");
|
||||
break;
|
||||
|
||||
case ".trace":
|
||||
$trace = $tree->trace($parts[1]);
|
||||
foreach($trace as $depth => $item) {
|
||||
echo("$depth: {$item->node->value} (#$item->id)\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case ".stats":
|
||||
echo("Tree stats: ");
|
||||
var_dump($tree->stats());
|
||||
break;
|
||||
|
||||
default:
|
||||
echo("Error: Unknown dot-command {$parts[0]} (try .help)\n");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue