0

おい。Python で解決しようとしている問題があり、賢い実装が思いつきません。入力は文字列です。それらのいくつかは変数を表し、他は演算子を表し、変数の大量の値を反復処理したい (異なる構成)。

同等の質問は、文字列として保存された多くの変数を持つ方程式を取得し、x、y、z などをさまざまな値に置き換えたり、ブール式を満たすときに解を見たいということだと思います。

これを実装する巧妙なデータ構造は考えられません。式を毎回「設定」し、変数を代入するだけで、実際に評価するよりも時間がかかります。

私はそれが少し抽象的なことを知っています-しかし、誰かが何かアイデアを持っているか、似たようなことをした経験がありますか?

エバリュエーターを実装したいという人がいます。それは本当ですが、的外れです。質問のために、 eval(string) 関数はすでに実装されていると想定されています。値を保持し、毎回クリーンかつ効率的に変更できる効率的なデータ構造とは? 確かに文字列ではありません!変更可能なリストでなければなりません。また、変数 x のインスタンスがいくつかある場合は、それらすべてにすばやくアクセスして、評価の前に値を変更できる必要があります。

4

4 に答える 4

1

Ira が、この仕事をするには Lex/Yacc のような式評価器が必要だと言ったので、ここでたくさん利用できます:

http://wiki.python.org/moin/LanguageParsing

私はpyparsingを使用しており、そのようなことには偉業を成し遂げています

于 2009-09-13T17:42:08.877 に答える
0

あなたが望むのは明らかに式評価器です。

その他、通常「eval」と呼ばれる言語の組み込み機能を使用するか (Python がこれを提供しているかどうかはわかりません)、式パーサー/評価器を構築します。

このような式エバリュエーターを作成する方法のアイデアを得るには、次の SO ゴルフ演習を参照してください。 コード ゴルフ: 数学的式エバリュエーター (PEMDAS を尊重する)

必要なことを行うには、基本的な考え方 (ほとんど同じです) を Python に変換し、変数名を解析してその値を検索する機能を追加する必要があります。これは非常に簡単な拡張機能です。

于 2009-09-13T17:18:24.497 に答える
0

アイデアはすでに言及されてevalいるようですが、代わりの演算子がある場合、これは役に立ちませんが、過去に次を使用しました。

def evaluate_expression(expr, context):
    try:
        return eval(expr, {'__builtins__': None}, context)
    except (TypeError, ZeroDivisionError, SyntaxError):
        # TypeError is when combining items in the wrong way (ie, None + 4)
        # ZeroDivisionError is when the denominator happens to be zero (5/0)
        # SyntaxError is when the expression is invalid
        return None

次に、次のようなことができます。

values = {
    'a': 1.0,
    'b': 5.0,
    'c': 1.0,
}
evaluate_expression('a + b - (1/c)', **values)

これは 1.0 + 5.0 - 1/1.0 == 5.0 と評価されます

つまり、'd' を + に評価することはevalできませんが、Python で関数を安全に使用する方法が得られます ("while True" を実行することはできません)。例えば)。

安全に使用するための詳細については、この例を参照してください。eval

于 2009-09-13T17:56:41.407 に答える
0

その文字列をツリー (または同等に解釈可能なデータ構造) に一度だけ解析し、関数を繰り返し使用して、対象の変数割り当てセットごとに「ツリーを解釈」します。(「解釈可能なデータ構造」として Python バイトコードを生成することもできるためeval、「ツリーの解釈」として使用できます。これにより、生成が遅くなりますが、必要なのは 1 回だけであり、解釈も高速です)。

あなたが言うように、それは少し抽象的なので、単純すぎる場合は具体的な例を挙げましょう。たとえば、変数が x、y、z、t の文字であり、演算子が加算の場合は a で、減算の場合は s であるとします。隣接する文字の文字列は、一般的な数学の慣例のように、暗黙のうちに優先順位の高い乗算を意味します。括弧なし、厳密な左から右への実行 (つまり、演算子の優先順位なし、乗算を超える)。これらの 6 文字を除くすべての文字は無視する必要があります。次に、非常にアドホックなパーサーと Python バイトコード ジェネレーターを次に示します。

class BadString(Exception): pass

def makeBytecode(thestring):
  theoperator = dict(a='+', s='-')
  python = []
  state = 'start'
  for i, letter in enumerate(thestring):
    if letter in 'xyzt':
      if state == 'start':
        python.append(letter)
        state = 'variable'
      elif state == 'variable':
        python.append('*')
        python.append(letter)
    elif letter in 'as':
      if state == 'start':
        raise BadString(
            'Unexpected operator %r at column %d' % (letter, i))
      python.append(theoperator[letter])
      state = 'start'

  if state != 'variable':
    raise BadString(
      'Unexpected operator %r at end of string' % letter)
  python = ''.join(python)
  # sanity check
  # print 'Python for %r is %r' % (thestring, python)
  return compile(python, thestring, 'eval')

evalこれで、この結果を最初の引数として呼び出し、値を x、y、z、および t に関連付ける辞書を 2 番目の引数として単純に呼び出すことができます。例 (上記のモジュールを as としてインポートしpar、健全性チェックのコメントを外した場合):

>>> c=par.makeBytecode('xyax')
Python for 'xyax' is 'x*y+x'
>>> for x in range(4):
...   for y in range(5):
...     print 'x=%s, y=%s: result=%s' % (x,y,eval(c,dict(x=x,y=y)))
... 
x=0, y=0: result=0
x=0, y=1: result=0
x=0, y=2: result=0
x=0, y=3: result=0
x=0, y=4: result=0
x=1, y=0: result=1
x=1, y=1: result=2
x=1, y=2: result=3
x=1, y=3: result=4
x=1, y=4: result=5
x=2, y=0: result=2
x=2, y=1: result=4
x=2, y=2: result=6
x=2, y=3: result=8
x=2, y=4: result=10
x=3, y=0: result=3
x=3, y=1: result=6
x=3, y=2: result=9
x=3, y=3: result=12
x=3, y=4: result=15
>>> 

より洗練されたシンプルな文字列の解析と、迅速に解釈可能なデータ構造の構築については、たとえばpyparsingを参照してください。

于 2009-09-13T17:39:22.483 に答える