Построение динамических функций в Python - PullRequest
2 голосов
/ 07 декабря 2010

Мне нужно создать функцию для произвольного числа пиков, чтобы перейти к процедуре подгонки по методу наименьших квадратов. Для каждого пика в функции есть дополнительный член, т. Е.

Один член со значением 50 возвращает функцию: f(p, x) = p[0]*50

Два члена со значениями в 50, 60 возвращают функцию: f(p, x) = p[0]*50 + p[1]*60*x

Три члена со значениями в 50, 60, 70 возвращает: f(p, x) = p[0]*50 + p[1]*60*x + p[2]*70*x^2

и т.д.

Пара наивных попыток показана ниже,

def foo(vals):
    fn = lambda p, x: 0
    i = 0
    for v in vals:
        fn = lambda p, x : fn(p, x) + p[i] * v * x**i
        i += 1
    return fn
# Causes a recursion error (I think)

Вторая попытка ...

def bar(vals):
    terms = []
    i = 0
    for v in vals:
        terms.append(lambda x, p: p[i] * v * x**i)
        i += 1
    def fn(x, p):
        tvals = [t(x, p) for t in terms]
        sum = 0
        for t in terms:
            sum = sum + t(x, p)
        return sum
    return fn
# Generates the wrong values

Я подозреваю, что это проблема со ссылками, т.е. Python ссылается на объявления списков и т. Д., Но это немного сложно распутать - любая помощь будет принята!

Ответы [ 4 ]

2 голосов
/ 07 декабря 2010

Как насчет:

def foo(vals):
    def f(p,x):
        result=0
        for i,(av,ap) in enumerate(zip(vals,p)):
            result+=av*ap*(x**i)
        return result
    return f

print(foo([50])([2],3))
# f(p,x)=50*2
# 100
print(foo([50,60])([2,3],4))
# f(p,x)=50*2+60*3*x
# 820
1 голос
/ 07 декабря 2010

Замыкания не фиксируют значения переменных во внешних областях во время их создания, они действительно фиксируют эти переменные. [lambda: i for i in range(5)] дает вам пять функций, которые все возвращают 4, потому что все они ссылаются на один и тот же i (то есть 4, когда заканчивается итерация). Вы можете обойти это, используя аргументы по умолчанию (которые являются значениями связывания во время определения функции): [lambda i=i: i for i in range(5)] работает как положено.

Также используйте enumerate. Это, вместе с fn лямбда-выражением, может уменьшить ваш код до всего двух , imo одинаково читаемых строк (я предполагаю, что вторая версия, первая, кажется, разбита во многих отношениях, как указано комментарии):

def bar(vals):
    terms = [lambda x, p, i=i, v=v: p[i] * v * x**i for i, v in enumerate(vals)]
    return lambda x, p: sum(term(x, p) for term in terms)
1 голос
/ 07 декабря 2010

Вы можете написать все это как функцию, которая возвращает замыкание:

def make_function(vals):
    def evaluate(x,p):
        return sum(p[i] * v * x**i
                   for i,v in enumerate(vals))
    return evaluate

Проблема, с которой вы сталкиваетесь с функциями terms, регулярно возникает. Я написал длинное объяснение об этой проблеме раньше, надеюсь, это будет полезно.

Кстати, foo и bar являются синтаксическими переменными, что означает, что они используются для объяснения синтаксиса . Для задач реализации вы должны действительно использовать хорошие имена, которые что-то значат в домене, что часто облегчает понимание проблемы.

0 голосов
/ 07 декабря 2010

Простая модификация сохраняет ссылку на каждый fn в параметре по умолчанию

def foo(vals):
    fn = lambda p, x: 0
    i = 0
    for v in vals:
        fn = lambda p, x, f=fn, i=i: f(p, x) + p[i] * v * x**i
        i += 1
    return fn
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...