Поскольку вы цитируете некоторые тесты, похоже, что вы хотя бы попытались решить проблему. Я предполагаю, что вы уже определили одно число, которое может быть целым или действительным - не имеет значения, вы все равно конвертируете все в плавающее число - и часть из двух чисел, возможно, что-то вроде этого:
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)
- к счастью, поскольку второй термин является необязательным, он «проходит», но на самом деле он не выполняет то, что нам нужно.
Нам нужно сделать 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 выполняет только то, что вам говорят, иначе это просто прямой анализ слева направо.