stringtranslate.com

Objeto de función

En programación de computadoras , 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 functores (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 de procedimiento , como C , se puede realizar utilizando punteros de función . [2] Sin embargo, puede resultar 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 en realidad 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, soportan 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++

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 pedido 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 principal ( vacío ) { int elementos [] = { 4 , 3 , 1 , 2 }; qsort ( elementos , tamaño de ( elementos ) / tamaño de ( elementos [ 0 ]), tamaño de ( elementos [ 0 ]), compareInts ); devolver 0 ; }                                

En C++, se puede usar un objeto de función en lugar de una función ordinaria definiendo una clase que sobrecargue el operador de llamada de función definiendo 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 { operador bool ()( const int & a , const int & b ) const { return a < b ; } };               int principal () { std :: vector < int > elementos { 4 , 3 , 1 , 2 }; std :: ordenar ( elementos . comenzar (), elementos . fin (), IntComparator ()); devolver 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 otros miembros (datos o funciones) del objeto. Por supuesto, este es sólo un ejemplo trivial. Para comprender qué poder proporciona un funtor más que una función normal, considere el caso de uso común de ordenar objetos por un campo 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.

estructura Comparar por { const std :: cadena SORT_FIELD ; CompareBy ( const std :: string & sort_field = "nombre" ) : SORT_FIELD ( sort_field ) { /* validar sort_field */ } operador bool ()( const Empleado & a , const Empleado & b ) { if ( SORT_FIELD == "nombre " ) devolver un . nombre < b . nombre ; de lo contrario, si ( SORT_FIELD == "edad" ) devuelve un . edad < b . edad ; de lo contrario , si ( SORT_FIELD == "idnum" ) devuelve un . núm id < b . idnum ; else /* lanzar una excepción o algo así */ } };                                                  int main ( ) { std :: vector <Empleado> emps ; _ /* código para completar la base de datos */ // Ordena la base de datos por número de identificación del empleado std :: sort ( emps . begin (), emps . end (), CompareBy ( "idnum" )); devolver 0 ; }             

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

int main ( ) { std :: vector <Empleado> emps ; _ /* código para completar 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 el campo */ }); devolver 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 funtor normalmente no se utiliza en relación con el objeto de función. Siguiendo con el ejemplo,

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

Además de los funtores de tipo clase, en C++ también son posibles otros tipos de objetos de función. Pueden aprovechar las funciones de plantilla o puntero de miembro 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 cv-qualifiers , 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 pueden llamarse 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 ; } usando T = decltype ( comparar ); operador T * () const { retorno comparar ; } };                          int principal () { std :: vector < int > elementos { 4 , 3 , 1 , 2 }; std :: ordenar ( elementos . comenzar (), elementos . fin (), IntComparator ()); devolver 0 ; }              

Manteniendo el estado

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 <algoritmo> #include <iostream> #include <iterador>   clase CountFrom { public : CountFrom ( int count ) : count_ ( count ) {} int operador ()() { return count_ ++ ; }                privado : int recuento_ ; };  int principal () { 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 <algoritmo> #include <iostream> #include <iterador>   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 mediante delegados . Un delegado se puede declarar utilizando 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 Sistema ; usando System.Collections.Generic ;  clase pública ComparisonClass1 { public static int CompareFunction ( int x , int y ) { return x - y ; }                public static void Main () { var elementos = nueva Lista < int > { 4 , 3 , 1 , 2 }; Comparación <int> del = CompareFunction ; _ _ elementos . Ordenar ( del ); } }                     

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

usando Sistema ; usando System.Collections.Generic ;  clase pública ComparisonClass2 { public static void Main () { var elementos = nueva Lista < int > { 4 , 3 , 1 , 2 }; elementos . Ordenar (( x , y ) => x - y ); } }                         

en re

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

bool buscar ( T ) ( T [] pajar , bool delegado ( T ) prueba_aguja ) { foreach ( paja ; pajar ) { if ( prueba_aguja ( paja )) devuelve verdadero ; } falso retorno ; }                 void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; aguja interna = 123 ; bool agujaTest ( int n ) { return n == aguja ; } afirmar ( buscar ( pajar y prueba de aguja ) ); }                           

El compilador determina de forma automática y conservadora la diferencia entre un delegado y un cierre en D. D también admite funciones literales, que permiten una definición de estilo lambda:

void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; aguja interna = 123 ; afirmar ( buscar ( pajar , ( int n ) { retorno n == aguja ; })); }                         

Para permitir que el compilador incorpore el código (ver arriba), los objetos de función también se pueden especificar al estilo C++ mediante la sobrecarga de operadores :

bool buscar ( T , F ) ( T [] pajar , F prueba_aguja ) { foreach ( paja ; pajar ) { if ( prueba_aguja ( paja )) devuelve verdadero ; } falso retorno ; }                 void main () { int [] pajar = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; aguja interna = 123 ; clase PruebaAguja { int aguja ; this ( int n ) { aguja = n ; } bool opCall ( int n ) { return n == aguja ; } } afirmar ( buscar ( pajar , nuevo NeedleTest ( aguja ))); }                                         

En Eiffel

En el método y lenguaje de desarrollo de software de Eiffel , las operaciones y los objetos siempre se ven como conceptos separados. Sin embargo, el mecanismo del 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 pasarlos como argumentos en llamadas a procedimientos o especificarlos como rutinas de devolución de llamadas. El diseño del mecanismo del agente en Eiffel intenta reflejar la naturaleza orientada a objetos del método y del 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 las más abstractas ROUTINE.

Dentro del texto del software, la palabra clave del idioma agentpermite construir agentes en una 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_botón . seleccionar_acciones . extender ( agente my_gauge . 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) para proporcionar capacidades de programación controlada por eventos .

En otras clases de biblioteca, 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_allde tipo BOOLEANque acepta un agente, una instancia de FUNCTION, como argumento. Así, en el siguiente ejemplo, my_actionse ejecuta sólo si todos los miembros de my_listcontienen el carácter '!':

 mi_lista : LINKED_LIST [ STRING ] ... si mi_lista . for_all ( agente { STRING }. tiene ( '!' )) luego my_action end ...            

Cuando se crean agentes, los argumentos de las rutinas que modelan e incluso el objeto objetivo al que se aplican pueden cerrarse o dejarse abiertos . Los argumentos cerrados y los objetivos reciben valores en el momento de la creación del agente. La asignación de valores para argumentos abiertos y objetivos se difiere hasta algún momento después de que se crea el agente. La rutina for_allespera como argumento un agente que represente una función con un argumento abierto u objetivo 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 un argumento se deja abierto, el carácter de signo de interrogación ('?') se codifica como 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 ) - 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 combinación de argumentos abiertos y objetivos abiertos en agentes utilizados como argumentos para la misma rutina.

 mi_lista : LINKED_LIST [ STRING ] ... mi_lista . do_all ( agente print_on_new_line ( ? )) mi_lista . do_all ( agente { STRING }. to_lower ) mi_lista . do_all ( agente print_on_new_line ( ? )) ...               

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 formato my_list, las convierte a minúsculas y luego las imprime nuevamente.

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 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 para cerrar todos los argumentos excepto el número necesario:

mi lista . do_all ( agente my_multi_arg_procedure ( arg_cerrado_1 , ? , arg_cerrado_2 , arg_cerrado_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 Callableinterfaz), generalmente con la implementación de una clase interna anónima o, a partir de Java 8, una lambda .

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

Lista <Cadena> lista = Matrices . _ _ asList ( "10" , "1" , "20" , "11" , "21" , "12" ); Comparador <Cadena> numStringComparator = nuevo Comparador <Cadena> ( ) { public int comparar ( Cadena cadena1 , Cadena cadena2 ) { retorno Entero . _ _ _ valor de ( str1 ). compareTo ( Entero . valorDe ( str2 )); } };                       Colecciones . ordenar ( lista , numStringComparator ); 

En Java 8+, esto se puede escribir como:

Lista <Cadena> lista = Matrices . _ _ asList ( "10" , "1" , "20" , "11" , "21" , "12" ); Comparador <Cadena> numStringComparator = ( cadena1 , cadena2 ) - > Entero . _ valor de ( str1 ). compareTo ( Entero . valorDe ( 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 ) { var corriente = 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 11          var b = Acumulador ( 42 ); x = segundo ( 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 con tipos, por lo que es posible hacer que cualquier objeto Julia arbitrario sea "invocable" agregando 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 del lenguaje de programación): [5]

julia> estructura mutable Acumulador n :: Int end     julia> función ( acc :: Acumulador ) ( n2 ) acc . n += n2 final      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 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 ) 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 esquema

En los lenguajes de la familia Lisp, como Common Lisp , Scheme y otros, las funciones son objetos, al igual que 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, al igual que el entorno léxico: los enlaces de las variables léxicas visibles se capturan y almacenado en el objeto de función, que más comúnmente se denomina cierre . Los enlaces capturados desempeñan el papel de variables miembro , y la parte de código del cierre desempeña el papel de 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 funtores en lenguajes como C++ son simplemente emulaciones del constructor de cierre que falta. 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, 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.

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

( contador defclass () (( valor :initarg :valor :accesor valor-de )))       ( defmethod funtor-call (( c contador )) ( incf ( valor-de c )))      ( defun make-counter ( valor-inicial ) ( make-instance 'contador : valor valor-inicial ))      ;;; use 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 invocables en Common Lisp, lo falsificamos definiendo una función genérica llamada FUNCTOR-CALL. Esto puede especializarse 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 proporciona objetos de función, que son una construcción de programación de computadora 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 Lisps proporcionan objetos funcallable como una extensión simple. Hacer que los objetos sean invocables usando 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 funciones , 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 mediante un cierre. Esto es mucho más breve y directo. El argumento VALOR INICIAL de la función de fábrica MAKE-COUNTER se captura y utiliza directamente. No es necesario copiarlo en algún objeto de clase auxiliar a través de un constructor. Es el mostrador. Se crea un objeto auxiliar, pero eso sucede detrás de escena .

( defun make-counter ( valor ) ( lambda () ( valor incf )))      ;;; use 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 manera algo más idiomática.

( definir ( valor de contador de creación ) ( lambda () ( establecer! valor ( + valor 1 )) valor )) ;;; use el contador ( defina 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 despacho único se puede realizar completamente con cierres.

Por tanto, existe una especie de túnel que se está cavando a ambos lados de la proverbial montaña. Los programadores en lenguajes OOP descubren objetos de función restringiendo los objetos para que tengan una función principal para cumplir el propósito funcional de ese objeto, ¡e incluso eliminando 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 sea llamado 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 programación orientada a objetos de tipo de despacho único .

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:

// Construir un objeto de función SEL sel = @selector ( myMethod ); NSInvocation * inv = [ NSInvocation invocaciónWithMethodSignature : [ self métodoSignatureForSelector : sel ]]; [ inv setTarget : uno mismo ]; [ inv setSelector : sel ];           // Realizar la invocación real [ inv invoke ]; 

Una ventaja de NSInvocationes que el objeto de destino se puede modificar después de la creación. NSInvocationSe puede crear uno único y luego llamarlo para cada uno de cualquier número de objetivos, por ejemplo, desde un objeto observable. Se NSInvocationpuede crear un solo a partir de un protocolo, pero no es sencillo. Mira 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 ; mi $arg = cambio ; mi $obj = sub { mi $num = cambio ; $arg += $núm ; }; bendice $obj , $clase ; } 1 ;                           

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

paquete Acc2 ; usar sobrecarga '&{}' => sub { my $self = shift ; sub { mi $num = turno ; $self -> { arg } += $núm ; } };                     sub nuevo { mi $clase = turno ; mi $arg = cambio ; mi $obj = { arg => $arg }; bendice $obj , $clase ; } 1 ;                     

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

utilizar Acc1 ; mi $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) :

utilizar Acc2 ; mi $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 y cierres lambda.

función  Acumulador ( $inicio ) {  $actual  =  $inicio ;  función de retorno  ( $x ) uso ( & $actual ) { retorno $actual += $x ; }; }       

Un ejemplo de esto en uso:

$a  =  Acumulador ( 4 ); $x  =  $a ( 5 ); eco  "x = $x <br/>" ; // x = 9 $x  =  $a ( 2 ); eco  "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  Menos {  función pública  __invocar ( $x , $y ) { retorno $x - $y ; } }        $a  =  matriz ( 3 ,  1 ,  4 ); usort ( $a ,  nuevo  Menos ());

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 secuencia de comandos es una instancia de un tipo System.Management.Automation.ScriptBlock de Microsoft .NET Framework .

Función  Get-Accumulator ( $x )  {  {  param ( $y )  return  $x  +=  $y  }. Obtener nuevo cierre () }
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 pitón

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. __call__()Se puede llamar a cualquier objeto con un método utilizando la sintaxis de llamada de función.

Un ejemplo es esta clase de acumulador (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 . norte  =  norte def  __call__ ( yo ,  x ):  yo . n  +=  x  devolver  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 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 objetos semifuncionales: UnboundMethod y block. UnboundMethods primero debe estar vinculado a un objeto (convirtiéndose así en un método) antes de poder usarse como un objeto de función. Los bloques pueden llamarse como objetos de función, pero para usarse en cualquier otra capacidad como objeto (por ejemplo, pasados ​​como argumento), primero deben convertirse a un Proc. Más recientemente, los símbolos (a los que se accede mediante el indicador unario literal :) también se pueden convertir a Procs. Usando el operador unario de Ruby &, equivalente a llamar to_proca un objeto y suponiendo que ese método exista , el Proyecto Ruby Extensions creó un truco simple.

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

Ahora, el método foopuede ser un objeto de función, es decir, a Proc, vía &:fooy utilizado vía 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 generalmente no se usa en Ruby para referirse a un objeto Función. Sólo un tipo de delegación de despacho introducida por el proyecto Ruby Facets se denomina Functor. La definición más básica de la cual 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 funcionales, como ML , y la terminología matemática original.

Otros significados

En un contexto más teórico, se puede considerar que un objeto 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 funcionales 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 funtores utilizados de esta manera son análogos al significado matemático original de funtor 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 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, funtor es sinónimo de símbolo de función .

Ver también

Notas

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

Referencias

  1. ^ ¿ Cuál es la diferencia entre un funtor y un funtor?
  2. ^ 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 analizará 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 analizará más adelante.
  3. ^ 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 analizará inmediatamente después". Sólo 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é.
  4. ^ "Resolución de sobrecarga§Llamada a un objeto de clase". cppreference.com .
  5. ^ Generador acumulador
  6. ^ Documentación PHP sobre métodos mágicos
  7. ^ Generador acumulador
  8. ^ Manual de referencia de Python: definiciones de funciones

Otras lecturas

enlaces externos