En programación informática , un objeto de función [a] es una construcción que permite invocar o llamar a un objeto como si fuera una función ordinaria , normalmente con la misma sintaxis (un parámetro de función que también puede ser una función). Los objetos de función a menudo se denominan functores .
Descripción
Un uso típico de un objeto de función es escribir funciones de devolución de llamada . Se puede realizar una devolución de llamada en lenguajes de procedimiento , como C , mediante punteros de función . [2] Sin embargo, puede ser difícil o incómodo pasar un estado dentro o fuera de la función de devolución de llamada. Esta restricción también inhibe un comportamiento más dinámico de la función. Un objeto de función resuelve esos problemas ya que la función es realmente una fachada para un objeto completo, que lleva su propio estado.
Muchos lenguajes modernos (y algunos más antiguos), por ejemplo, C ++ , Eiffel , Groovy , Lisp , Smalltalk , Perl , PHP , Python , Ruby , Scala y muchos otros, admiten objetos de función de primera clase e incluso pueden hacer un uso significativo de ellos. [3] Los lenguajes de programación funcional también admiten cierres , es decir, funciones de primera clase que pueden "cerrar" las variables en su entorno circundante en el momento de la creación. Durante la compilación, una transformación conocida como elevación lambda convierte los cierres en objetos funcionales.
En C y C ++
Considere el ejemplo de una rutina de clasificación que utiliza una función de devolución de llamada para definir una relación de ordenación entre un par de elementos. El siguiente programa en C usa punteros de función:
#include / * qsort () función de devolución de llamada, devuelve <0 si a 0 si a> b, 0 si a == b * / ,>int compareInts ( const void * a , const void * b ) { return ( * ( int * ) a - * ( int * ) b )); } ... // el prototipo de qsort es // void qsort (void * base, size_t nel, size_t width, int (* compar) (const void *, const void *)); ... int main ( void ) { int elementos [] = { 4 , 3 , 1 , 2 }; qsort ( artículos , tamaño de ( artículos ) / tamaño de ( artículos [ 0 ]), tamaño de ( artículos [ 0 ]), compareInts ); return 0 ; }
En C ++, se puede usar un objeto de función en lugar de una función ordinaria definiendo una clase que sobrecarga el operador de llamada de función al definir una operator()
función miembro. En C ++, esto puede aparecer de la siguiente manera:
// predicado del comparador: devuelve verdadero si a ,>struct IntComparator { bool operator () ( const int & a , const int & b ) const { return a < b ; } };int main () { estándar :: vector < int > elementos { 4 , 3 , 1 , 2 }; std :: sort ( elementos . begin (), elementos . end (), IntComparator ()); return 0 ; }
Observe que la sintaxis para proporcionar la devolución de llamada a la std::sort()
función es idéntica, pero se pasa un objeto en lugar de un puntero de función. Cuando se invoca, la función de devolución de llamada se ejecuta como cualquier otra función miembro y, por lo tanto, tiene acceso completo a los demás miembros (datos o funciones) del objeto. Por supuesto, este es solo un ejemplo trivial. Para comprender qué poder proporciona un funtor más que una función regular, considere el caso de uso común de ordenar objetos por un campo en particular. En el siguiente ejemplo, se utiliza un functor para ordenar una base de datos de empleados simple por el número de identificación de cada empleado.
struct CompareBy { const std :: string SORT_FIELD ; CompareBy ( const std :: string & sort_field = "nombre" ) : SORT_FIELD ( sort_field ) { / * validar sort_field * / } bool operator () ( const Employee & a , const Employee & b ) { if ( SORT_FIELD == "name" ) return a . nombre < b . nombrar ; de lo contrario, si ( SORT_FIELD == "edad" ) devuelve a . edad < b . edad ; de lo contrario, si ( SORT_FIELD == "idnum" ) devuelve un . idnum < b . idnum ; else / * lanzar una excepción o algo * / } };int main () { std :: vector < Empleado > emps ; / * código para completar la base de datos * / // Ordenar la base de datos por número de identificación de empleado std :: sort ( EMP . Empiezan (), EMP . Extremo (), CompareBy ( "IDNUM" )); return 0 ; }
En C ++ 11 , la expresión lambda proporciona una forma más sucinta de hacer lo mismo.
int main () { std :: vector < Empleado > emps ; / * código para llenar la base de datos * / const std :: string sort_field = "idnum" ; std :: sort ( emps . begin (), emps . end (), [ & sort_field ] ( const Employee & a , const Employee & b ) { / * código para seleccionar y comparar campo * / }); return 0 ; }
Es posible utilizar objetos de función en situaciones distintas a las funciones de devolución de llamada. En este caso, el término abreviado functor normalmente no se usa para el objeto de función. Continuando con el ejemplo,
IntComparator cpm ; resultado bool = cpm ( a , b );
Además de los functores de tipo de clase, también son posibles otros tipos de objetos de función en C ++. Pueden aprovechar las facilidades de puntero de miembro o plantilla de C ++ . La expresividad de las plantillas permite utilizar algunas técnicas de programación funcional , como definir objetos de función en términos de otros objetos de función (como la composición de funciones ). Gran parte de la biblioteca de plantillas estándar (STL) de C ++ hace un uso intensivo de objetos de función basados en plantillas.
Estado de mantenimiento
Otra ventaja de los objetos de función es su capacidad para mantener un estado que afecta operator()
entre llamadas. Por ejemplo, el siguiente código define un generador que cuenta desde 10 en adelante y se invoca 11 veces.
#include #include #include class CountFrom { public : CountFrom ( int count ) : count_ ( count ) {} int operator () () { return count_ ++ ; } privado : int count_ ; };int main () { const int estado ( 10 ); std :: generate_n ( std :: ostream_iterator < int > ( std :: cout , " \ n " ), 11 , CountFrom ( estado )); }
En C ++ 14 o posterior, el ejemplo anterior podría reescribirse como:
#include #include #include int main () { std :: generate_n ( std :: ostream_iterator < int > ( std :: cout , " \ n " ), 11 , [ count = 10 ] () mutable { return count ++ ; }); }
C ª#
En C # , los objetos de función se declaran a través de delegados . Un delegado se puede declarar mediante un método con nombre o una expresión lambda . A continuación, se muestra un ejemplo que utiliza un método con nombre.
usando el sistema ; utilizando System.Collections.Generic ;public class ComparisonClass1 { public static int CompareFunction ( int x , int y ) { return x - y ; } public static void Main () { var items = new List < int > { 4 , 3 , 1 , 2 }; Comparación < int > del = CompareFunction ; elementos . Ordenar ( eliminar ); } }
A continuación, se muestra un ejemplo que utiliza una expresión lambda.
usando el sistema ; utilizando System.Collections.Generic ;public class ComparisonClass2 { public static void Main () { var items = new List < int > { 4 , 3 , 1 , 2 }; elementos . Ordenar (( x , y ) => x - y ); } }
En D
D proporciona varias formas de declarar objetos de función: estilo Lisp / Python a través de cierres o estilo C # a través de delegados , respectivamente:
bool encontrar ( T ) ( T [] pajar , bool delegado ( T ) prueba_aguja ) { foreach ( pajita ; pajar ) { if ( prueba_aguja ( pajita )) devuelve verdadero ; } devolver falso ; }void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; bool needleTest ( int n ) { return n == aguja ; } aseverar ( buscar ( pajar , & NeedleTest )); }
El compilador determina de forma automática y conservadora la diferencia entre un delegado y un cierre en D. D también admite literales de función, que permiten una definición de estilo lambda:
void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; afirmar ( encontrar ( pajar , ( int n ) { devolver n == aguja ; })); }
Para permitir que el compilador inserte el código en línea (ver arriba), los objetos de función también se pueden especificar al estilo C ++ mediante la sobrecarga del operador :
bool find ( T , F ) ( T [] pajar , F prueba_aguja ) { foreach ( paja ; pajar ) { if ( prueba_aguja ( paja )) return true ; } devolver falso ; }void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; class NeedleTest { int aguja ; esto ( int n ) { aguja = n ; } bool opCall ( int n ) { return n == aguja ; } } aseverar ( encontrar ( pajar , nuevo NeedleTest ( aguja ))); }
En eiffel
En el método y lenguaje de desarrollo de software de Eiffel , las operaciones y los objetos se ven siempre como conceptos separados. Sin embargo, el mecanismo del agente facilita el modelado de operaciones como objetos en tiempo de ejecución. Los agentes satisfacen el rango de aplicación atribuido a los objetos de función, como pasar como argumentos en llamadas de procedimiento o especificar como rutinas de devolución de llamada. El diseño del mecanismo del agente en Eiffel intenta reflejar la naturaleza orientada a objetos del método y el lenguaje. Un agente es un objeto que generalmente es una instancia directa de una de las dos clases de biblioteca, que modelan los dos tipos de rutinas en Eiffel: PROCEDURE
y FUNCTION
. Estas dos clases descienden de las más abstractas ROUTINE
.
Dentro del texto del software, la palabra clave del idioma agent
permite que los agentes se construyan de forma compacta. En el siguiente ejemplo, el objetivo es agregar la acción de adelantar el medidor a la lista de acciones que se ejecutarán en caso de que se haga clic en un botón.
my_button . select_actions . extender ( agente my_gauge . step_forward )
La rutina a la que se hace extend
referencia en el ejemplo anterior es una característica de una clase en una biblioteca de interfaz gráfica de usuario (GUI) para proporcionar capacidades de programación basadas en eventos .
En otras clases de bibliotecas, se considera que los agentes se utilizan para diferentes propósitos. En una biblioteca que soporta estructuras de datos, por ejemplo, una clase que modela estructuras lineales efectúa una cuantificación universal con una función for_all
de tipo BOOLEAN
que acepta un agente, una instancia de FUNCTION
, como argumento. Entonces, en el siguiente ejemplo, my_action
se ejecuta solo si todos los miembros de my_list
contienen el carácter '!':
my_list : LINKED_LIST [ STRING ] ... si my_list . for_all ( agent { STRING }. has ( '!' )) luego my_action end ...
Cuando se crean agentes, los argumentos de las rutinas que modelan e incluso el objeto de destino al que se aplican pueden cerrarse o dejarse abiertos . Los objetivos y argumentos cerrados reciben valores en el momento de la creación del agente. La asignación de valores para objetivos y argumentos abiertos se aplaza hasta algún momento después de que se crea el agente. La rutina for_all
espera como argumento un agente que representa una función con un argumento o objetivo abierto que se ajusta al parámetro genérico real de la estructura ( STRING
en este ejemplo).
Cuando el destino de un agente se deja abierto, el nombre de clase del destino esperado, entre llaves, se sustituye por una referencia de objeto como se muestra en el texto agent {STRING}.has ('!')
del ejemplo anterior. Cuando un argumento se deja abierto, el carácter de signo de interrogación ('?') Se codifica como un marcador de posición para el argumento abierto.
La capacidad de cerrar o dejar objetivos y argumentos abiertos tiene como objetivo mejorar la flexibilidad del mecanismo del agente. Considere una clase que contiene el siguiente procedimiento para imprimir una cadena en la salida estándar después de una nueva línea:
print_on_new_line ( s : STRING ) - Imprime `s 'precedido por una nueva línea do print ( "% N " + s ) end
El siguiente fragmento, que se supone que pertenece a la misma clase, se utiliza print_on_new_line
para demostrar la combinación de argumentos abiertos y objetivos abiertos en agentes utilizados como argumentos para la misma rutina.
my_list : LINKED_LIST [ STRING ] ... my_list . do_all ( agente print_on_new_line ( ? )) my_list . do_all ( agente { STRING }. to_lower ) mi_lista . hacer_todos ( agente print_on_new_line ( ? )) ...
Este ejemplo utiliza el procedimiento do_all
para estructuras lineales, que ejecuta la rutina modelada por un agente para cada elemento de la estructura.
La secuencia de tres instrucciones imprime las cadenas my_list
, las convierte a minúsculas y luego las imprime de nuevo.
El procedimiento do_all
itera a través de la estructura ejecutando la rutina sustituyendo el elemento actual por el argumento abierto (en el caso de los agentes basados en print_on_new_line
) o el objetivo abierto (en el caso del agente basado en to_lower
).
Los argumentos y objetivos abiertos y cerrados también permiten el uso de rutinas que requieren más argumentos de los que se requieren al cerrar todos los argumentos excepto el número necesario:
mi_lista . hacer_todos ( agente my_multi_arg_procedure ( closed_arg_1 , ? , closed_arg_2 , closed_arg_3 )
El mecanismo del agente Eiffel se detalla en el documento estándar Eiffel ISO / ECMA .
En Java
Java no tiene funciones de primera clase , por lo que los objetos de función generalmente se expresan mediante una interfaz con un solo método (más comúnmente la Callable
interfaz), generalmente con la implementación como una clase interna anónima o, a partir de Java 8, una lambda .
Para un ejemplo de la biblioteca estándar de Java, java.util.Collections.sort()
toma un List
y un functor cuya función es comparar objetos en la Lista. Sin funciones de primera clase, la función es parte de la interfaz del comparador. Esto podría usarse de la siguiente manera.
List < String > list = Matrices . asList ( "10" , "1" , "20" , "11" , "21" , "12" );Comparator < String > numStringComparator = new Comparator < String > () { public int compare ( String str1 , String str2 ) { return Integer . valueOf ( cadena1 ). compareTo ( Integer . valueOf ( str2 )); } };Colecciones . sort ( lista , numStringComparator );
En Java 8+, esto se puede escribir como:
List < String > list = Matrices . asList ( "10" , "1" , "20" , "11" , "21" , "12" );Comparator < String > numStringComparator = ( str1 , str2 ) -> Entero . valueOf ( cadena1 ). compareTo ( Integer . valueOf ( str2 ));Colecciones . sort ( lista , numStringComparator );
En JavaScript
En JavaScript , las funciones son objetos de primera clase. JavaScript también admite cierres.
Compare lo siguiente con el siguiente ejemplo de Python.
función Acumulador ( inicio ) { var actual = inicio ; función de retorno ( x ) { retorno actual + = x ; }; }
Un ejemplo de esto en uso:
var a = Acumulador ( 4 ); var x = a ( 5 ); // x tiene valor 9 x = a ( 2 ); // x tiene valor 11var b = acumulador ( 42 ); x = b ( 7 ); // x tiene valor 49 (actual = 49 en cierre b) x = a ( 7 ); // x tiene valor 18 (actual = 18 en el cierre a)
En julia
En Julia , los métodos están asociados con tipos, por lo que es posible hacer que cualquier objeto de Julia arbitrario sea "invocable" agregando métodos a su tipo. (Estos objetos "invocables" a veces se denominan "functores").
Un ejemplo es esta estructura mutable de acumulador (basada en el estudio de Paul Graham sobre la sintaxis y claridad del lenguaje de programación): [4]
julia > estructura mutable Accumulator n :: Int end julia > función ( acc :: Acumulador ) ( n2 ) acc . n + = n2 finaljulia > a = Acumulador ( 4 ) Acumulador ( 4 )julia > a ( 5 ) 9julia > a ( 2 ) 11julia > b = Acumulador ( 42 ) Acumulador ( 42 )julia > b ( 7 ) 49
Un acumulador de este tipo también se puede implementar mediante cierre:
julia > función Acumulador ( n0 ) n = n0 función ( n2 ) n + = n2 end end Acumulador ( función genérica con 1 método ) julia > a = Acumulador ( 4 ) ( :: # 1) (función genérica con 1 método)julia > a ( 5 ) 9julia > a ( 2 ) 11julia > b = Acumulador ( 42 ) ( :: # 1) (función genérica con 1 método)julia > b ( 7 ) 49
En Lisp y Scheme
En los lenguajes de la familia Lisp como Common Lisp , Scheme y otros, las funciones son objetos, como cadenas, vectores, listas y números. Un operador de construcción de cierre crea un objeto de función a partir de una parte del programa: la parte del código dada como argumento al operador es parte de la función, y también lo es el entorno léxico: las vinculaciones de las variables léxicamente visibles se capturan y almacenado en el objeto de función, que se denomina más comúnmente cierre . Los enlaces capturados desempeñan el papel de variables miembro , y la parte del código del cierre desempeña el papel de la función miembro anónima , al igual que operator () en C ++.
El constructor de cierre tiene la sintaxis (lambda (parameters ...) code ...)
. La (parameters ...)
parte permite declarar una interfaz, por lo que la función toma los parámetros declarados. La code ...
parte consta de expresiones que se evalúan cuando se llama al functor.
Muchos usos de functores en lenguajes como C ++ son simplemente emulaciones del constructor de cierre faltante. Dado que el programador no puede construir directamente un cierre, debe definir una clase que tenga todas las variables de estado necesarias y también una función miembro. Luego, construya una instancia de esa clase en su lugar, asegurándose de que todas las variables miembro se inicialicen a través de su constructor. Los valores se derivan precisamente de aquellas variables locales que deberían ser capturadas directamente por un cierre.
Una función-objeto que usa el sistema de clases, sin uso de cierres:
( contador de clase def () (( valor : initarg : valor : valor de acceso de ))) ( defmethod functor-call (( c contador )) ( incf ( valor de c )))( defun make-counter ( valor-inicial ) ( make-instance 'contador : valor valor -inicial ));;; usar el contador: ( defvar * c * ( make-counter 10 )) ( functor-call * c * ) -> 11 ( functor-call * c * ) -> 12
Dado que no hay una forma estándar de hacer objetos funcallable en Lisp, lo falsificamos definiendo una función genérica llamada FUNCTOR-CALL. Esto se puede especializar para cualquier clase. La función FUNCALL estándar no es genérica; solo toma objetos de función.
Es esta función genérica FUNCTOR-CALL la que nos da objetos de función, que son una construcción de programación informática que permite invocar o llamar a un objeto como si fuera una función ordinaria, normalmente con la misma sintaxis. Tenemos casi la misma sintaxis: FUNCTOR-CALL en lugar de FUNCALL. Algunos Lisps proporcionan objetos funcables como una simple extensión. Hacer que los objetos se puedan llamar utilizando la misma sintaxis que las funciones es un asunto bastante trivial. Hacer que un operador de llamada a función funcione con diferentes tipos de cosas de función , ya sean objetos de clase o cierres, no es más complicado que hacer un operador + que funcione con diferentes tipos de números, como enteros, reales o números complejos.
Ahora, un contador implementado usando un cierre. Esto es mucho más breve y directo. El argumento INITIAL-VALUE de la función de fábrica MAKE-COUNTER se captura y se usa directamente. No es necesario copiarlo en algún objeto de clase auxiliar a través de un constructor. Que es el contador. Se crea un objeto auxiliar, pero eso sucede detrás de escena .
( defun make-counter ( valor ) ( lambda () ( valor incf )));;; usar el contador ( defvar * c * ( make-counter 10 )) ( funcall * c * ) ; -> 11 ( funcall * c * ) ; -> 12
Scheme simplifica aún más los cierres, y el código de Scheme tiende a utilizar dicha programación de orden superior de forma algo más idiomática.
( definir ( valor del contador de fabricación ) ( lambda () ( establecer! valor ( + valor 1 )) valor )) ;;; usar el contador ( definir c ( hacer contador 10 )) ( c ) ; -> 11 ( c ) ; -> 12
Se puede crear más de un cierre en el mismo entorno léxico. Un vector de cierres, cada uno implementando un tipo específico de operación, puede emular con bastante fidelidad un objeto que tiene un conjunto de operaciones virtuales. Ese tipo de programación orientada a objetos de envío único se puede realizar completamente con cierres.
Por lo tanto, existe una especie de túnel que se está cavando a ambos lados de la montaña proverbial. Los programadores en lenguajes de programación orientada a objetos descubren objetos de función al restringir los objetos para que tengan una función principal para hacer el propósito funcional de ese objeto, ¡e incluso eliminan su nombre para que parezca que se está llamando al objeto! Si bien los programadores que usan cierres no se sorprenden de que un objeto se llame como una función, descubren que varios cierres que comparten el mismo entorno pueden proporcionar un conjunto completo de operaciones abstractas como una tabla virtual para OOP de tipo de despacho único .
En Objective-C
En Objective-C , se puede crear un objeto de función a partir de la NSInvocation
clase. La construcción de un objeto de función requiere una firma de método, el objeto de destino y el selector de destino. Aquí hay un ejemplo para crear una invocación al objeto actual myMethod
:
// Construir un objeto de función SEL sel = @selector ( myMethod ); NSInvocation * inv = [ NSInvocation invocationWithMethodSignature : [ self methodSignatureForSelector : sel ]]; [ inv setTarget : self ]; [ inv setSelector : sel ];// Realiza la invocación real [ inv invoke ];
Una ventaja NSInvocation
es que el objeto de destino se puede modificar después de su creación. Se NSInvocation
puede crear uno y luego llamarlo para cada uno de cualquier número de objetivos, por ejemplo, de un objeto observable. Se NSInvocation
puede crear un solo a partir de un protocolo, pero no es sencillo. Vea aquí .
En Perl
En Perl , se puede crear un objeto de función a partir del constructor de una clase que devuelve una función cerrada sobre los datos de instancia del objeto, bendecida en la clase:
paquete Acc1 ; sub nuevo { mi $ clase = turno ; my $ arg = turno ; my $ obj = sub { my $ num = shift ; $ arg + = $ num ; }; bendiga $ obj , $ clase ; } 1 ;
o sobrecargando al &{}
operador para que el objeto se pueda utilizar como función:
paquete Acc2 ; use overload '& {}' => sub { my $ self = shift ; sub { mi $ num = turno ; $ self -> { arg } + = $ num ; } };sub nuevo { mi $ clase = turno ; my $ arg = turno ; my $ obj = { arg => $ arg }; bendiga $ obj , $ clase ; } 1 ;
En ambos casos, el objeto de función se puede usar usando la sintaxis de flecha de eliminación de referencias $ ref -> (@ argumentos) :
use Acc1 ; my $ a = Acc1 -> nuevo ( 42 ); imprimir $ a -> ( 10 ), "\ n" ; # imprime 52 print $ a -> ( 8 ), "\ n" ; # impresiones 60
o usando la sintaxis de desreferenciación de coderef & $ ref (@arguments) :
use Acc2 ; my $ a = Acc2 -> nuevo ( 12 ); imprimir & $ a ( 10 ), "\ n" ; # imprime 22 print & $ a ( 8 ), "\ n" ; # impresiones 30
En PHP
PHP 5.3+ tiene funciones de primera clase que se pueden usar, por ejemplo, como parámetro para la función usort ():
$ a = matriz ( 3 , 1 , 4 ); usort ( $ a , function ( $ x , $ y ) { return $ x - $ y ; });
PHP 5.3+, también admite funciones lambda y cierres.
función Acumulador ( $ inicio ) { $ actual = $ inicio ; función de retorno ( $ x ) use ( & $ actual ) { retorno $ actual + = $ x ; }; }
Un ejemplo de esto en uso:
$ a = Acumulador ( 4 ); $ x = $ a ( 5 ); echo "x = $ x
" ; // x = 9 $ x = $ a ( 2 ); echo "x = $ x
" ; // x = 11
También es posible en PHP 5.3+ hacer que los objetos sean invocables agregando un método mágico __invoke () a su clase: [5]
class Minus { función pública __invoke ( $ x , $ y ) { return $ x - $ y ; } } $ a = matriz ( 3 , 1 , 4 ); usort ( $ a , nuevo Minus ());
En PowerShell
En el lenguaje de Windows PowerShell , un bloque de script es una colección de declaraciones o expresiones que se pueden usar como una sola unidad. Un bloque de script puede aceptar argumentos y devolver valores. Un bloque de script es una instancia de Microsoft .NET Framework tipo System.Management.Automation.ScriptBlock.
Función Get-Accumulator ( $ x ) { { param ( $ y ) return $ x + = $ y }. GetNewClosure () }
PS C: \> $ a = Get-Accumulator 4 PS C: \> & $ a 5 9 PS C: \> & $ a 2 11 PS C: \> $ b = Get-Accumulator 32 PS C: \> & $ b 10 42
En Python
En Python , las funciones son objetos de primera clase, como cadenas, números, listas, etc. Esta característica elimina la necesidad de escribir un objeto de función en muchos casos. Se __call__()
puede llamar a cualquier objeto con un método utilizando la sintaxis de llamada a función.
Un ejemplo es esta clase acumuladora (basada en el estudio de Paul Graham sobre la sintaxis y la claridad del lenguaje de programación): [6]
clase Acumulador : def __init__ ( self , n ) -> None : self . n = n def __call__ ( self , x ): self . n + = x devuelve uno mismo . norte
Un ejemplo de esto en uso (usando el intérprete interactivo):
>>> a = Acumulador ( 4 ) >>> a ( 5 ) 9 >>> a ( 2 ) 11 >>> b = Acumulador ( 42 ) >>> b ( 7 ) 49
Dado que las funciones son objetos, también pueden definirse localmente, recibir atributos y ser devueltos por otras funciones, [7] como se demuestra en el siguiente ejemplo:
def Acumulador ( n ): def inc ( x ): no local n n + = x return n return inc
En rubí
En Ruby , varios objetos pueden considerarse objetos de función, en particular los objetos Method y Proc. Ruby también tiene dos tipos de objetos que pueden considerarse objetos de semifunción: UnboundMethod y block. Los UnboundMethods deben estar vinculados primero a un objeto (convirtiéndose así en un Método) antes de que puedan usarse como un objeto de función. Los bloques se pueden llamar como objetos de función, pero para ser usados en cualquier otra capacidad como un objeto (por ejemplo, pasados como un argumento) primero deben convertirse a un Proc. Más recientemente, los símbolos (a los que se accede a través del indicador unario literal :
) también se pueden convertir en Proc
s. Usando el &
operador unario de Ruby, equivalente a llamar to_proc
a un objeto y asumiendo que ese método existe , el Proyecto de Extensiones de Ruby creó un truco simple.
class Symbol def to_proc proc { | obj , * args | obj . enviar ( self , * args ) } end end
Ahora, el método foo
puede ser un objeto de función, es decir Proc
, a , via &:foo
y usado via takes_a_functor(&:foo)
. Symbol.to_proc
fue agregado oficialmente a Ruby el 11 de junio de 2006 durante RubyKaigi2006. [1]
Debido a la variedad de formas, el término Functor no se usa generalmente en Ruby para referirse a un objeto Function. Solo un tipo de delegación de envío introducida por el proyecto Ruby Facets se denomina Functor. La definición más básica de la cual es:
class Functor def initialize ( & func ) @func = func end def method_missing ( op , * args , & blk ) @func . call ( op , * args , & blk ) end end
Este uso es más parecido al utilizado por los lenguajes de programación funcional, como ML , y la terminología matemática original.
Otros significados
En un contexto más teórico, se puede considerar que un objeto de función es cualquier instancia de la clase de funciones, especialmente en lenguajes como Common Lisp en el que las funciones son objetos de primera clase .
La familia ML de lenguajes de programación funcional utiliza el término functor para representar un mapeo de módulos a módulos, o de tipos a tipos, y es una técnica para reutilizar código. Los functores usados de esta manera son análogos al significado matemático original de functor en la teoría de categorías , o al uso de programación genérica en C ++, Java o Ada .
En Haskell , el término se usa en el mismo sentido que en la teoría de categorías.
En Prolog y lenguajes relacionados, functor es sinónimo de símbolo de función .
Ver también
- Devolución de llamada (informática)
- Cierre (informática)
- Puntero de función
- Función de orden superior
- Patrón de comando
- Zurra
Notas
- ^ En C ++, un functionoid es un objeto que tiene un método principal, y un functor es un caso especial de functionoid. [1] Son similares a un objeto de función, pero no lo mismo .
Referencias
- ^ ¿Cuál es la diferencia entre un functionoide y un funtor?
- ^ Silan Liu. "Tutorial de C ++ Parte I - Básico: 5.10 Los punteros de función se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá inmediatamente después" . TRÍPODE: Tutoriales de programación Copyright © Silan Liu 2002 . Consultado el 7 de septiembre de 2012 .
Los punteros de función se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá a continuación.
- ^ Paweł Turlejski (2 de octubre de 2009). "Tutorial de C ++ Parte I - Básico: 5.10 Los punteros de función se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá inmediatamente después" . Solo unas pocas líneas . Consultado el 7 de septiembre de 2012 .
PHP 5.3, junto con muchas otras características, introdujo cierres. Así que ahora finalmente podemos hacer todas las cosas interesantes que los chicos de Ruby / Groovy / Scala / any_modern_language pueden hacer, ¿verdad? Bueno, podemos, pero probablemente no lo haremos ... He aquí por qué.
- ^ Generador acumulador
- ^ Documentación PHP sobre métodos mágicos
- ^ Generador acumulador
- ^ Manual de referencia de Python - Definiciones de funciones
Otras lecturas
- David Vandevoorde y Nicolai M Josuttis (2006). Plantillas C ++: la guía completa , ISBN 0-201-73484-2 : Específicamente, el capítulo 22 está dedicado a los objetos de función.
enlaces externos
- Descripción del repositorio de patrones de Portland
- Problemas de diseño avanzado de C ++: C ++ asíncrono por Kevlin Henney
- Tutoriales del puntero de función de Lars Haendel (2000/2001)
- Artículo " Indicadores de función generalizada " por Herb Sutter
- Algoritmos genéricos para Java
- Funciones PHP - Objetos de función en PHP
- ¿Qué diablos es un functionoide y por qué debería usar uno? (Preguntas frecuentes de C ++)