## SymPy – 24 – manipolazione avanzata di espressioni – 2 Continuo da qui, copio qui.

Ricorsione attraverso l’albero delle espressioni
Now that you know how expression trees work in SymPy, let’s look at how to dig our way through an expression tree. Every object in SymPy has two very important attributes, `func`, and `args`.

`func`
`func` is the head of the object. For example, `(x*y)`.func is `Mul`. Usually it is the same as the class of the object (though there are exceptions to this rule).

Two notes about `func`. First, the class of an object need not be the same as the one used to create it. For example We created `Add(x, x)`, so we might expect `expr.func` to be `Add`, but instead we got `Mul`. Why is that? Let’s take a closer look at `expr`. `Add(x, x)`, i.e., `x + x`, was automatically converted into `Mul(2, x)`, i.e., `2*x`, which is a `Mul`. SymPy classes make heavy use of the `__new__` class constructor, which, unlike `__init__`, allows a different class to be returned from the constructor.

Second, some classes are special-cased, usually for efficiency reasons [note 3].

Note 3: Classes like `One` and `Zero` are singletonized, meaning that only one object is ever created, no matter how many times the class is called. This is done for space efficiency, as these classes are very common. For example, `Zero` might occur very often in a sparse matrix represented densely. As we have seen, `NegativeOne` occurs any time we have `-x` or `1/x`. It is also done for speed efficiency because singletonized objects can be compared by is. The unique objects for each singletonized class can be accessed from the `S` object. For the most part, these issues will not bother us. The special classes `Zero`, `One`, `NegativeOne`, and so on are subclasses of `Integer`, so as long as you use isinstance, it will not be an issue.

`args`
`args` are the top-level arguments of the object. `(x*y).args` would be `(x, y)`. Let’s look at some examples From this, we can see that `expr == Mul(3, y**2, x)`. In fact, we can see that we can completely reconstruct `expr` from its `func` and its `args`. Note that although we entered `3*y**2*x`, the args are `(3, x, y**2)`. In a `Mul`, the `Rational` coefficient will come first in the `args`, but other than that, the order of everything else follows no special pattern. To be sure, though, there is an order. `Mul`’s args are sorted, so that the same `Mul` will have the same `args`. But the sorting is based on some criteria designed to make the sorting unique and efficient that has no mathematical significance.

The `srepr` form of our `expr` is `Mul(3, x, Pow(y, 2))`. What if we want to get at the args of `Pow(y, 2)`. Notice that the `y**2` is in the third slot of `expr.args`, i.e., `expr.args`. So to get the `args` of this, we call `expr.args.args`. Now what if we try to go deeper. What are the `args` of `y`. Or `2`. Let’s see. They both have empty `args`. In SymPy, empty `args` signal that we have hit a leaf of the expression tree.

So there are two possibilities for a SymPy expression. Either it has empty `args`, in which case it is a leaf node in any expression tree, or it has `args`, in which case, it is a branch node of any expression tree. When it has `args`, it can be completely rebuilt from its `func` and its `args`. This is expressed in the key invariant.

Key invariant
Every well-formed SymPy expression must either have empty `args` or satisfy `expr == expr.func(*expr.args)`.

(Recall that in Python if `a` is a tuple, then `f(*a)` means to call `f` with arguments from the elements of `a`, e.g., `f(*(1, 2, 3))` is the same as `f(1, 2, 3)`.)

This key invariant allows us to write simple algorithms that walk expression trees, change them, and rebuild them into new expressions. Tutto questo nel prossimo post. Posta un commento o usa questo indirizzo per il trackback.