2011-02-10 10 views
12

Clojure ha una macro "->" che inserisce ciascuna espressione in modo ricorsivo come primo argomento dell'espressione successiva.Clojure style function "threading" in Python

Questo significa che ho potuto scrivere:

(-> arg f1 f2 f3) 

e si comporta come (tubazioni shell):

f3(f2(f1(arg))) 

desidero fare questo in Python; tuttavia, la ricerca sembra essere un incubo! Non ho potuto cercare "->", e nemmeno potrei cercare la funzione Python threading!

C'è un modo per sovraccaricare, diciamo, il | operatore in modo che potrei scrivere questo in Python?

arg | f1 | f2 | f3 

Grazie!

+0

dependig su come pazzo che si desidera ottenere, potrebbe essere la pena di verificare pitone 'hy' a https://github.com/hylang/hy. – joefromct

risposta

12

È possibile implementare facilmente qualcosa di simile.

def compose(current_value, *args): 
    for func in args: 
     current_value = func(current_value) 
    return current_value 

def double(n): 
    return 2*n 

print compose(5, double, double) # prints 20 
-1

No, non c'è (almeno sanamente). Né vorresti. Perché non scrivere semplicemente f3(f2(f1(arg)))? O meglio ancora, modella il tuo problema in un modo che non richiede ricorsione.

Potrebbe essere possibile sovraccaricare | avvolgendo espressioni in una classe e definendo __or__ in quella classe, ma per favore, per amore di Guido, non farlo.

Si potrebbe anche fare ciò che ha scritto btilly, ma non lo consiglierei neanche. Lavora all'interno di ciò che la lingua ti fornisce.

+1

eh, perché parli di ricorsione dal nulla? –

+0

@Jochen '->' in Clojure è ricorsivo in natura –

+0

So che a Guido non piacciono le tecniche funzionali, ma questo non è un motivo per non dire alle persone a cui piace come usarle in Python. – btilly

16

O forse utilizzare la funzione di ridurre nel modo seguente:

reduce(lambda x,f : f(x), [f1,f2,f3], arg) 
+0

Questo è un modo molto pulito e funzionale di implementarlo. Grazie! Anche gli altri modi che vengono presentati fanno lo stesso, ma "arg" è davanti e penso che faccia molta differenza in termini di flusso della scrittura del codice. – Vimal

+1

@Vimal: se sei preoccupato dell'ordinazione, puoi usare riduci (lambda x, f: f (x), [arg, f1, f2, f3]). – Howard

3

Mentre io simpatizzo con il desiderio di creare nuovi costrutti del linguaggio fresco (à la Lisp macro), in realtà non è il Python filosofia per fare questo:

Ma come hanno detto gli intervistati, è possibile eseguire la propria concatenazione in vari modi. Ecco uno che è forse più esplicitamente Lisp-like, se che si adatta la vostra fantasia:

a = lambda x: x*2 
b = lambda x: x+1 

def chain(first, *args): 
    if len(args) == 0: 
     return first 
    else: 
     return first(chain(*args)) 

print chain(b, a, 1) 
+0

Abbiamo bisogno di estrarre il Lisp da Python, non aggiungerlo di più in ... –

+4

Le influenze della lisp di Python sono ciò che lo rende eccezionale. – Phob

+0

L'appropriatezza di Lisp in Python dipende dal progetto in questione. I dati demografici della potenziale comunità di sviluppatori pesano fortemente qui. Scrivere una libreria di analisi? Lispiness per la vittoria! Fare numeri? Usa Lisp e perdi il 90% del tuo pool di sviluppatori. – MRocklin

8

Sulla soluzione di Howard:

def T(*args): 
    return reduce(lambda l, r: r(l), args) 

def dbl(n): 
    return 2*n 

T(5,dbl,dbl) 
#=> 20 

T(5,dbl,dbl,lambda x: 3*x) 
#=> 60 
1

C'è un thread function nella libreria pytoolz (in realtà ce ne sono due, fanno cose leggermente diverse su funzioni di più ar guments).

Esiste anche un'implementazione cython della libreria pytoolz denominata cytoolz che è probabilmente più efficiente. Può essere installato utilizzando pip.

0

Un po 'tardi alla festa, ma ecco un metodo più pulito, imo.Soddisferà la maggior parte delle esigenze FP.

def stream(*args): 
    return reduce(lambda a, t: t[0](t[1], a), args[1:], args[0]) 

Una mappa di base, filtro, ridurre:

>>> my_list = [1, 2, 3, 4, 5] 
>>> stream(my_list, 
... (map, lambda x: x ** 2), 
... (filter, lambda x: x < 20), 
... (reduce, lambda a, x: a + x)) 
30