2012-07-21 22 views
5

Me preguntaron cuál es la relación entre la aplicación de funciones parciales y los cierres. Diría que no hay ninguno, a menos que me esté faltando el punto. Digamos que estoy escribiendo en Python y tengo una muy simple MiSuma función definida de la siguiente manera:Aplicación parcial y cierres

MySum = lambda x, y : x + y; 

Ahora estoy fijando un parámetro para obtener una función con aridad pequeño que devuelve el mismo valor que lo haría MiSuma Vuelta Si lo llamé con los mismos parámetros (aplicación parcial):

MyPartialSum = lambda x : MySum(x, 0); 

que podía hacer el la misma cosa con C:

int MySum(int x, int y) { return x + y; } 
int MyPartialSum(int x) { return MySum(x, 0); } 

Así, la du pregunta mb es: ¿cuál es la diferencia? ¿Por qué debería necesitar cierres para aplicaciones parciales? Estos códigos son equivalentes, no veo cuál es el límite con los cierres y la aplicación parcial.

Respuesta

3

La aplicación parcial es una técnica mediante la cual toma una función existente y un subconjunto de sus argumentos, y produce una nueva función que acepta los argumentos restantes.

En otras palabras, si usted tiene la función F(a, b), una función que aplica la aplicación parcial de a se vería como B(fn, a) donde F(a, b) = B(F, a)(b).

En su ejemplo, simplemente está creando nuevas funciones, en lugar de aplicar una aplicación parcial a la existente.

He aquí un ejemplo en Python:

def curry_first(fn, arg): 
    def foo(*args): 
     return fn(arg, *args) 
    return foo 

Esto crea un cierre sobre la función y los argumentos aducidos. Se devuelve una nueva función que llama a la primera función con nueva firma de argumento. El cierre es importante: permite el acceso fn a arg. Ahora usted puede hacer este tipo de cosas:

add = lambda x, y : x + y; 
add2 = curry_first(add, 2) 
add2(4) # returns 6 

generalmente He oído esto designado currying.

+0

He tratado de aclarar las cosas leyendo muchas cosas al respecto: hay mucha confusión entre currying y aplicaciones parciales, que comúnmente se intercambian pero son diferentes. Wikipedia dice: "aplicación parcial (o aplicación de función parcial) se refiere al proceso de fijar una cantidad de argumentos a una función, produciendo otra función de menor tamaño" – Cancer

+0

que es exactamente lo que está sucediendo arriba. Por lo que puedo decir, currying es solo una expresión particular de la forma. – Hamish

+0

Sí, ya veo, su código hace eso, y está bastante claro. Pero ... ¿acaso el mío no lo hace? De manera diferente, es obvio, no estoy pasando funciones como parámetros, pero también estoy arreglando un argumento y creando una función con arity menor que hace lo mismo y devuelve el mismo valor que si llamara a la función original con todos los parámetros . Lo siento, me siento bastante tonto, estoy perplejo ... – Cancer

0

Para mí, al usar partialSum de esa manera, me aseguro de que solo depende de una función para sumar los números (MySum) y eso hará que la depuración sea mucho más fácil si las cosas van mal, porque no tendría que preocuparse sobre la lógica de tu código en dos partes diferentes de tu código.

Si en el futuro decide cambiar la lógica de MiSuma, (digamos por ejemplo, hacer que vuelva x + y + 1), entonces usted no tendrá que preocuparse por MyPartialSum porque llama MiSuma

Incluso si parece estúpido, tener un código escrito de esa manera es solo para simplificar el proceso de dependencias en las funciones. Estoy seguro de que lo notarás más adelante en tus estudios.

+0

Ya veo. De todos modos, podría hacer esto, independientemente del idioma que use, sea compatible con cierres o no. Los cierres son bastante inútiles en este caso – Cancer

+0

Sin embargo, esto es solo 'escribir código'. La aplicación parcial debe devolver una * función *, no un resultado. – Hamish

+0

¿Debería? ¡Eso es muy interesante! Entonces, si definí: MyPartialSum = lambda x: lambda x: MySum (x, 0) ¿esa sería una aplicación parcial "verdadera"? Esto no es muy claro para mí – Cancer

1

aplicación de función parcial es sobre la fijación de algunos argumentos de una función dada para producir otra función con menos argumentos, como

sum = lambda x, y: x + y 
inc = lambda x: sum(x, 1) 

en cuenta que 'inc' es 'suma' aplicado parcialmente, sin capturar cualquier cosa de el contexto (como mencionó el cierre).

Pero estas funciones escritas a mano (generalmente anónimas) son un poco tediosas. Uno puede usar una fábrica de funciones, que devuelve una función interna. La función interna se puede parametrizar mediante la captura de alguna variable de su contexto, como

# sum = lambda x, y: x + y 
def makePartialSumF(n): 
    def partialSumF(x): 
     return sum(x, n) 
    return partialSumF 

inc = makePartialSumF(1) 
plusTwo = makePartialSumF(2) 

Aquí la fábrica makePartialSumF se invoca dos veces. Cada llamada da como resultado una función partialSumF (capturando diferentes valores como n). Usar el cierre hace que la implementación de la aplicación parcial sea conveniente. Por lo tanto, puede decir que la aplicación parcial de se puede implementar mediante el cierre. ¡Por supuesto, los cierres pueden hacer muchas otras cosas! (Como un nodo lado, pitón no tiene cierre adecuado.)

Currying consiste en convertir una función de N argumentos en una función unario que devuelve una función unaria ... por ejemplo tenemos una función que tiene tres argumentos y devuelve un valor:

sum = lambda x, y, z: x + y + z 

La versión curry es

curriedSum = lambda x: lambda y: lambda z: x + y + z 

apuesto a que no iba a escribir código Python así. IMO la motivación de Currying es principalmente de interés teórico. (Un marco para expresar cálculos usando solo funciones unarias: cada función es unario!) El subproducto práctico es que, en los idiomas donde las funciones son al curry, algunas aplicaciones parciales (cuando 'arreglas' argumentos desde la izquierda) son tan triviales como suministrando argumentos a la función curried. (Pero no todas las aplicaciones parciales son como tales. Ejemplo: dadas f (x, y, z) = x + 2 * y + 3 * z, cuando se une a y a una constante para producir una función de dos variables). Se puede decir que Currying es una técnica que, en la práctica y como subproducto, puede hacer que muchas aplicaciones funcionales parciales útiles sean triviales, pero ese no es el punto de Currying.

1

Simplemente, el resultado de una aplicación parcial normalmente se implementa como un cierre.

1

Los cierres no son una funcionalidad requerida en un idioma. Estoy experimentando un lenguaje casero, lambdatalk, en el que las lambdas no crean cierres, pero aceptan la aplicación parcial. Por ejemplo, este es como el conjunto [contras, coche, CDR] podría ser definido en el Esquema:

(def cons (lambda (x y) (lambda (m) (m x y)))) 
(def car (lambda (z) (z (lambda (p q) p)))) 
(def cdr (lambda (z) (z (lambda (p q) q)))) 

(car (cons 12 34)) -> 12 
(cdr (cons 12 34)) -> 34 

y en lambdatalk:

{def cons {lambda {:x :y :m} {:m :x :y}}} 
{def car {lambda {:z} {:z {lambda {:x :y} :x}}}} 
{def cdr {lambda {:z} {:z {lambda {:x :y} :y}}}} 

{car {cons 12 34}} -> 12 
{cdr {cons 12 34}} -> 34 

en el Esquema el lambda exterior ahorra x e y en un cierre el lambda interno puede acceder a m dado. En lambdatalk, la lambda guarda: x y: y devuelve una nueva lambda en espera de: m. Entonces, incluso si el cierre (y el alcance léxico) son funcionalidades útiles, no hay necesidad. Sin ninguna variable libre, fuera de cualquier alcance léxico, las funciones son verdaderas cajas negras sin ningún efecto secundario, en total independencia, siguiendo un verdadero paradigma funcional. ¿No lo crees?