まず、コンテキスト:
サイド プロジェクトとして、方程式を解くために必要な手順を生成するコンピューター代数システムを Python で構築しています。
これまでのところ、代数式と方程式を構文解析して式ツリーにすることができました。次のような構造になっています (実際のコードではなく、実行されていない可能性があります)。
# Other operators and math functions are based off this.
# Numbers and symbols also have their own classes with 'parent' attributes.
class Operator(object):
def __init__(self, *args):
self.children = args
for child in self.children:
child.parent = self
# the parser does something like this:
expr = Add(1, Mult(3, 4), 5)
これに加えて、式を単純化するために再帰的に動作する一連の関数があります。それらは純粋に機能的ではありませんが、操作の可変性に依存することを避けようとしており、代わりに、使用しているノードの変更されたコピーを返します。各関数は次のようになります。
def simplify(node):
for index, child in enumerate(node.children):
if isinstance(child, Operator):
node.children[index] = simplify(node)
else:
# perform some operations to simplify numbers and symbols
pass
return node
課題は「一歩一歩」の部分にあります。「単純化」関数をすべて、何かを解決するために必要なステップを「生成」するネストされたジェネレーターにしたいと考えています。したがって、基本的に、各関数が操作を実行するたびに、次のようなことを実行できるようにしたいと考えています。yield (deepcopy(node), expression, "Combined like terms.")
このライブラリに依存しているものはすべて、次のようなものを出力できます。
5x + 3*4x + 3
5x + 12x + 3 Simplified product 3*4x into 12x
17x + 3 Combined like terms 5x + 12x = 17x
ただし、各関数は、node
それが動作しているものについての知識しか持っていませんが、全体がどのexpression
ように見えるかはわかりません。
これが私の質問です:各「ステップ」が式全体の知識を持つように、式ツリー全体の「状態」を維持する最良の方法は何でしょうか?
私が思いついた解決策は次のとおりです。
- すべての操作を適切に実行し、グローバル変数またはクラス内のインスタンス変数を使用して、方程式へのポインターを格納します。最初にクラスをセットアップする必要があるため、単体テストがより困難になるため、これは好きではありません。また、より機能的なアプローチの他の利点も失います。
- 式のルートをすべての関数に渡します。ただし、これは、式を更新するためにすべての操作を繰り返す必要があるか、可変性に依存する必要があることを意味します。
- 私が生成した各ステップに基づいて、最上位関数に式ツリーを「再構築」させます。たとえば、yield
5x + 4x = 9x
の場合、最上位関数に (5x + 4x) ノードを見つけて「9x」に置き換えます。これが最善の解決策のように思えますが、各ステップを「再構築」するにはどうすればよいでしょうか?
関連する最後の 2 つの質問: これは意味がありますか? 私は今、私のシステムにカフェインがたくさんあり、私がクリアしているかどうかわかりません.
可変性について心配しすぎていませんか? これは時期尚早の最適化のケースですか?