Сворачивание констант Python с метками

Я хотел бы сделать что-то похожее на постоянное свертывание с использованием Python.

Python имеет удобную встроенную функцию eval(), так что только константные уравнения можно легко сложить, применив eval().

Пример:

s = '4 + (5) * 2'
reduced_s = str(eval(s))  # '14'

Однако свертывание констант, которое я хочу реализовать, должно обрабатывать непостоянные метки.

Примеры:

s = '_tbl + (2) + (2) * 4' 
should be folded to '_tbl + 10'.

Если постоянные части разделены меткой, например (s = '2 + _tbl + 4'), должно получиться '_tbl + 6' (или '6 + _tbl).

Я написал процедуру свертывания констант, используя «C» много лет назад. Это была непростая процедура, так как мне нужно было построить дерево и оценить приоритет операторов.

Поскольку Python гораздо более мощный язык, чем «C», прежде чем делать то же самое с помощью Python, я хотел бы узнать мнение других людей.

Мы очень ценим ваше понимание этой проблемы.


person user2756376    schedule 02.02.2015    source источник
comment
Что вы пытаетесь получить от этого? Вы просто хотите преобразовать строку '_tbl + (2) + (2) * 4' в строку '_tbl + 10'? Для чего вы используете струны?   -  person BrenBarn    schedule 02.02.2015
comment
Да, Python предоставляет функцию eval; есть еще exec. Но это не значит, что вы должны их использовать. :) В общем, настоятельно рекомендуется организовать свой код так, чтобы вам не нужно было использовать eval. Например, вы можете вычислять постоянные значения в глобальном контексте скрипта, чтобы они не вычислялись каждый раз, когда вы вызываете функцию; вы можете составлять списки или словари лямбда-выражений; вы можете использовать частичные функции; и т.п.   -  person PM 2Ring    schedule 02.02.2015
comment
Если вы eval используете строки, полное содержание которых известно только во время выполнения, вы должны быть очень осторожны, чтобы гарантировать, что опасный код не будет выполнен. См. Eval действительно опасен от ТАКОГО обычного Неда Батчелдера. Но если вы действительно хотите использовать eval-подобный подход, а не методы, упомянутые в моем предыдущем комментарии, вы можете использовать ast.literal_eval().   -  person PM 2Ring    schedule 02.02.2015
comment
OP должен использовать модуль ast, анализировать выражение в AST, а затем применять их постоянное свертывание к AST, а затем преобразовывать AST обратно в код Python.   -  person Dan D.    schedule 02.02.2015
comment
› Что вы пытаетесь получить от этого? Это как-то связано с оптимизацией кода (анализ потока данных). В настоящее время идентичными считаются только случаи, когда два случая буквально идентичны. Итак, «_lbl + (2) + (2)» и «_lbl + (4)» — нет.   -  person user2756376    schedule 02.02.2015
comment
Спасибо, что рассказали мне, насколько опасными могут быть функции eval() и exec().   -  person user2756376    schedule 02.02.2015


Ответы (2)


Если "+" содержится в выражении eval, то будет сложно разобрать выражение, но если операция "+" находится вне строкового выражения, то можно перегрузить оператор "add " для имитации eval.

class MyString(str):
    def __init__(self):
        self.mIntList = []
        self.mStringList = []

    def __add__(self, other):
        result = 0
        try:
            result = eval(other)
        except:
            self.mStringList.append(other)
        else:
            self.mIntList.append(result)
        finally:
            return self

    def __str__(self):
        return str(sum(self.mIntList)) + " ".join(self.mStringList)

if __name__ == '__main__':
    s1 = MyString()
    s1 = s1 + "_table" + "(2) + (2) * 4"
    print s1
    s2 = MyString()
    s2 = s2 + "2" + "table" + "2*4"
    print s2

выход

10_table
10table
person Cui Heng    schedule 02.02.2015

Как указано в комментариях, использование модуля ast звучит хорошей идеей. Ниже я даю образец для отделения непостоянной метки и оценки остальной части выражения. Надеюсь, это даст некое положительное направление.

import ast

label = None

class MyTransformer(ast.NodeTransformer):

    def visit_Name(self, node):
        global label

        if(node.id != "evaluated"):
            label = node.id
            return ast.Num(0)
        return node


expression = '_tbl + (2) + (2) * 4' 

node = ast.parse("evaluated=" + expression)
node=MyTransformer().visit(node)
node = ast.fix_missing_locations(node)
eval(compile(node, '<string>', 'exec'))

print(label, str(evaluated))
person sujoy    schedule 02.02.2015
comment
Спасибо, что познакомили меня с модулем ast. Интересно знать, что такой модуль доступен в Python. Он генерирует дерево после вызова parse(). Однако в модуле ast не существует метода свертывания констант, и он по-прежнему выполняется с помощью eval(). Если это так, я не вижу большой разницы с грубым подходом, использующим re.search('[_A-Za-z][_A-Za-z0-9]*', exp) для определения того, является ли метка здесь. Разделите exp, если есть метка, затем примените eval() к строке, которая находится после метки. В моем случае ярлык всегда стоит первым, если он есть. Итак, это сработает. - person user2756376; 03.02.2015