'Evaluate the expression represented by this tree.
``Operators`` should be a dictionary mapping operator names (\'tensor\',
\'product\', etc.) to a function that implements that operator for the
correct number of operands.
If given, ``getter`` is a function evaluated on each *leaf* node\'s
value before applying the operator between them.  This could be used,
for example, to operate on an attribute of the node values rather than
directly on the node values.  The ``getter`` is passed both the index
of the leaf (a count starting at 0 that is incremented after each leaf
is found) and the leaf node itself.
The ``start`` and ``stop`` arguments allow evaluating a sub-expression
within the expression tree.
TODO: Document this better.'

def evaluate(self, operators, getter=None, start=0, stop=None):

    stack = deque()
    if (getter is None):
        getter = (lambda idx, value: value)
    if (start is None):
        start = 0
    leaf_idx = 0
    for node in self.traverse_postorder():
        if node.isleaf:
            stack.append((getter(leaf_idx, node.value), leaf_idx))
            leaf_idx += 1
        else:
            operator = operators[node.value]
            if (len(stack) < 2):
                continue
            right = stack.pop()
            left = stack.pop()
            operands = []
            for operand in (left, right):
                if (operand[(-1)] == (-1)):
                    operands.append(operand)
                else:
                    (operand, idx) = operand
                    if ((start <= idx) and ((stop is None) or (idx < stop))):
                        operands.append((operand, idx))
            if (len(operands) == 2):
                (left, right) = operands
                stack.append((operator(left[0], right[0]), (-1)))
            elif (len(operands) == 0):
                stack.append(left)
            else:
                stack.extend(operands)
    return stack.pop()[0]
