Использование Lambdas для создания исполняемых функций из строковых выражений - PullRequest
1 голос
/ 19 июля 2010

Я использую python, и мне нужна функция, которая принимает строку, содержащую математическое выражение одной переменной (x), и возвращает функцию, которая оценивает это выражение с помощью лямбда-выражения. Синтаксис должен быть таким:

f = f_of_x("sin(pi*x)/(1+x**2)")
print f(0.5)
0.8
Синтаксис

должен разрешать (), а также [] и использовать стандартный приоритет операторов. Тригонные функции должны иметь приоритет ниже, чем умножение и выше, чем сложение. Следовательно, строка «sin 2x + 1» будет эквивалентна sin (2x) +1, хотя оба действительны. Это для оценки пользовательского ввода алгебраических и тригонометрических выражений, так что думайте, что математический синтаксис не программирует синтаксис. Список поддерживаемых функций должен быть легко расширяемым, а код должен быть понятным и простым для понимания. Это нормально, чтобы не свернуть константные выражения.

Функция выборки здесь неполная. Он принимает вложенный список, представляющий выражение, и генерирует соответствующую функцию. Хотя это несколько легко понять, даже для Python это кажется уродливым.

import math

def f_of_x(op):
    if (isinstance((op[0]), (int, long, float, complex)) ):
        return (lambda x:op[0])
    elif op[0]=="pi": return lambda x: 3.14159265358979
    elif op[0]=="e": return lambda x: 2.718281828459
    elif op[0]=="x": return lambda x: x
    elif op[0]=="sin": return lambda x: math.sin(f_of_x(op[1])(x))
    elif op[0]=="cos": return lambda x: math.cos(f_of_x(op[1])(x))
    elif op[0]=="tan": return lambda x: math.tan(f_of_x(op[1])(x))
    elif op[0]=="sqrt": return lambda x: math.sqrt(f_of_x(op[1])(x))
    elif op[0]=="+": return lambda x: (f_of_x(op[1])(x))+(f_of_x(op[2])(x))
    elif op[0]=="-": return lambda x: (f_of_x(op[1])(x))-(f_of_x(op[2])(x))
    elif op[0]=="*": return lambda x: (f_of_x(op[1])(x))*(f_of_x(op[2])(x))
    elif op[0]=="/": return lambda x: (f_of_x(op[1])(x))/(f_of_x(op[2])(x))
    elif op[0]=="**": return lambda x: (f_of_x(op[1])(x))**(f_of_x(op[2])(x))
    # should never get here with well formed input
    return

def test():
    # test function f(x) = sin(pi*x)/(1+x**2)
    s = ['/',['sin',['*',['pi'],['x']]],['+',[1],['**',['x'],[2]]]]
    f = f_of_x(s)
    for x in range(30):
        print " "*int(f(x*0.2)*30+10)+"x"

В качестве общего руководства, думайте о своем решении как о учебнике по лямбдам и парсерам, а не по коду гольфа. Вот пример кода, поэтому напишите, что вы считаете наиболее ясным.

Ответы [ 3 ]

5 голосов
/ 19 июля 2010

Как насчет этого:

import math
def f_of_x(op):
    return eval("lambda x:" + op, math.__dict__)

Его можно легко сделать для поддержки [], а также () и использует стандартный приоритет оператора.Тем не менее, он не позволяет использовать функции триггера без паренов, а также не подразумевает умножение на сопоставление (например, 2x).Однако список поддерживаемых функций легко расширяется, и код, вероятно, настолько прост и понятен, насколько это возможно.

Если вам абсолютно необходимы дополнительные функции, посмотрите http://christophe.delord.free.fr/tpg/.Пример, приведенный на этой странице, может быть легко изменен, чтобы сделать все, что вы хотите.

0 голосов
/ 19 июля 2010

Пример fourFn.py кода даст вам довольно хорошее руководство при написании этого с помощью pyparsing Адаптация этого кода в соответствии с конкретными требованиями ОП оставлена ​​в качестве упражнения для ОП.

- Пол

0 голосов
/ 19 июля 2010

Если вы настаиваете на том, чтобы использовать для своих выражений язык, резко отличающийся от языка Python (в котором все функции всегда вызываются в скобках, в то время как вы хотите разрешить вызов нескольких функций без - allow2x для обозначения 2 * x - и т. Д.), Вам сначала нужно проанализировать строку, например, с помощью pyparsing (автономное решение) или ply ("python lexx и yacc"), если вам нужен более традиционный подход, основанный на лексическом анализаторе и отдельном "компиляторе компилятора" (это то, что обозначают два "c" в yacc: компилятор еще одного компилятора).

Исходя из результатов этого парсера, вы можете сгенерировать синтаксис Python для компиляции - однако, нет реальной причины генерировать lambda вместо простого def, так как вам придется скомпилировать либоиз них все равно.Таким образом, думать о подходе как о «учебнике по лямбдам» было бы очень странно, поскольку решение использовать lambda было бы произвольным и весьма спорным.

Мы говорим о стоимости нескольких часовпрограммирование и, вероятно, более 100 строк получившегося кода Python, если вы надеетесь на какую-либо ясность.Я считаю, что это определенно выходит за рамки того, что задумано как обычная сфера для вопросов и ответов SO.

Значительно меньше работы было бы для генерации закрытого типа байт-кода (и для этого частного интерпретатора в Python.код) - но тогда lambda (что даже в заголовке вашего вопроса, поясняющего, что вы полностью поглощены важностью использования этого ключевого слова в решении) было бы еще безумнееиспользовать (поскольку очевидный подход к реализации этого варианта заключается в том, чтобы вместо этого возвращать в качестве «функции» связанный метод экземпляра соответствующего пользовательского класса - чтобы данные «bytecode» и простой интерпретатор в python могли быть соответствующим образомсвязаны друг с другом).

lambda выжили в Python (и неохотно, потому что Гвидо изначально стремился удалить его при переходе к Python 3, и спал только тогда, когда столкнулся с «массовым восстанием», вызывающим следующую точку... ;-) по одной причине: потому что есть большое количествотривиально простых задач (функция, возвращающая константу, одна, возвращающая в точности свой аргумент и т. д.), которые очень короткие lambda, с , все ограничения, связанные с его телом, являютсяпросто выражение, удобное для выполнения.Поэтому растягивать лямбды в Python за пределами этой чрезвычайно ограниченной роли (как вы, очевидно, очень хотите) - это довольно плохая идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...