Articulo de referencia

Objeto de función

En programación informática , un objeto función [https://isocpp.org/wiki/faq/pointers-to-members#functor-vs-functionoid What's the difference between a functionoid and a functor...

En programación informática , un objeto función [ a ] es una construcción que permite invocar o llamar a un objeto como si fuera una función ordinaria , generalmente con la misma sintaxis (un parámetro de función que también puede ser una función). En algunos lenguajes, particularmente C++, los objetos función suelen denominarse functores (sin relación con el concepto de programación funcional ).

Descripción

Un uso típico de un objeto función es la escritura de funciones de devolución de llamada . En lenguajes procedimentales , como C , una devolución de llamada puede realizarse mediante punteros a funciones . [ 2 ] Sin embargo, puede resultar difícil o engorroso pasar un estado hacia o desde la función de devolución de llamada. Esta restricción también limita el comportamiento dinámico de la función. Un objeto función resuelve estos problemas, ya que la función es en realidad una interfaz para un objeto completo, que contiene su propio estado.

Muchos lenguajes modernos (y algunos más antiguos), como 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" variables en su entorno circundante en el momento de su creación. Durante la compilación, una transformación conocida como elevación lambda convierte los cierres en objetos de función.

En C y C++

Consideremos el ejemplo de una rutina de ordenación que utiliza una función de devolución de llamada para definir una relación de orden entre un par de elementos. El siguiente programa en C/C++ utiliza punteros a funciones:

#include <stdlib.h>// Función de devolución de llamada qsort() // Devuelve < 0 si a < b, > 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 items [] = { 4 , 3 , 1 , 2 }; qsort ( items , sizeof ( items ) / sizeof ( items [ 0 ]), sizeof ( items [ 0 ]), compareInts ); return 0 ; }

En C++, se puede usar un objeto función en lugar de una función ordinaria definiendo una clase que sobrecarga el operador de llamada a función mediante la definición de una operator()función miembro. En C++, esto puede aparecer de la siguiente manera:

importar std ;usando std :: vector ;// predicado comparador: devuelve verdadero si a < b, falso en caso contrario class IntegerComparator { public : bool operator ()( const int & a , const int & b ) const { return a < b ; } };int main () { vector <int> items = { 4 , 3 , 1 , 2 }; std :: ranges :: sort ( items , IntegerComparator ( ) ) ; return 0 ; }

Observe que la sintaxis para proporcionar la std::sort()función de devolución de llamada es idéntica, pero se pasa un objeto en lugar de un puntero a la función. Al invocarse, 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 sencillo. Para comprender la potencia que ofrece un functor en comparación con una función regular, considere el caso de uso común de ordenar objetos por un campo específico. En el siguiente ejemplo, se utiliza un functor para ordenar una base de datos simple de empleados por el número de identificación de cada empleado.

importar std ;usando std :: vector ;clase enumeración SortField { NOMBRE , EDAD , ID }clase CompareBy { private : const SortField SORT_FIELD ; public : explicit CompareBy ( SortField field ) : SORT_FIELD { field } {} bool operator ()( const Employee & a , const Employee & b ) const { switch ( SORT_FIELD ) { case SortField :: NAME : return a . getName () < b . getName (); case SortField :: AGE : return a . getAge () < b . getAge (); case SortField :: ID : return a . getId () < b . getId (); default : std :: unreachable (); } } };int main () { vector <Employee> employees ; // código para llenar la base de datos // Ordenar la base de datos por número de ID de empleado std :: range :: sort ( employees , CompareBy ( SortField :: ID ) ) ; return 0 ; }

El uso de una expresión lambda (introducida en C++11 ) proporciona una forma más concisa de hacer lo mismo.

importar std ;usando std :: vector ;int main () { vector < Employee > employees ;// código para llenar la base de datosconst SortField field = SortField :: ID ; std :: ranges :: sort ( employees , [ & field ]( const Employee & a , const Employee & b ) const- > bool { /* código para seleccionar y comparar campos */ }); return 0 ; }

Es posible utilizar objetos de función en situaciones distintas a las de funciones de devolución de llamada. En este caso, el término abreviado functor normalmente no se utiliza para referirse al objeto de función. Continuando con el ejemplo,

Comparador de enteros cpm ; bool resultado = cpm ( a , b );

Además de los functores de tipo clase, en C++ también son posibles otros tipos de objetos función. Estos pueden aprovechar las funcionalidades de punteros a miembros o plantillas de C++. La expresividad de las plantillas permite utilizar algunas técnicas de programación funcional , como la definición de objetos función en términos de otros objetos función (como la composición de funciones ). Gran parte de la Biblioteca de Plantillas Estándar (STL) de C++ utiliza intensivamente objetos función basados ​​en plantillas.

Otra forma de crear un objeto función en C++ es definir una función de conversión no explícita a un tipo de puntero a función, un tipo de referencia a función o una referencia a un tipo de puntero a función. Suponiendo que la conversión no descarta los calificadores cv , esto permite que un objeto de ese tipo se utilice como una función con la misma firma que el tipo al que se convierte. Modificando un ejemplo anterior para usar esto, obtenemos la siguiente clase, cuyas instancias se pueden llamar como punteros a función: [ 4 ]

importar std ;usando std :: vector ;// predicado comparador: devuelve verdadero si a < b, falso en caso contrario class IntegerComparator { public : static bool compare ( const int & a , const int & b ) noexcept { return a < b ; }usando CompareFn = decltype ( compare );operador CompareFn * () const { return compare ; } };int main () { vector <int> items = { 4 , 3 , 1 , 2 }; std :: ranges :: sort ( items , IntegerComparator ( ) ) ; return 0 ; }

Manteniendo el estado

Otra ventaja de los objetos de función es su capacidad para mantener un estado que se conserva operator()entre llamadas. Por ejemplo, el siguiente código define un generador que cuenta desde 10 hacia arriba y se invoca 11 veces.

importar std ;using std :: cout ; using std :: ostream_iterator ;clase CountFrom { privado : int count ; público : CountFrom ( int count ) : count { count } {} int operator ()() { return count ++ ; } };int main () { const int estado = 10 ; std :: generate_n ( ostream_iterator <int> ( cout , " \n " ) , 11 , CountFrom ( estado ) ) ; }

En C++14 o posterior, el ejemplo anterior podría reescribirse como:

importar std ;using std :: cout ; using std :: ostream_iterator ;int main () { std :: generate_n ( ostream_iterator < int > ( cout , " \n " ), 11 , [ count = 10 ]() mutable -> int { return count ++ ; } ); }

Cª#

En C# , los objetos de función se declaran mediante delegados . Un delegado puede declararse utilizando un método con nombre o una expresión lambda . Aquí se muestra un ejemplo utilizando un método con nombre.

using System ; using System.Collections.Generic ;static int CompareFunction ( int x , int y ) { return x - y ; }Lista < int > items = new ( 4 , 3 , 1 , 2 ); Comparación < int > del = CompareFunction ; items . Sort ( del );

Aquí tienes un ejemplo usando una expresión lambda.

using System ; using System.Collections.Generic ;List < int > items = new ( 4 , 3 , 1 , 2 ); items . Sort (( x , y ) => x - y );

En Re

D proporciona varias formas de declarar objetos de función: al estilo Lisp/Python mediante cierres o al estilo C# mediante delegados , respectivamente:

bool find ( T )( T [] haystack , bool delegate ( T ) needle_test ) { foreach ( straw ; haystack ) { if ( needle_test ( straw )) { return true ; } } return false ; }void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; bool pruebaAguja ( int n ) { return n == aguja ; } assert ( find ( pajar , & pruebaAguja )); }

La diferencia entre un delegado y un cierre en D la determina automáticamente y de forma conservadora el compilador . D también admite literales de función, que permiten una definición al estilo lambda:

void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; assert ( find ( pajar , ( int n ) { return 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 de operadores :

bool find ( T , F )( T [] haystack , F needle_test ) { foreach ( paja ; haystack ) { if ( needle_test ( paja )) { return true ; } } return false ; }void main () { int [] haystack = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int needle = 123 ; class NeedleTest { int needle ;this ( int n ) { needle = n ; }bool opCall ( int n ) { return n == needle ; } } assert ( find ( haystack , new NeedleTest ( needle ))); }

En Eiffel

En el método y lenguaje de desarrollo de software Eiffel , las operaciones y los objetos siempre se consideran conceptos separados. Sin embargo, el mecanismo de agentes 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 ser pasados ​​como argumentos en llamadas procedimentales o especificados como rutinas de devolución de llamada. El diseño del mecanismo de agentes en Eiffel intenta reflejar la naturaleza orientada a objetos del método y 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: PROCEDUREy FUNCTION. Estas dos clases descienden de la más abstracta ROUTINE.

En el código del software, la palabra clave `language` agentpermite construir agentes de forma compacta. En el siguiente ejemplo, el objetivo es añadir la acción de avanzar el indicador a la lista de acciones que se ejecutarán al pulsar un botón.

mi_botón.select_actions.extend ( agente mi_indicador.step_forward )

La rutina extenda la que se hace referencia en el ejemplo anterior es una característica de una clase en una biblioteca de interfaz gráfica de usuario (GUI) que proporciona capacidades de programación orientada a eventos .

En otras clases de biblioteca, se observa que los agentes se utilizan para diferentes propósitos. En una biblioteca que admite estructuras de datos, por ejemplo, una clase que modela estructuras lineales efectúa la cuantificación universal con una función for_allde tipo BOOLEANque acepta un agente, una instancia de FUNCTION, como argumento. Así, en el siguiente ejemplo, my_actionse ejecuta solo si todos los miembros de my_listcontienen el carácter '!':

mi_lista : LISTA_ENLAZADA [ CADENA ] ... si mi_lista.para_todos ( agente { CADENA } .tiene ( ' !' ) ) entonces mi_acción fin ...

Cuando se crean los agentes, los argumentos de las rutinas que modelan, e incluso el objeto de destino al que se aplican, pueden ser cerrados o abiertos . A los argumentos y destinos cerrados se les asignan valores en el momento de la creación del agente. La asignación de valores para los argumentos y destinos abiertos se pospone hasta después de la creación del agente. La rutina for_allespera como argumento un agente que represente una función con un argumento o destino abierto que cumpla con el parámetro genérico real para la estructura ( STRINGen este ejemplo).

Cuando el objetivo de un agente queda sin especificar, el nombre de la clase del objetivo esperado, entre llaves, sustituye a la referencia del objeto, como se muestra en agent {STRING}.has ('!')el ejemplo anterior. Cuando un argumento queda sin especificar, el signo de interrogación ('?') se utiliza como marcador de posición.

La capacidad de cerrar o dejar abiertos objetivos y argumentos 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 un salto de línea do print ( "%N" + s ) end

El siguiente fragmento, que se supone pertenece a la misma clase, se utiliza print_on_new_linepara demostrar la mezcla de argumentos abiertos y objetivos abiertos en agentes utilizados como argumentos para la misma rutina.

mi_lista : LISTA_ENLAZADA [ CADENA ] ... mi_lista.hacer_todo ( agente imprimir_en_nueva_línea ( ? ) ) mi_lista.hacer_todo ( agente { CADENA } .to_lower ) mi_lista.hacer_todo ( agente imprimir_en_nueva_línea ( ? ) ) ...

Este ejemplo utiliza el procedimiento do_allpara estructuras lineales, que ejecuta la rutina modelada por un agente para cada elemento de la estructura.

La secuencia de tres instrucciones imprime las cadenas de texto my_list, las convierte a minúsculas y luego las vuelve a imprimir.

El procedimiento do_allitera 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 por 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 necesarios al cerrar todos excepto el número de argumentos indispensable:

mi_lista . hacer_todo ( agente mi_procedimiento_multi_argumento ( 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 función se suelen expresar mediante una interfaz con un único método (generalmente la Callableinterfaz), cuya implementación suele ser una clase interna anónima o, a partir de Java 8, una expresión lambda .

Como ejemplo de la biblioteca estándar de Java, java.util.Collections.sort()se toma un Listy un functor cuya función es comparar objetos en la lista. Sin funciones de primera clase, la función forma parte de la interfaz Comparator. Esto podría usarse de la siguiente manera.

Lista < Cadena > lista = Arrays . asList ( "10" , "1" , "20" , "11" , "21" , "12" );Comparator < String > numStringComparator = new Comparator < String > () { public int compare ( String str1 , String str2 ) { return Integer . valueOf ( str1 ). compareTo ( Integer . valueOf ( str2 )); } };Colecciones.ordenar ( lista , numStringComparator ) ;

En Java 8+, esto se puede escribir como:

Lista < Cadena > lista = Arrays . asList ( "10" , "1" , "20" , "11" , "21" , "12" );Comparator < String > numStringComparator = ( str1 , str2 ) -> Integer . valueOf ( str1 ). compareTo ( Integer . valueOf ( str2 ));Colecciones.ordenar ( 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 ) { sea actual = inicio return función ( x ) { return actual += x } }

Un ejemplo de esto en uso:

sea ​​a = Acumulador ( 4 ) sea x = a ( 5 ) // x tiene valor 9 x = a ( 2 ) // x tiene valor 11sea ​​b = Acumulador ( 42 ) x = b ( 7 ) // x tiene el valor 49 (actual = 49 en el cierre b) x = a ( 7 ) // x tiene el valor 18 (actual = 18 en el cierre a)

En Julia

En Julia , los métodos están asociados a los tipos, por lo que es posible hacer que cualquier objeto de Julia sea "invocable" añadiéndole métodos a su tipo. (Estos objetos "invocables" a veces se denominan "functores").

Un ejemplo es esta estructura mutable acumuladora (basada en el estudio de Paul Graham sobre la sintaxis y la claridad de los lenguajes de programación): [ 5 ]

julia> mutable struct Accumulator n :: Int endjulia> función ( acc :: Acumulador )( n2 ) acc . n += n2 finjulia> a = Acumulador ( 4 ) Acumulador(4)julia> a ( 5 ) 9julia> a ( 2 ) 11julia> b = Acumulador ( 42 ) Acumulador(42)julia> b ( 7 ) 49

Dicho acumulador también puede implementarse utilizando un cierre:

julia> función Acumulador ( n0 ) n = n0 función ( n2 ) n += n2 fin fin 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 lenguajes de la familia Lisp, como Common Lisp , Scheme y otros, las funciones son objetos, al igual que las cadenas, los vectores, las listas y los números. Un operador de construcción de cierres crea un objeto función a partir de una parte del programa: la parte del código que se le pasa como argumento forma parte de la función, al igual que el entorno léxico: las vinculaciones de las variables visibles léxicamente se capturan y se almacenan en el objeto función, que comúnmente se denomina cierre . Las vinculaciones capturadas actúan como variables miembro , y la parte del código del cierre actúa como la función miembro anónima , al igual que el operador () en C++.

El constructor de cierre tiene la sintaxis (lambda (parameters ...) code ...). La (parameters ...)parte permite declarar una interfaz, de modo que la función tome 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 que falta. Dado que el programador no puede construir un cierre directamente, debe definir una clase que contenga todas las variables de estado necesarias, así como una función miembro. Luego, debe crear una instancia de esa clase, asegurándose de que todas las variables miembro se inicialicen mediante su constructor. Los valores se derivan precisamente de aquellas variables locales que deberían ser capturadas directamente por un cierre.

Un objeto función que utiliza el sistema de clases de Common Lisp, sin usar cierres:

( defclass counter () (( valor :initarg :valor :accesor valor-de )))( defmethod functor-call (( c counter )) ( incf ( value-of c )))( defun make-counter ( initial-value ) ( make-instance 'counter :value initial-value ));;; usar el contador: ( defvar *c* ( make-counter 10 )) ( functor-call *c* ) --> 11 ( functor-call *c* ) --> 12

Dado que no existe una forma estándar de crear objetos que admitan funciones en Common Lisp, lo simulamos definiendo una función genérica llamada FUNCTOR-CALL. Esta función puede especializarse para cualquier clase. La función FUNCALL estándar no es genérica; solo acepta objetos de función.

Es esta función genérica FUNCTOR-CALL la que nos proporciona objetos función, que son una construcción de programación que permite invocar o llamar a un objeto como si fuera una función ordinaria, generalmente con la misma sintaxis. Tenemos casi la misma sintaxis: FUNCTOR-CALL en lugar de FUNCALL. Algunos lenguajes Lisp proporcionan objetos funcallables como una extensión sencilla. Hacer que los objetos sean invocables usando la misma sintaxis que las funciones es bastante trivial. Hacer que un operador de llamada a función funcione con diferentes tipos de elementos de función , ya sean objetos de clase o cierres, no es más complicado que hacer que un operador + funcione con diferentes tipos de números, como enteros, reales o complejos.

Ahora bien, veamos un contador implementado mediante 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 utiliza directamente. No es necesario copiarlo en un objeto de clase auxiliar mediante un constructor. Es el contador. Se crea un objeto auxiliar, pero esto ocurre en segundo plano .

( defun make-counter ( valor ) ( lambda () ( incf valor )));;; 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 Scheme tiende a utilizar este tipo de programación de orden superior de una manera algo más idiomática.

( define ( make-counter value ) ( lambda () ( set! value ( + value 1 )) value )) ;;; usar el contador ( define c ( make-counter 10 )) ( c ) ; --> 11 ( c ) ; --> 12

Se pueden crear varias clausuras en el mismo entorno léxico. Un vector de clausuras, cada una implementando un tipo específico de operación, puede emular fielmente un objeto con un conjunto de operaciones virtuales. Este tipo de programación orientada a objetos con despacho único se puede realizar completamente con clausuras.

Así, se abre una especie de túnel desde ambos lados de la proverbial montaña. Los programadores en lenguajes de POO descubren los objetos función al restringir los objetos a una única función principal que cumpla su propósito funcional, e incluso eliminan su nombre para que parezca que se está llamando al objeto. Si bien a los programadores que usan cierres no les sorprende que un objeto se llame como una función, descubren que múltiples cierres que comparten el mismo entorno pueden proporcionar un conjunto completo de operaciones abstractas, como una tabla virtual para la POO de despacho único .

En Objective-C

En Objective-C , se puede crear un objeto función a partir de una NSInvocationclase. La construcción de un objeto función requiere la firma del 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 ];// Realizar la invocación real [ inv invoke ];

Una ventaja es NSInvocationque el objeto de destino puede modificarse después de su creación. NSInvocationSe puede crear un único objeto y luego llamarlo para cada uno de cualquier número de objetivos, por ejemplo, desde un objeto observable. Se NSInvocationpuede crear un objeto a partir de un protocolo, pero no es sencillo. Véase aquí .

En Perl

En Perl , un objeto de función se puede crear a partir del constructor de una clase que devuelve una función cerrada sobre los datos de instancia del objeto, bendecidos en la clase:

paquete Acc1 ; sub nuevo { mi $class = shift ; mi $arg = shift ; mi $obj = sub { mi $num = shift ; $arg += $num ; }; bendecir $obj , $class ; } 1 ;

o sobrecargando el &{}operador para que el objeto pueda usarse como una función:

paquete Acc2 ; usar sobrecarga '&{}' => sub { mi $self = shift ; sub { mi $num = shift ; $self -> { arg } += $num ; } };sub nuevo { mi $class = shift ; mi $arg = shift ; mi $obj = { arg => $arg }; bendecir $obj , $class ; } 1 ;

En ambos casos, el objeto de función se puede utilizar utilizando la sintaxis de flecha de desreferenciación $ref->(@arguments) :

use Acc1 ; my $a = Acc1 -> new ( 42 ); print $a -> ( 10 ), "\n" ; # imprime 52 print $a -> ( 8 ), "\n" ; # imprime 60

o utilizando la sintaxis de desreferenciación coderef &$ref(@arguments) :

use Acc2 ; my $a = Acc2 -> new ( 12 ); print & $a ( 10 ), "\n" ; # imprime 22 print & $a ( 8 ), "\n" ; # imprime 30

En PHP

PHP 5.3+ tiene funciones de primera clase que se pueden usar, por ejemplo, como parámetro de la usort()función:

$a = array ( 3 , 1 , 4 ); usort ( $a , function ( $x , $y ) { return $x - $y ; });

PHP 5.3+, también admite funciones lambda y cierres.

función Acumulador ( $start ) { $current = $start ; return función ( $x ) use ( & $current ) { return $current += $x ; }; }

Un ejemplo de esto en uso:

$a = Acumulador ( 4 ); $x = $a ( 5 ); echo "x = $x <br/>" ; // x = 9 $x = $a ( 2 ); echo "x = $x <br/>" ; // x = 11

También es posible en PHP 5.3+ hacer que los objetos sean invocables agregando un __invoke()método mágico a su clase: [ 6 ]

clase Minus { función pública __invoke ( $x , $y ) { return $x - $y ; } }$a = array ( 3 , 1 , 4 ); usort ( $a , new Minus ());

En PowerShell

En el lenguaje Windows PowerShell , un bloque de script es una colección de instrucciones 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 del tipo System.Management.Automation.ScriptBlock del Microsoft .NET Framework .

Función ObtenerAcumulador ( $x ) { { parámetro ( $y ) devolver $x += $y }. ObtenerNuevoCierre () }
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, al igual que las cadenas de texto, los números, las listas, etc. Esta característica elimina la necesidad de escribir un objeto función en muchos casos. Cualquier objeto con un __call__()método puede ser llamado mediante 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 de los lenguajes de programación): [ 7 ]

clase Acumulador : def __init __ ( self , n : int ) - > None : self.n = ndef __call__ ( self , x : int ) -> int : self . n += x return self . n

Un ejemplo de esto en uso:

a : Acumulador = Acumulador ( 4 ) print ( a ( 5 )) # imprime: 9 print ( a ( 2 )) # imprime: 11 b : Acumulador = Acumulador ( 42 ) print ( b ( 7 )) # imprime: 49

Dado que las funciones son objetos, también pueden definirse localmente, asignarse atributos y ser devueltas por otras funciones, [ 8 ] como se demuestra en el siguiente ejemplo:

def Acumulador ( n : int ) -> Llamable [[ int ], int ]: def inc ( x : int ) -> int : nonlocal n : int n += x return n return inc

En Ruby

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 pensarse como objetos semifuncionales: UnboundMethod y block. Los UnboundMethods primero deben vincularse a un objeto (convirtiéndose así en un Method) antes de que puedan usarse como un objeto de función. Los Blocks pueden llamarse como objetos de función, pero para ser usados ​​en cualquier otra capacidad como un objeto (por ejemplo, pasados ​​como 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 pueden convertirse a s. Usando el operador Procunario de Ruby —equivalente a llamar en un objeto, y asumiendo que ese método existe— el Ruby Extensions Project creó un truco simple.&to_proc

clase Símbolo def to_proc proc { | obj , * args | obj . send ( self , * args ) } end end

Ahora, el método foopuede ser un objeto de función, es decir, un Proc, a través de &:fooy utilizado a través de takes_a_functor(&:foo). Symbol.to_procse agregó oficialmente a Ruby el 11 de junio de 2006, durante RubyKaigi2006.

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 despacho introducido por el proyecto Ruby Facets se denomina Functor. Su definición más básica es:

class Functor def inicializar ( & func ) @func = func end def método_missing ( op , * args , & blk ) @func . llamada ( op , * args , & blk ) fin fin

Este uso se asemeja más al utilizado por los lenguajes de programación funcional, como ML , y a la terminología matemática original.

Otros significados

En un contexto más teórico, un objeto función puede considerarse cualquier instancia de la clase de funciones, especialmente en lenguajes como Common Lisp, en los que las funciones son objetos de primera clase .

La familia de lenguajes de programación funcional ML utiliza el término functor para representar una correspondencia entre módulos o entre tipos, y es una técnica para reutilizar código. Los functores utilizados de esta manera son análogos al significado matemático original de functor en la teoría de categorías , o al uso de la programación genérica en C++, Java o Ada .

En Haskell , el término functor también se utiliza para un concepto relacionado con el significado de functor en la teoría de categorías.

En Prolog y lenguajes relacionados, functor es sinónimo de símbolo de función .

Véase también

Notas

  1. En C++, un functionoid es un objeto que tiene un método principal, y un functor es un caso especial de un functionoid. [ 1 ] Son similares a un objeto función, pero no son lo mismo .

Referencias

  1. ¿Cuál es la diferencia entre un functionoide y un functor?
  2. Silan Liu. "Tutorial de C++ Parte I - Básico: 5.10 Los punteros a funciones se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá justo después" . TRIPOD: Tutoriales de programación Copyright © Silan Liu 2002. Recuperado el 7 de septiembre de 2012. Los punteros a funciones se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá justo después.
  3. Paweł Turlejski (2009-10-02). "Tutorial de C++ Parte I - Básico: 5.10 Los punteros a funciones se utilizan principalmente para lograr la técnica de devolución de llamada, que se discutirá justo después" . Solo unas pocas líneas . Recuperado el 7 de septiembre de 2012. PHP 5.3, junto con muchas otras características, introdujo los cierres. Así que ahora por fin podemos hacer todas las cosas geniales que los chicos de Ruby / Groovy / Scala / cualquier lenguaje moderno pueden hacer, ¿verdad? Bueno, podemos, pero probablemente no lo haremos... He aquí por qué.
  4. "Resolución de sobrecarga§Llamada a un objeto de clase" . cppreference.com .
  5. Generador acumulador
  6. Documentación de PHP sobre métodos mágicos
  7. Generador acumulador
  8. Manual de referencia de Python - Definiciones de funciones

Lecturas adicionales

  • David Vandevoorde y Nicolai M Josuttis (2006). Plantillas C++: la guía completa , ISBN 0-201-73484-2En concreto, el capítulo 22 está dedicado a los objetos de función.
  • Descripción del repositorio de patrones de Portland
  • Problemas de diseño avanzado en C++ - C++ asíncrono. Archivado el 22/09/2020 en Wayback Machine por Kevlin Henney.
  • Tutoriales sobre punteros a funciones de Lars Haendel (2000/2001)
  • Artículo " Punteros de función generalizados " de Herb Sutter
  • Algoritmos genéricos para Java
  • Funtores PHP - Objetos de función en PHP
  • ¿Qué demonios es un functionoid y por qué debería usar uno? (Preguntas frecuentes de C++)