2015-12-14 13 views
17

Ho un'equazione piuttosto lunga che devo integrare utilizzando scipy.integrate.quad e mi chiedevo se c'è un modo per aggiungere le funzioni lambda l'una all'altra. Quello che ho in mente è qualcosa di simileAggiunta di funzioni lambda con lo stesso operatore in python

y = lambda u: u**(-2) + 8 
x = lambda u: numpy.exp(-u) 
f = y + x 
int = scipy.integrate.quad(f, 0, numpy.inf) 

Le equazioni che sto molto usare sono molto più complicato di quanto sto accennando a qui, quindi per migliorare la leggibilità sarebbe utile per spezzare l'equazione in più piccole, più parti gestibili.

C'è un modo per fare con le funzioni lambda? O forse un altro modo che non richiede nemmeno funzioni lambda, ma darà lo stesso risultato?

+3

Non ho familiarità con 'scipy.integrate.quad', ma' f = lambda u: y (u) + x (u) 'sarebbe un modo per aggiungere le due funzioni insieme. –

+5

A parte: perché stai usando 'lambda' affatto se hai intenzione di dare un nome alle tue funzioni immediatamente comunque? – DSM

+0

Onestamente sto usando lambda perché non conosco altre opzioni con la mia esperienza di Python molto limitata, motivo per cui ho aggiunto l'ultima parte alla domanda. – coffeepls

risposta

17

In Python, è' Normalmente usiamo solo un lambda per funzioni molto brevi e semplici che si adattano facilmente alla linea che le sta creando. (Alcune lingue hanno altre opinioni.)

Come suggerito da @DSM nel loro commento, lambda è essenzialmente una scorciatoia per creare funzioni quando non vale la pena dare loro un nome.

Se stai facendo cose più complesse, o se devi dare al codice un nome per riferimento futuro, un'espressione lambda non sarà molto più una scorciatoia per te - invece, potresti anche def ine una semplice vecchia funzione.

Così, invece di assegnare l'espressione lambda ad una variabile:

y = lambda u: u**(-2) + 8 

È possibile definire la variabile per essere una funzione:

def y(u): 
    return u**(-2) + 8 

che ti dà spazio per spiegare un po ', o essere più complesso, o qualunque cosa tu abbia bisogno di fare:

def y(u): 
    """ 
    Bloopinate the input 

    u should be a positive integer for fastest results. 
    """ 
    offset = 8 
    bloop = u ** (-2) 
    return bloop + offset 

Funzioni e lambda sono entrambi "callabl" e ", il che significa che sono essenzialmente intercambiabili per quanto riguarda lo scipy.integrate.quad().

Per combinare le calle, è possibile utilizzare diverse tecniche.

def triple(x): 
    return x * 3 

def square(x): 
    return x * x 

def triple_square(x): 
    return triple(square(x)) 

def triple_plus_square(x): 
    return triple(x) + square(x) 

def triple_plus_square_with_explaining_variables(x): 
    tripled = triple(x) 
    squared = square(x) 
    return tripled + squared 

ci sono opzioni più avanzate che vorrei prendere in considerazione solo se si rende il codice più chiaro (che probabilmente non sarà).Ad esempio, è possibile inserire i callable in un elenco:

all_the_things_i_want_to_do = [triple, square] 

Una volta che sono in una lista, è possibile utilizzare le operazioni basati su elenco di lavorare su di essi (tra cui la loro applicazione a sua volta reduce la lista a un singolo valore).

Ma se il codice è come la maggior parte del codice, le funzioni regolari che si chiamano semplicemente per nome sono le più semplici da scrivere e più facili da leggere.

+2

Questa è probabilmente la soluzione al vero problema dell'OP. – justhalf

+0

Questo è pythonic e testabile. Lambda dovrebbe essere ovviamente corretto. Come scrivere una stringa o aggiungere una costante. Per il codice numerico nella maggior parte dei casi sarà meglio estrarre le funzioni. Inoltre non incoraggerà le variabili del nome singolo come vediamo sopra. – bearrito

11

Non c'è alcuna funzionalità built-in per questo, ma è possibile implementare abbastanza facilmente (con qualche calo di prestazioni, ovviamente):

import numpy 

class Lambda: 

    def __init__(self, func): 
     self._func = func 

    def __add__(self, other): 
     return Lambda(
      lambda *args, **kwds: self._func(*args, **kwds) + other._func(*args, **kwds)) 

    def __call__(self, *args, **kwds): 
     return self._func(*args, **kwds) 

y = Lambda(lambda u: u**(-2) + 8) 
x = Lambda(lambda u: numpy.exp(-u)) 

print((x + y)(1)) 

Altri operatori possono essere aggiunti in modo simile.

+0

Questo può essere utile per creare un DSL completo, ma tieni presente che ci sono molte piccole complicazioni da gestire. Avrai bisogno di fare dei test caso limite davvero approfonditi per farlo funzionare senza intoppi. Per la maggior parte delle applicazioni, probabilmente non ne vale la pena, il che ti lascia con la soluzione di RJHunter. +1 per il modo Python di forzare gli operatori, però. – jpmc26

4

Usa codice sottostante per ricchi stesso risultato con la scrittura come meno codice possibile:

y = lambda u: u**(-2) + 8 
x = lambda u: numpy.exp(-u) 
f = lambda u, x=x, y=y: x(u) + y(u) 
int = scipy.integrate.quad(f, 0, numpy.inf) 
10

Con sympy si può fare un'operazione funzione come questa:

>>> import numpy 
>>> from sympy.utilities.lambdify import lambdify, implemented_function 
>>> from sympy.abc import u 
>>> y = implemented_function('y', lambda u: u**(-2) + 8) 
>>> x = implemented_function('x', lambda u: numpy.exp(-u)) 
>>> f = lambdify(u, y(u) + x(u)) 
>>> f(numpy.array([1,2,3])) 
array([ 9.36787944, 8.13533528, 8.04978707]) 
3

Come programmatore funzionale, vi suggerisco di generalizzare le soluzioni a un applicative combinator:

In [1]: def lift2(h, f, g): return lambda x: h(f(x), g(x)) 
In [2]: from operator import add 
In [3]: from math import exp 
In [4]: y = lambda u: u**(-2) + 8 
In [5]: x = lambda u: exp(-u) 
In [6]: f = lift2(add, y, x) 
In [7]: [f(u) for u in range(1,5)] 
Out[7]: [9.367879441171443, 8.385335283236612, 8.160898179478975, 8.080815638888733] 

Utilizzando lift2, è possibile combinare la potenza di due funzioni che utilizzano funzioni binari arbitrari in modo pointfree. E la maggior parte delle cose in operator dovrebbero probabilmente essere sufficienti per tipiche combinazioni matematiche, evitando di dover scrivere qualsiasi lambda.

In una fasion simile, è possibile definire lift1 e forse anche lift3.