Reputation: 199
Ok, this is a bit of a tricky one.
I have a bunch of code that takes expression trees such that:
((a + b)/(c + d) + sqrt(e))
is stored in a vector (I'm using C++ but I just need the algorithm) in the prefix form:
+(/(+(a,b),+(c,d)),sqrt(e))
//Brackets are just to help you read it. Each operator and terminal is an element in the vector.
Now there is this other means of representing an expression tree known as ORF form
(Third page of the paper: http://arxiv.org/ftp/cs/papers/0102/0102027.pdf)
In this form you represent the tree as if reading the tree from left to right, top to bottom.
((a + b)/(c + d) + sqrt(e))
now becomes:
+/sqrt++eabcd
What I have been failing to do is create an algorithm that can convert:
+/sqrt++eabcd
//ORF
into:
+(/(+(a,b),+(c,d)),sqrt(e))
//Prefix
All I have so far is some code to get the breadth of the tree at different levels:
bool ConvertPrefixToORF(const std::vector<Node> & individual,
std::vector<Node> & ETindividual){
bool all_terminal = false;
int breadth;
int breadthOfDepth[individual.size()];
int depthCounter = 0;
breadthOfDepth[0] = 1;
//Resize only once.
ETindividual.reserve(individual.size());
while (all_terminal == false) {
//Reset breadth
breadth = 0;
//Iterate over next level and calculate depth of level after that.
for (int i = 0; i < breadthOfDepth[depthCounter]; i++) {
all_terminal = true;
//If the individual is a function...
if (individual[current+i].isFunction()) {
//Get individual from i'th item at this depth
function f = individual[current + i];
//Increment breadth of next level with arity
breadth += f->getArity();
//if a single function is found
all_terminal = false;
}
}
//Go down a level in the tree.
depthCounter++;
//Set the breadth of that tree depth:
breadthOfDepth[depthCounter] = breadth;
}
}
Thanks in advance for any help! This is doing my head in. Oh, and this is performance critical :(
Upvotes: 1
Views: 1189
Reputation: 189
Just build a tree T. Each node is a tuple (terminal,)
or (unary_operator, operand)
or (binary_operator, first_operand, second_operand)
. The operands themselves indexes of nodes in the tree.
For instance, the expression a + (b / c)
, would have the tree T[0] = (+, 1, 2), T[1] = (a,), T[2] = (/, 3, 4), T[3] = (b,), T[4] = (c,)
. Once you have this just do a preorder. Here's the Python code for this.
def preorder(T, i):
X = [T[i][0]]
if len(T[i]) > 1:
X.extend(preorder(T, T[i][1]))
if len(T[i]) > 2:
X.extend(preorder(T, T[i][2]))
return X
def convert(A):
binary_operators = ['+', '-', '/']
unary_operators = ['sqrt']
left = 0
right = 0
T = dict([(i, ()) for i in range(len(A))])
for a in A:
if a in binary_operators:
T[left] = (a, right + 1, right + 2)
right += 2
elif a in unary_operators:
T[left] = (a, right + 1)
right += 1
else:
T[left] = (a,)
left += 1
return preorder(T, 0)
def main(argv=None):
A = ['+', '/', 'sqrt', '+', '+', 'e', 'a', 'b', 'c', 'd']
print convert(A)
When you build T from your ORF, keep a left and right pointer which tells you the first node you have to fill in in your expression tree, and right tells you the last node.
Upvotes: 0
Reputation: 71615
My strategy would be to build the parse tree, then walk it depth-first to generate prefix notation.
You can build the parse tree from ORF with a queue. As you encounter each operator (or term), make it a child of the operator at the head of the queue. When the node at the head of the queue has enough children, pop it off of the queue.
In your example... Start with the +
and push it onto the queue (special case for initial element).
Next process the /
. Since the +
at the head of the queue has no children (but needs two), you attach the /
to the +
as its first child and push the /
onto the queue. So now the queue looks like:
+/
...and the tree looks like
+
/ .
. .
...where "." is an element waiting to be filled in.
Next comes the sqrt
. Since +
is at the head of the queue and does not have two children yet, attach the sqrt
to the +
and push the sqrt
onto the queue. So now the queue looks like:
+/sqrt
...and the tree looks like
+
/ sqrt
. . .
Next comes the second +
. The head of the queue is the first +
, but now that has all of its children already. So pop it off the queue. The next element of the queue is the /
, and it has no children yet, so this +
becomes its child and goes onto the back of the queue. Queue now reads:
/sqrt+
...and tree is now:
+
/ sqrt
+ . .
. .
Next the third +
becomes the second child of the /
and gets pushed onto the queue. So the queue will be:
/sqrt++
...and the tree will be (sorry, my ASCII art is weak):
+
/ sqrt
+ + .
. . . .
Now the /
is satisfied, so when you hit the e
, you pop the /
off of the queue. Now the sqrt
is the start of the queue, so the e
gets attached to that. Queue is now:
sqrt++
...and tree is:
+
/ sqrt
+ + e
. . . .
The next four iterations obviously assign a,b,c,d to the remaining leaves, giving you your parse tree.
std::dequeue
is a perfect data structure to use for the queue, by the way.
Upvotes: 2