stringtranslate.com

Objeto de función

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 , 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 de función a menudo se denominan funtores (no relacionados con el concepto de programación funcional ).

Descripción

Un uso típico de un objeto de función es escribir funciones de devolución de llamada . Una devolución de llamada en lenguajes procedimentales , como C , se puede realizar mediante el uso de punteros de función . [2] Sin embargo, puede resultar difícil o complicado 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" 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 de función.

En C y C++

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

#incluir <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 utilizar un objeto de función en lugar de una función normal definiendo una clase que sobrecargue el operador de llamada de función mediante la definición de una operator()función miembro. En C++, esto puede aparecer de la siguiente manera:

// predicado del comparador: devuelve verdadero si a < b, falso en caso contrario struct IntComparator { bool operator () ( const int & a , const int & b ) const { devuelve a < b ; } };               int main () { std :: vector < int > elementos { 4 , 3 , 1 , 2 } ; std :: sort ( elementos.begin ( ) , elementos.end ( ) , IntComparator ( )); return 0 ; }              

Tenga en cuenta 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 funtor 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 Empleado & a , const Empleado & b ) const { if ( SORT_FIELD == "nombre" ) ​​devuelve a . nombre < b . nombre ; de lo contrario if ( SORT_FIELD == "edad" ) devuelve a . edad < b . edad ; de lo contrario if ( SORT_FIELD == "idnum" ) devuelve a . idnum < b . idnum ; de lo contrario /* lanzar una excepción o algo */ } };                                                   int main () { std :: vector < Employee > emps ; /* código para llenar la base de datos */ // Ordenar la base de datos por número de identificación del empleado std :: sort ( emps.begin ( ) , emps.end ( ) , CompareBy ( " idnum" )); return 0 ; }             

En C++11 , la expresión lambda proporciona una forma más concisa de hacer lo mismo.

int main () { std :: vector < Employee > emps ; /* código para rellenar la base de datos */ const std :: string sort_field = "idnum" ; std :: sort ( emps.begin ( ), emps.end ( ), [ &sort_field]( const Employee & a, const Employee & b ) const { / * código para seleccionar y comparar el campo * / } ); return 0 ; }                       


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

IntComparator cpm ; bool resultado = cpm ( a , b );     

Además de los funtores de tipo de clase, también son posibles otros tipos de objetos de función en C++. Pueden aprovechar las funciones de puntero a miembro o de 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.

Otra forma de crear un objeto de función en C++ es definir una función de conversión no explícita a un tipo de puntero de función, un tipo de referencia de función o una referencia a un tipo de puntero de función. Suponiendo que la conversión no descarta los calificadores cv , esto permite que un objeto de ese tipo se use 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 de función: [4]

// predicado del comparador: devuelve verdadero si a < b, falso en caso contrario struct IntComparator { static bool compare ( const int & a , const int & b ) { return a < b ; } using T = decltype ( compare ); operator T * () const { return compare ; } };                          int main () { std :: vector < int > elementos { 4 , 3 , 1 , 2 } ; std :: sort ( elementos.begin ( ) , elementos.end ( ) , IntComparator ( )); return 0 ; }              

Manteniendo el estado

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

#include <algoritmo> #include <iostream> #include <iterador>   clase CountFrom { público : CountFrom ( int count ) : count_ ( count ) {} int operador ()() { devolver count_ ++ ; }                privado : int count_ ; };  int main () { const int estado ( 10 ); std :: generar_n ( std :: ostream_iterator < int > ( std :: cout , " \n " ), 11 , CountFrom ( estado )); }         

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

#include <algoritmo> #include <iostream> #include <iterador>   int main () { std :: generar_n ( std :: ostream_iterator < int > ( std :: cout , " \n " ), 11 , [ conteo = 10 ]() mutable { devolver conteo ++ ; }); }           

Cª#

En C# , los objetos de función se declaran mediante delegados . Un delegado se puede declarar mediante un método con nombre o una expresión lambda . A continuación, se muestra un ejemplo en el que se utiliza un método con nombre.

utilizando System ; utilizando System.Collections.Generic ;  clase pública ComparisonClass1 { pública estática int CompareFunction ( int x , int y ) { devolver x - y ; }                public static void Main () { var items = new Lista < int > { 4 , 3 , 1 , 2 } ; Comparación < int > del = CompareFunction ; items.Sort ( del ) ; } }                     

A continuación se muestra un ejemplo que utiliza una expresión lambda.

utilizando System ; utilizando System.Collections.Generic ;  clase pública ComparisonClass2 { public static void Main () { var items = new List < int > { 4 , 3 , 1 , 2 }; items . Sort (( x , y ) => x - y ); } }                         

En D

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 [] pajar , bool delegate ( T ) prueba_de_aguja ) { foreach ( paja ; pajar ) { if ( prueba_de_aguja ( paja )) devuelve verdadero ; } devuelve falso ; }                 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 de 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 incorpore 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_de_aguja ) { foreach ( paja ; pajar ) { if ( prueba_de_aguja ( paja )) devuelve verdadero ; } devuelve falso ; }                 void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int aguja = 123 ; clase NeedleTest { int aguja ; this ( int n ) { aguja = n ; } bool opCall ( int n ) { return n == aguja ; } } assert ( find ( pajar , new NeedleTest ( aguja ))); }                                         

En Eiffel

En el método y lenguaje de desarrollo de software de Eiffel , las operaciones y los objetos siempre se consideran conceptos separados. Sin embargo, el mecanismo de agente facilita el modelado de operaciones como objetos de tiempo de ejecución. Los agentes satisfacen el rango de aplicación atribuido a los objetos de función, como pasarse como argumentos en llamadas de procedimiento o especificarse como rutinas de devolución de llamada. El diseño del mecanismo de 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: PROCEDUREy FUNCTION. Estas dos clases descienden de la más abstracta ROUTINE.

En el texto de software, la palabra clave language agentpermite construir agentes de forma compacta. En el siguiente ejemplo, el objetivo es agregar la acción de hacer avanzar el indicador a la lista de acciones que se ejecutarán en caso de que se haga clic en un botón.

mi_boton . select_actions . extend ( agente mi_indicador . paso_adelante )  

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) para proporcionar capacidades de programación basadas en 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 una cuantificación universal con una función for_allde tipo BOOLEANque acepta un agente, una instancia de FUNCTION, como argumento. Por lo tanto, 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 . for_all ( 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 estar cerrados o abiertos . A los argumentos y objetivos cerrados se les asignan valores en el momento de la creación del agente. La asignación de valores para argumentos y objetivos abiertos se pospone hasta algún momento 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 un objetivo abierto que se ajuste al parámetro genérico real de la estructura ( STRINGen este ejemplo).

Cuando el objetivo de un agente se deja abierto, el nombre de clase del objetivo esperado, entre llaves, se sustituye por una referencia de objeto, como se muestra en el texto agent {STRING}.has ('!')del ejemplo anterior. Cuando se deja abierto un argumento, el signo de interrogación ('?') se codifica como un marcador de posición para el argumento abierto.

La capacidad de cerrar o dejar abiertos los 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 ) -- Imprimir 's' precedido por una nueva 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 }. bajar ) 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 en my_list, convierte las cadenas 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 cerrando todos los argumentos excepto el número necesario:

mi_lista .do_all ( agente mi_procedimiento_multi_arg ( argumento_cerrado_1 , ? , argumento_cerrado_2 , argumento_cerrado_3 )      

El mecanismo del agente Eiffel se detalla en el documento estándar ISO/ECMA de Eiffel.

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 Callableinterfaz), generalmente con una implementación que es 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()se toma un Listy un funtor cuya función es comparar objetos en la lista. Sin funciones de primera clase, la función es parte de la interfaz Comparator. Esto se podría utilizar de la siguiente manera.

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

En Java 8+, esto se puede escribir como:

Lista < String > lista = Arrays . asList ( "10" , "1" , "20" , "11" , "21" , "12" ); Comparador < String > numStringComparator = ( str1 , str2 ) -> Integer . valueOf ( str1 ). 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 ; return función ( x ) { return 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 11          var b = Accumulator ( 42 ); x = b ( 7 ); // x tiene valor 49 (actual = 49 en el 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 a tipos, por lo que es posible hacer que cualquier objeto de Julia 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): [5]

julia> estructura mutable Acumulador n :: Int fin     julia> función ( acc :: Acumulador )( n2 ) acc . n += n2 fin      julia> a = Acumulador ( 4 ) Acumulador(4)   Julia> a ( 5 ) 9 Julia> a ( 2 ) 11 julia> b = Acumulador ( 42 ) Acumulador(42)   Julia> b ( 7 ) 49 

Un acumulador de este tipo también se puede implementar mediante el cierre:

julia> funcion Acumulador ( n0 ) n = n0 funcion ( n2 ) n += n2 fin fin Acumulador (funcion generica con 1 metodo)           julia> a = Acumulador ( 4 ) (::#1) (función genérica con 1 método)   Julia> a ( 5 ) 9 Julia> a ( 2 ) 11 julia> 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, al igual que las cadenas, los vectores, las listas y los números. Un operador constructor de clausuras 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 almacenan en el objeto de función, que se denomina más comúnmente clausura . Las vinculaciones capturadas desempeñan el papel de variables miembro , y la parte de código de la clausura desempeña el papel de 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 funtor.

Muchos usos de los funtores en lenguajes como C++ son simplemente emulaciones del constructor de clausura faltante. Dado que el programador no puede construir directamente una clausura, 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, 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 una clausura.

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

( defclass contador () (( valor :initarg :valor :accessor valor-de )))       ( defmethod llamada-a-función (( c contador )) ( incf ( valor-de c )))      ( defun make-counter ( valor-inicial ) ( make-instance 'counter :value valor-inicial ))      ;;; usa el contador: ( defvar *c* ( make-counter 10 )) ( functor-call *c* ) --> 11 ( functor-call *c* ) --> 12         

Como no existe una forma estándar de crear objetos que puedan llamarse mediante 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 da los 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 Lisp proporcionan objetos funcallables como una extensión simple. Hacer que los objetos sean invocables utilizando la misma sintaxis que las funciones es un asunto bastante trivial. Hacer que un operador de llamada de 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 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 tiene que copiarse en algún objeto de clase auxiliar a través de un constructor. Es el contador. Se crea un objeto auxiliar, pero eso sucede detrás de escena .

( defun make-counter ( valor ) ( lambda () ( incf valor )))      ;;; usa el contador ( defvar *c* ( make-counter 10 )) ( funcall *c* ) ; --> 11 ( funcall *c* ) ; --> 12       

Scheme hace que los cierres sean aún más simples, y el código de Scheme tiende a utilizar dicha programación de orden superior de forma un poco más idiomática.

( define ( make-counter value ) ( lambda () ( set! value ( + value 1 )) value )) ;;; usa el contador ( define c ( make-counter 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 de los cuales implementa 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.

De este modo, existe una especie de túnel que se está cavando desde ambos lados de la proverbial montaña. Los programadores en lenguajes OOP descubren los objetos de función al restringirlos a una función principal para realizar 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 se llame a un objeto 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 un solo tipo de despacho OOP.

En Objective-C

En Objective-C , se puede crear un objeto de función a partir de la NSInvocationclase. La construcción de un objeto de función requiere una firma de método, el objeto de destino y el selector de destino. A continuación, se muestra un ejemplo para crear una invocación al objeto actual myMethod:

// Construye 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 invocar ]; 

Una ventaja de NSInvocationes que el objeto de destino se puede modificar después de su creación. NSInvocationSe puede crear un único objeto y luego llamarlo para cada uno de cualquier número de destinos, por ejemplo, desde un objeto observable. NSInvocationSe puede crear un objeto desde un solo protocolo, pero no es sencillo. Consulte 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, bendecida en la clase:

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

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

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

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

usar Acc1 ; mi $a = Acc1 -> new ( 42 ); imprimir $a -> ( 10 ), "\n" ; # imprime 52 imprimir $a -> ( 8 ), "\n" ; # imprime 60          

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

usar Acc2 ; mi $a = Acc2 -> nuevo ( 12 ); imprimir & $a ( 10 ), "\n" ; # imprime 22 imprimir & $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 función usort():

$a  =  matriz ( 3 ,  1 ,  4 ); usort ( $a ,  función  ( $x ,  $y )  {  devolver  $x  -  $y ;  });

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

función  Acumulador ( $inicio ) {  $actual  =  $inicio ;  devolver  función ( $x )  usar ( & $actual )  {  devolver  $actual  +=  $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 método mágico __invoke() a su clase: [6]

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

En PowerShell

En el lenguaje Windows PowerShell , un bloque de script es una colección de instrucciones o expresiones que se pueden utilizar como una sola unidad. Un bloque de script puede aceptar argumentos y devolver valores. Un bloque de script es una instancia de un tipo System.Management.Automation.ScriptBlock de Microsoft .NET Framework .

Función  Get-Accumulator ( $x )  {  {  parámetro ( $y )  return  $x  +=  $y  }. GetNewClosure () }
PS C:\> $a  =  Obtener-Acumulador  4 PS C:\> &  $a  5 9 PS C:\> &  $a  2 11 PS C:\> $b  =  Obtener-Acumulador  32 PS C:\> &  $b  10 42

En Python

En Python , las funciones son objetos de primera clase, al igual que las cadenas, los números, las listas, etc. Esta característica elimina la necesidad de escribir un objeto de función en muchos casos. Cualquier objeto con un __call__()método puede llamarse 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 claridad del lenguaje de programación): [7]

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

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 devueltas por otras funciones, [8] 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 objetos Method y Proc. Ruby también tiene dos tipos de objetos que pueden considerarse como objetos semifuncionales: UnboundMethod y block. Los UnboundMethod primero deben estar enlazados a un objeto (convirtiéndose así en un Method) antes de que puedan usarse como un objeto de función. Los blocks pueden ser llamados como objetos de función, pero para ser usados ​​en cualquier otra capacidad como un objeto (por ejemplo, pasados ​​como argumento) primero deben convertirse en un Proc. Más recientemente, los símbolos (a los que se accede a través del indicador unario literal :) también pueden convertirse en Procs. Usando el operador unario de Ruby &, equivalente a llamar to_proca un objeto y asumiendo que el método existe , el Ruby Extensions Project creó un truco simple.

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

Ahora, el método foopuede ser un objeto de función, es decir, un Proc, a través de &:fooy se utiliza a través de takes_a_functor(&:foo). Symbol.to_procse agregó 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 se denomina Functor a un tipo de delegación de despacho introducido por el proyecto Ruby Facets. La 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 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, un objeto función puede considerarse 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 una asignación de módulos a módulos, o de tipos a 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 programación genérica en C++, Java o Ada .

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

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

Véase también

Notas

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

Referencias

  1. ^¿ Cuál es la diferencia entre un funcionoide y un funtor?
  2. ^ Silan Liu. "C++ Tutorial Part I - Basic: 5.10 Function pointers are used mainly to achieve call back technique, which be explained after". TRIPOD: 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 analizará a continuación.
  3. ^ Paweł Turlejski (2009-10-02). "C++ Tutorial Part I - Basic: 5.10 Function pointers are primaryly used to achieve call back technique, which will be explained right after". Solo unas pocas líneas . Consultado el 7 de septiembre de 2012 . PHP 5.3, junto con muchas otras características, introdujo los cierres. Así que ahora finalmente podemos hacer todas las cosas geniales que los chicos de Ruby / Groovy / Scala / any_modern_language pueden hacer, ¿cierto? Bueno, podemos, pero probablemente no lo haremos... Aquí está el por qué.
  4. ^ "Resolución de sobrecarga§Llamada a un objeto de clase". cppreference.com .
  5. ^ Generador de acumuladores
  6. ^ Documentación PHP sobre métodos mágicos
  7. ^ Generador de acumuladores
  8. ^ Manual de referencia de Python: definiciones de funciones

Lectura adicional

Enlaces externos