Refactor recursion out of compute 

Previously we wrote lots of `compute` methods like the following:

```Python
@dispatch(Projection, DataFrame)
def compute(t, df):
    parent = compute(t.parent, df)
    return parent[list(t.columns)]
```

The traversal down the tree was baked into each method.  We extract this bit into an explicit `bottom_up` function and instead write methods like the following:

```Python
@dispatch(Projection, DataFrame)
def compute_one(t, df):
    return df[list(t.columns)]
```
Fixed.  Replaced with `if-else`.  This required screwing around with `Sort` objects to make them hashable when the key was a list.