¿Cómo escribo una función que devuelve otra función?

Respuestas:

191

Pruebe esto, usando Python:

import math
def make_cylinder_volume_func(r):
    def volume(h):
        return math.pi * r * r * h
    return volume

Úselo así, por ejemplo con radius=10y height=5:

volume_radius_10 = make_cylinder_volume_func(10)
volume_radius_10(5)
=> 1570.7963267948967

Tenga en cuenta que devolver una función fue una simple cuestión de definir una nueva función dentro de la función y devolverla al final, teniendo cuidado de pasar los parámetros apropiados para cada función. Para su información, la técnica de devolver una función de otra función se conoce como curado .

Óscar López
fuente
1
¿Entonces lo 10que pasaste está guardado en algún lugar? ¿Cuándo se recoge la basura?
sudo
4
@sudo echa un vistazo a en.wikipedia.org/wiki/Closure_(computer_programming)
David Hernandez
19

Usando lambdas, también conocidas como funciones anónimas, puede abstraer la volumefunción dentro de la make_cylinder_volume_funcen una sola línea. De ninguna manera diferente a la respuesta de Óscar López, la solución con lambda sigue siendo en cierto sentido 'más funcional'.

Así es como puede escribir la respuesta aceptada usando una expresión lambda:

import math
def make_cylinder_volume_fun(r):
    return lambda h: math.pi * r * r * h

Y luego llame como lo haría con cualquier otra función curry:

volume_radius_1 = make_cylinder_volume_fun(1)
volume_radius_1(1) 
=> 3.141592653589793
DaveIdito
fuente
Me doy cuenta de que está respondiendo a lo que se solicitó, pero por el bien de mi comprensión, si lambda h:se eliminara, ¿la función funcionaría igual?
Schoon
2
@schoon No, no funcionará en este caso. Este es en realidad un caso muy interesante para resaltar la idea de 'alcance variable' y currización de funciones (que básicamente se basa en el alcance variable). La razón por la que no funciona (en mi ejemplo) es porque returnintentará evaluar el resultado antes de regresar, y debido a que es un montón de variables, devolverá algún valor flotante (intente devolver una función y funcionará). lambdale dice que el siguiente código no debe evaluarse y también que el alcance de la variable r se conservará en las funciones devueltas por make_cylinder...
DaveIdito
10

Solo quiero señalar que puedes hacer esto con pymonad

 import pymonad 

 @pymonad.curry
 def add(a, b):
     return a + b

 add5 = add(5)
 add5(4)
 9
Erotémico
fuente
1
from functools import partial add5 = partial(add, 5)Hace exactamente lo mismo
R3ctor
1

Sé que llego demasiado tarde a la fiesta, pero creo que esta solución puede resultarle interesante.

from math import pi
from functools import partial

def cylinder_volume(r, h):
    return pi * r * r * h

make_cylinder_with_radius_2 = partial(cylinder_volume, 2)
make_cylinder_with_height_3 = partial(cylinder_volume, h=3)

print(cylinder_volume(2, 3))            # 37.6991118431
print(make_cylinder_with_radius_2(3))   # 37.6991118431
print(make_cylinder_with_height_3(2))   # 37.6991118431

Aquí hay documentación sobre cómo partialfunciona.

R3ctor
fuente