Python: заменить функцию по ее выражению, в математическом выражении в виде строки, на Sympy? - PullRequest
0 голосов
/ 30 октября 2019

У меня есть определение функции в виде строки, скажем, func_def = "f(x, y) = x + 2*y + 3".
У меня есть математическое выражение в виде строки, включая эту функцию, скажем,
expr = '2*a*b + f(b+a, 2*b)'.

Как я могу заменить в своем выражении функцию на ее значение?
Другими словами, как мне получить строку expr_eval = "2*a*b + ( (b+a) + 2*(2*b) + 3 )"?

Есть ли простое решение дляэто, например, с использованием Sympy комбинации sympify и subs и некоторой другой функции? Я чувствую, что он должен быть, но не могу его найти. Я должен сделать это для довольно многих уравнений, содержащих много символов, так что создание символов Sympy для каждого из них в отдельности не кажется отличным вариантом.

Пока я использую регулярное выражение, но я нахожу это решениесложно придумать, обобщить (другие функции с другим числом переменных), масштабировать и читать. Это выглядит так:

import re

func_def = "f(x, y) = x + 2*y + 3"
expr = '2*a*b + f(b+a, 2*b)'

# extract substring with function
str_to_replace = re.findall(r'f\([a-zA-z0-9\*\+\"\']*, [a-zA-z0-9\*\+\"\'/]*\)', expr)
str_to_replace = str_to_replace[0]

# extract function name and arguments in substring
func_name, args, _ = re.split('\(|\)', str_to_replace)
args = args.split(',')
args = [i.strip() for i in args]

# parse the function definition
func_def_lhs, func_def_rhs = func_def.split('=')
func_def_name, args_def, _ = re.split('\(|\)', func_def_lhs)
args_def = args_def.split(',')
args_def = [i.strip() for i in args_def]

# replace each argument in the definition by its expression
for i, arg_def in enumerate(args_def) : 

    func_def_rhs = func_def_rhs.replace(arg_def, '({})' .format(args[i]))

expr_eval = expr.replace(str_to_replace, '({})' .format(func_def_rhs))

1 Ответ

1 голос
/ 30 октября 2019

Вы можете использовать re.sub:

import re

strip_f = lambda f: (f.split("(")[0], f.split("(")[1][:-1].split(", "))

def explicit(expr, functions):
    d = {}
    for f in functions:
        func_vars, func_def = f.split(" = ")
        func_name, func_vars = strip_f(func_vars)
        d[func_name] = (func_vars, func_def)

    def replace(match):
        m = match.groups()[0]
        fn_name, expr_vars = strip_f(m)
        func_vars, s = d[fn_name]
        for fv, ev in zip(func_vars, expr_vars):
            s = s.replace(fv, "("+ev+")")
        s = "("+s+")"
        return s
    return re.sub(r"(.\(([^\)]+,?)+?\))", replace, expr)


expr = '2*a*b + f(b+a, 2*b) + g(5, 2*c, 3+a)'
f1 = "f(x, y) = x + 2*y + 3"
f2 = "g(x, y, z) = x + y + z - 2"

print(explicit(expr, [f1, f2]))

Дисплеи:

2*a*b + ((b+a) + 2*(2*b) + 3) + ((5) + (2*c) + (3+a) - 2)

Регулярное выражение с разбивкой:

(                   begin capturing group for function
   .                match any character (function nname)
   \(               match open parenthesis
   (                begin capturing group for variable
     [^\)]+         match at least one non-parenthesis character
     ,?             match a comma if it's there
   )                end variable capturing 
   +?               match at least one variable
   \)               match close parenthesis
)                   end capturing group

Если вы неЕсли вы упростите вывод, вы можете использовать sympy методы, которые вы упомянули:

import sympy

expr = '2*a*b + f(b+a, 2*b) + g(5, 2*c, 3+a)'
f1 = "f(x, y) = x + 2*y + 3"
f2 = "g(x, y, z) = x + y + z - 2"

def split(fn):
    return str(fn).partition("(")

def check_fn(ex):
    name, sep, rest = split(ex)
    return name and sep

def parse_functions(functions):
    fns = {}
    for f in functions:
        name, _, rest = split(f)
        fn = rest.split(" = ")
        fns[name] = fn[0][:-1].split(", "), fn[1]
    return fns

def expand(expr, functions):
    fns = parse_functions(functions)

    def sub_fn(ex):
        with sympy.evaluate(False):
            vs, fn = fns[split(ex)[0]]
            fn = sympy.UnevaluatedExpr(sympy.sympify(fn))
            return fn.subs(dict(zip(vs, str(ex)[2:-1].split(", "))))

    return sympy.sympify(expr).replace(check_fn, sub_fn)

print(sympy.StrPrinter({'order':'none'})._print(expand(expr, [f1, f2])))

Отображение:

2*a*b + a + b + 2*(2*b) + 3 + 5 + 2*c + a + 3 - 2

Обратите внимание, что это предполагает, что вы хотите полный,неупорядоченное неупорядоченное уравнение.

...