Вопрос по регулярным выражениям на Python
В этом задании нужно написать программу, которая найдет в тексте ряд простых арифметических выражений и подсчитает их.
Любое выражение начинается с имени переменной a, b или c, затем может идти + или -, затем идет =, затем может идти имя переменной a, b или c, а затем может идти + или - и целое число. Если в правой части выражения нет переменной, то число может быть без знака + или -
Не бывает названий переменных, кроме a, b или c, и действий, кроме описанных тут. Не бывает пробелов вокруг знаков. В тексте не встречаются некорректные выражения, в которых справа от = нет ни переменной, ни числа. Таким образом, список типов выражений, которые могут встречаться, выглядит примерно так:
a=1, a=+1, a=-1, a=b, a=b+100, a=b-100, b+=10, b+=+10, b+=-10, b+=b, b+=b+100, b+=b-100, c-=101, c-=+101, c-=-101, c-=b, c-=b+101, c-=b-101
Выражения могут встречаться внутри текста, например loremc-=a+10ipsuma-=adb+=10olorsitamet.
В вашу функцию calculate(data, findall) будет передан словарь с начальными значениями переменных a, b и c: data = {"a":1, "b":2, "c": 3} и ссылка на функцию findall, а вы должны вернуть такой же словарь с новыми значениями для a, b и c.
Работает findall() аналогично re.findall(), только у нее всего один параметр – регулярное выражение. А текст, в котором она будет искать выражения, она знает сама (см. приложенный архив, чтобы понять, о чем речь). С помощью findall() нужно как минимум найти все выражения в тексте, а как максимум найти их и разбить на группы так, чтобы было удобно их обработать. Если findall() будет вызвано больше одного раза или если размер списка, который она вернет, будет отличаться от количества выражений в тексте, тест провалится.
Эталонное решение занимает 11 строк, не содержит импортов, не использует eval, а регулярное выражение находит выражения и бьет каждое на четыре группы (некоторые группы для некоторых выражений оказываются пустыми):
Имя переменной слева. Знак перед = (если есть). Имя переменной справа (если есть). Число (если есть) со знаком (если есть). Это позволяет легко (буквально в одну строку) посчитать правую часть, а потом, в зависимости от наличия знака перед =, произвести действие с левой частью. Однако ваш алгоритм может быть другим, требуется только выполнить ограничения на вызов findall() и оставить сигнатуру calculate() неизменной.
Нужно переписать функцию calculate() внутри шаблона:
def calculate(data, findall):
matches = findall(r"") # Если придумать хорошую регулярку, будет просто
for v1, s, v2, n in matches: # Если кортеж такой структуры: var1, [sign]=, [var2], [[+-]number]
# Если бы могло быть только =, вообще одной строкой все считалось бы, вот так:
data[v1] = data.get(v2, 0) + int(n or 0)
return data
Для проверки решения локально запустите:
import re
from regexp import calculate
def findall(regexp):
text = """
a=1
a=+1
a=-1
a=b
a=b+100
a=b-100
b+=10
b+=+10
b+=-10
b+=b
b+=b+100
b+=b-100
c-=101
c-=+101
c-=-101
c-=b
c-=b+101
c-=b-101
"""
return re.findall(regexp, text)
result = calculate({'a': 1, 'b': 2, 'c': 3}, findall)
correct = {"a": -98, "b": 196, "c": -686}
if result == correct:
print ("Correct")
else:
print ("Incorrect: %s != %s" % (result, correct))
Есть вариант, который уже отправлял на проверку, но система его не принимает. Вот он:
data = {"a":1, "b":2, "c": 3}
def calculate(data):
matches = re.findall(r"([abc])([+-]?=)([abc])?([+-]?\d+)?",text)
for v1, s, v2, n in matches:
if n == "":
n = 0
if s == "=" and v2 == "":
data[v1] = int(n)
elif s == "=" and v2 != "":
data[v1] = data[v2] + int(n)
print("eee")
elif s == "+=" and v2 == "":
data[v1] += int(n)
elif s == "+=" and v2 != "":
data[v1] += data[v2] + int(n)
elif s == "-=" and v2 == "":
data[v1] -= int(n)
elif s == "-=" and v2 != "":
data[v1] -= data[v2] + int(n)
return data