あなたはいくつかのテストを引用しているので、少なくとも問題を突き刺したように思えます。整数または実数の単一の数値をすでに定義していると仮定します-関係ありません、とにかくすべてを浮動小数点に変換しています-そして2つの数値の一部、おそらく次のようなものです:
from pyparsing import Regex, Optional
number = Regex(r"\d+(\.\d*)?").setParseAction(lambda t: float(t[0]))
fraction = number("numerator") + "/" + number("denominator")
fraction.setParseAction(lambda t: t.numerator / t.denominator)
(解析時に浮動小数点変換と分数除算を実行する解析アクションの使用に注意してください。後で戻ってふるいにかけるのではなく、何かが数値や分数などであることがわかっている場合は、解析中にこれを行うことを好みます。断片化された文字列の束を介して、パーサーがすでに実行した認識ロジックを再作成しようとします。)
これが私があなたの問題のために作成したテストケースで、整数と分数、そして整数と実数の両方を使用した整数と分数で構成されています。
tests = """\
1
1.0
1/2
1.0/2.0
1 1/2
1.0 1/2
1.0 1.0/2.0""".splitlines()
for t in tests:
print t, fractExpr.parseString(t)
最後のステップは、単一の数値、分数、または単一の数値と分数である可能性のある分数式を定義する方法です。
pyparsingは左から右であるため、regexenと同じ種類のバックトラックは実行されません。したがって、この式はあまりうまく機能しません。
fractExpr = Optional(number) + Optional(fraction)
数値と小数部から得られる可能性のある数値を合計するには、次の解析アクションを追加します。
fractExpr.setParseAction(lambda t: sum(t))
私たちのテストは次のように印刷されます。
1 [1.0]
1.0 [1.0]
1/2 [1.0]
1.0/2.0 [1.0]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]
1/2
分数だけを含むテストケースの場合、先頭の分子はOptional(number)
用語に一致しますが、「/ 2」だけが残ります。これは一致しませんOptional(fraction)
。幸い、2番目の用語はオプションであるため、これは「合格」です。 、しかしそれは本当に私たちが望むことをしていません。
孤独な数と分数の主要な分子の間にこの潜在的な混乱があるため、fractExprを少し賢くし、最初に孤独な分数を探す必要があります。これを行う最も簡単な方法は、fractExprに次のように読み取ることです。
fractExpr = fraction | number + Optional(fraction)
この変更により、テストの結果が改善されました。
1 [1.0]
1.0 [1.0]
1/2 [0.5]
1.0/2.0 [0.5]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]
pyparsingにはいくつかの古典的な落とし穴があり、これはそのうちの1つです。pyparsingは、指示した先読みのみを実行することを覚えておいてください。それ以外の場合は、左から右へのまっすぐな解析です。