Esiste un nome per questo costrutto / modello di programmazione funzionale?

3

Ho scritto una funzione e mi piacerebbe scoprire se si tratta di un'implementazione di alcuni pattern o costrutti di programmazione funzionale. Mi piacerebbe scoprire il nome di questo modello o costrutto (se esiste)?

Ho una funzione che prende un elenco di funzioni e fa questo a loro:

    wrap(fn1, fn2, fn3, fn4)
    # returns
    partial(fn4, partial(fn3, partial(fn2, fn1)))

Ci sono forti similitudini con la composizione, la riduzione e altri costrutti di metaprogrammazione fp, dal momento che le funzioni sono organizzate insieme e restituite come un'unica funzione.

Ha anche forti somiglianze con decoratori e gestori di contesto Python poiché fornisce un modo per incapsulare comportamenti pre e post esecuzione in un'unica funzione. Quale fu l'impulso per scrivere questa funzione. Volevo l'abilità fornita dai gestori di contesto, ma volevo essere in grado di definirla in una funzione e di essere in grado di sovrapporre la funzione dopo la funzione.

UPDATE:

Per chiarezza ho aggiunto l'implementazione:


_wrap = partial(reduce, lambda inner, outer: partial(outer, inner))
def wrap(fns):
    fns = tuple(fns)
    if len(fns)==1:
        return fns[0]
    return lambda *a, **kw: _wrap(iter(fns))(*a, **kw)

e un esempio:


def fn_one(fn, *a, **kw):
  print "entered one"
  response = fn(*a, **kw)
  print "exited one"
  return response

def fn_two(fn, *a, **kw):
  print "entered two"
  response = fn(*a, **kw)
  print "exited two"
  return response

def zero():
  print "zero"

wrapped_zero = wrap((zero, fn_one, fn_two))
wrapped_zero() # returns entered two, entered one, zero, exited one, exited two 
    
posta dietbuddha 23.11.2012 - 23:40
fonte

3 risposte

9

tl; dr

Questo è chiamato currying, talvolta anche schoenfinkelization ed è un comportamento predefinito predefinito in Haskell e nei linguaggi derivati. Plausibilmente altre lingue con cui non ho familiarità.

link

In mathematics and computer science, currying is the technique of transforming a function that takes n multiple arguments (or an n-tuple of arguments) in such a way that it can be called as a chain of functions, each with a single argument (partial application). It was originated by Moses Schönfinkel[1] and later re-discovered by Haskell Curry.[2][3] Because of this, some say it would be more correct to name it schönfinkeling.[4][5]

Dettagli non necessari sulla vendita:

Qualsiasi linguaggio con funzioni di ordine superiore può avere questo comportamento imitato, i generici aiutano enormemente a non essere maldestro, ma le lingue meno maldestre per questo sono funzionali con funzionalità specifiche per farlo.

Detto questo, ci sono alcuni articoli su come farlo in varie lingue per illustrare come hai detto che è uno schema:

link

link

link

Per impostazione predefinita in haskell, ogni funzione non accetta più di un valore e restituisce un valore, finché alla fine non assume valori e restituisce un valore.

Questo è scritto come:

someFunc :: inputVal -> (returnVal)

Questo diventa curriculum come:

someFunc :: inputVal1 -> (inputVal2 -> (inputVal3 -> (returnVal))))

In questo esempio, someFunc accetta un valore e restituisce (una funzione che accetta un valore e restituisce (una funzione che accetta un valore e restituisce (un valore)))).

Ciascuna delle funzioni in quella catena assume un valore, l'ultimo non è una funzione in quanto non richiede nulla ma contiene un calcolo (forse 1 + 2, qualcosa che non richiede parametri).

Dico che questo è un comportamento predefinito in Haskell perché in Haskell, il passaggio di 2 valori a someFunc gliene assegnerà un valore e passerà il valore successivo alla funzione restituita, restituendo la terza funzione nella catena.

vale a dire. usando la firma sopra, se ho definito someFunc come:

someFunc x y z = x + y + z

allora lo chiamo come:

plusTwo :: inputVal3 -> (returnVal)
plusTwo = someFunc 1 1

Ora ho catturato la seconda funzione restituita nella catena dandogli un parametro che fa sì che restituisca la prima funzione, e il secondo parametro che ho dato è curried in quella prima funzione restituita causandone la chiamata e restituendo il 3a funzione, che utilizzo come valore per plusTwo

Quindi, in chiusura, currying funziona in questo modo:

data

someFunc a b c d = a + b + c + d
plusOne   = someFunc 1
plusTwo   = someFunc 1 1
plusThree = someFunc 1 1 1
    four  = someFunc 1 1 1 1

Le firme sarebbero:

someFunc :: a -> (b -> (c -> (d -> (e))))
       plusOne :: b -> (c -> (d -> (e)))
             plusTwo :: c -> (d -> (e))
                 plusThree :: d -> (e)
                            four :: e
    
risposta data 24.11.2012 - 00:06
fonte
1

Credo che tu ti stia riferendo a "threading". Ecco una bella spiegazione del Clojure di Michael Fogus.

(-> (Math/sqrt 25) int list)

Can literally be read:

Take the result of (Math/sqrt 25) Feed it into the function int Feed that result into the function list Graphically, this can be viewed as:

(Math/sqrt 25) --5.0--> (int 5.0) --5--> (list 5) 
=> (5) 

Which expands into the following s-expression:

(list (int (Math/sqrt 25)))
    
risposta data 24.11.2012 - 00:06
fonte
0

Non è essenzialmente una composizione di funzione (matematica), solo con ordine di applicazione invertito? Il tuo involucro è associativo a sinistra, comparato a una composizione che di solito è associativo, almeno in Common Lisp.

La notazione regolare sarebbe qualcosa di simile:

fn1∘fn2∘fn3∘fn4(x) = fn1(fn2(fn3(fn4(x))))

Nel tuo caso è invertito:

fn1∘fn2∘fn3∘fn4(x) = fn4(fn3(fn2(fn1(x))))

Questo è simile al threading, descritto nella risposta di Daniel J. Pritchett, solo con l'argomento omesso.

(-> Math/sqrt int list)

    
risposta data 24.11.2012 - 00:35
fonte