I’m having a bit of trouble with a custom tree. I’m writing a program that “learns” about animals. Lets start with the nodes. I started with a class called BTNode. It holds a string value and a “right” and “left” BTNode. I then extended BTNode to an abstract class called DecisionTreeNode. DecisionTreeNode is extended by ThingNode and QuestionNode. Thing node is intended to only be leaf nodes, with its string value being the name of an animal. QuestionNode will hold questions that differentiate between animals, and should always have two child nodes.
So, the idea is that it will learn by asking user-supplied questions. So, it will start by simply asking the user to think of an animal, then ask, “Is it a ?”, and if I answer no, it will ask for my answer, and a question that differentiates the two.
For example, I can “seed” the tree with a single thingNode(“mouse”), and if I’m thinking of a fish, then I will answer its first question “no”, and supply a question, “Does it live in water?”. Now the next time through, it will start by asking, “Does it live in water?” and a “yes” will result in, “is it a fish”, and a “no” will result in “is it a mouse?”.
So, I will post my “learn” method below. I think I am having a referencing problem. I pass a DecisionTreeNode into the learn method, but the changes I make to it are not cascading back up. Not sure if I’m explaining that right. The method works for the first run through, you will see where I change the root node from a Thing to a Question and set its child nodes to the two animals. This however does not work for “current”(the leaf node passed into the method), the last line of the learn method is the line that is not acting as expected.
Sorry for the wall of text, let me know if you would like more code posted or any other info.
Thanks in advance.
“root” is passed into the play method initially.
public static void play(DecisionTreeNode current) {
while(!current.isLeaf()) {
if(queryUser(current.getValue())) {
current = current.getYesLink();
}
else {
current = current.getNoLink();
}
}
System.out.println("Is it a " + current.getValue() + "?");
if(!queryUser("Correct?")) {
learn(current);
}
else {
System.out.println("I win!");
}
}
public static void learn(DecisionTreeNode current) {
String currentGuess;
String correctGuess;
String newQuestion;
ThingNode tempNode;
currentGuess = current.getValue();
System.out.println("I give up, what animal were you thinking of?");
correctGuess = stdin.nextLine();
System.out.println("Please enter a yes or no question that distinguishes a " + correctGuess + " from a " + currentGuess +": ");
newQuestion = stdin.nextLine();
tempNode = (ThingNode)current;
if(current == root) {
if(queryUser("")) {
root = new QuestionNode(newQuestion, tempNode, new ThingNode(correctGuess));
}
else {
root = new QuestionNode(newQuestion, new ThingNode(correctGuess), tempNode);
}
}
else {
current = new QuestionNode(newQuestion, tempNode, new ThingNode(correctGuess));
}
}
Your problem is here:
Current is the parameter to the method. It a reference to a DecisionTreeNode passed by value.
Setting
currenthere does not change thecurrentwhich is passed to the method.You can either pass in the parent of
currentto the function and tweak its reference tocurrentor create areplace(Node, Node)method you can call likeparent.replace(leaf, createNewLearnedNode(leaf)).