En matemáticas e informática , una función de orden superior ( HOF , por sus siglas en inglés ) es una función que realiza al menos una de las siguientes acciones:
- toma una o más funciones como argumentos (es decir, un parámetro procedimental , que es un parámetro de un procedimiento que es en sí mismo un procedimiento),
- Devuelve una función como resultado.
Todas las demás funciones son de primer orden . En matemáticas, las funciones de orden superior también se denominan operadores o funcionales . El operador diferencial en cálculo es un ejemplo común, ya que asigna a una función su derivada , que también es una función. Las funciones de orden superior no deben confundirse con otros usos del término «functor» en matemáticas; véase Functor (desambiguación) .
En el cálculo lambda sin tipos , todas las funciones son de orden superior; en un cálculo lambda con tipos , del cual se derivan la mayoría de los lenguajes de programación funcional , las funciones de orden superior que toman una función como argumento son valores con tipos de la forma.
Ejemplos generales
- La función map , presente en muchos lenguajes de programación funcional, es un ejemplo de función de orden superior. Recibe como argumentos una función f y una colección de elementos, y como resultado, devuelve una nueva colección con f aplicada a cada elemento de la colección.
- La función `sort` , que toma como parámetro una función de comparación, permite al programador separar el algoritmo de ordenación de las comparaciones de los elementos que se ordenan. La función estándar de C es un ejemplo de esto.
qsort - Función de filtro que toma una colección y una función que devuelve verdadero o falso, y devuelve una nueva colección que son los elementos de la colección de parámetros donde la función devolvió verdadero.
- plegar (incluyendo foldl y foldr )
- escanear
- aplicar
- Composición de la función
- Integración
- Llamar de vuelta
- Recorrido de árboles
- La gramática de Montague , una teoría semántica del lenguaje natural, utiliza funciones de orden superior.
Soporte en lenguajes de programación
Apoyo directo
Los ejemplos no pretenden comparar ni contrastar lenguajes de programación, sino servir como ejemplos de sintaxis de funciones de orden superior.
En los siguientes ejemplos, la función de orden superior twicetoma una función y la aplica dos veces a un valor. Si twicese debe aplicar varias veces para el mismo resultado, fpreferiblemente debería devolver una función en lugar de un valor. Esto concuerda con el principio de " no repetirse ".
APL
dos veces ← { ⍺⍺ ⍺⍺ ⍵ }más tres ← { ⍵ + 3 }g ← { más tres dos veces ⍵ } g 7 13O de manera tácita:
dos veces ← ⍣ 2más tres ← + ∘ 3g ← más tres dos veces g 7 13C++
Uso std::functionen C++11 :
importar std ;auto twice = []( const std :: function < int ( int ) >& f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; };auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = dos veces ( másTres );std :: println ( "{}" , g ( 7 )); // 13 }O bien, con expresiones lambda genéricas proporcionadas por C++14:
importar std ;auto dos veces = []( const auto & f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; };auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = dos veces ( másTres );std :: println ( "{}" , g ( 7 )); // 13 }DO#
Utilizando únicamente delegados:
usando el sistema ;public class Program { public static void Main ( string [] args ) { Func < Func < int , int > , Func < int , int >> twice = f => x => f ( f ( x ));Func < int , int > plusThree = i => i + 3 ;var g = dos veces ( más tres );Console.WriteLine ( g ( 7 ) ); // 13 } }O, de forma equivalente, con métodos estáticos:
usando el sistema ;public class Program { private static Func < int , int > Twice ( Func < int , int > f ) { return x => f ( f ( x )); }privado estático int PlusThree ( int i ) => i + 3 ;public static void Main ( string [] args ) { var g = Twice ( PlusThree );Console.WriteLine ( g ( 7 ) ); // 13 } }Clojure
( defn dos veces [ f ] ( fn [ x ] ( f ( f x ))))( defn más tres [ i ] ( + i 3 ))( def g ( dos veces más tres ))( println ( g 7 )) ; 13Lenguaje de marcado ColdFusion (CFML)
dos veces = función ( f ) { return función ( x ) { return f ( f ( x )); }; };plusThree = function ( i ) { return i + 3 ; };g = dos veces ( más tres );writeOutput ( g ( 7 )); // 13Lisp común
( defun twice ( f ) ( lambda ( x ) ( funcall f ( funcall f x )))) ( defun plus-three ( i ) ( + i 3 )) ( defvar g ( twice #' plus-three )) ( print ( funcall g 7 ))D
import std . stdio : writeln ;alias dos veces = ( f ) => ( int x ) => f ( f ( x ));alias plusThree = ( int i ) => i + 3 ;void main () { auto g = twice ( plusThree );escribir ( g ( 7 )); // 13 }Dardo
Función int ( int ) dos veces ( Función int ( int ) f ) { return ( x ) { return f ( f ( x )); }; }int plusThree ( int i ) { return i + 3 ; }void main () { final g = twice ( plusThree ); print ( g ( 7 )); // 13 }Elixir
En Elixir, puedes combinar definiciones de módulos y funciones anónimas.
defmodule Hof do def twice ( f ) do fn ( x ) -> f . ( f . ( x )) end end endmás_tres = fn ( i ) -> i + 3 fing = Hof.dos veces ( más_tres )IO . puts g . ( 7 ) # 13Alternativamente, también podemos componer utilizando funciones puramente anónimas.
dos veces = fn ( f ) -> fn ( x ) -> f . ( f . ( x )) fin finmás_tres = fn ( i ) -> i + 3 fing = dos veces . ( más tres )IO . puts g . ( 7 ) # 13Erlang
or_else ([], _) -> false ; or_else ([ F | Fs ], X ) -> or_else ( Fs , X , F ( X )).or_else ( Fs , X , false ) -> or_else ( Fs , X ); or_else ( Fs , _, { false , Y }) -> or_else ( Fs , Y ); or_else (_, _, R ) -> R .o_else ([ fun erlang : is_integer / 1 , fun erlang : is_atom / 1 , fun erlang : is_list / 1 ], 3 . 23 ).En este ejemplo de Erlang, la función de orden superior or_else/2toma una lista de funciones ( Fs) y un argumento ( X). Evalúa la función Fcon el argumento Xcomo argumento. Si la función devuelve falso, se evaluará Fla siguiente función en . Si la función devuelve , se evaluará la siguiente función en con el argumento . Si la función devuelve , la función de orden superior devolverá . Tenga en cuenta que , , y pueden ser funciones. El ejemplo devuelve .FsF{false, Y}FsYFRor_else/2RXYRfalse
F#
sea dos veces f = f >> fsea plus_three = (+) 3sea g = dos veces más tresg 7 |> printf "%A" // 13Ir
paquete principalimportar "fmt"func dos veces ( f func ( int ) int ) func ( int ) int { return func ( x int ) int { return f ( f ( x )) } }func main () { plusThree := func ( i int ) int { return i + 3 }g := dos veces ( más tres )fmt.Println ( g ( 7 ) ) // 13 }Nótese que una función literal puede definirse con un identificador ( twice) o de forma anónima (asignada a la variable plusThree).
Genial
def twice = { f , x -> f ( f ( x )) } def plusThree = { it + 3 } def g = twice . curry ( plusThree ) println g ( 7 ) // 13Haskell
dos veces :: ( Int -> Int ) -> ( Int -> Int ) dos veces f = f . fplusThree :: Int -> Int plusThree = ( + 3 )principal :: E/S () principal = imprimir ( g 7 ) -- 13 donde g = dos veces más tresJ
Explícitamente,
dos veces =. adverbio : 'uu y'más tres = verbo : 'y + 3' g = más tres dos veces g 7 13o tácitamente,
dos veces =. ^: 2más tres =. +& 3 g =. más tres dos veces g 7 13Java (1.8+)
Utilizando únicamente interfaces funcionales:
import java.util.function.* ;clase Main { public static void main ( String [] args ) { Function < IntUnaryOperator , IntUnaryOperator > twice = f -> f . andThen ( f );OperadorUnarioEntero másTres = i -> i + 3 ;var g = dos veces.apply ( plusThree ) ;System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }O, de forma equivalente, con métodos estáticos:
import java.util.function.* ;clase Main { private static IntUnaryOperator twice ( IntUnaryOperator f ) { return f . andThen ( f ); }private static int plusThree ( int i ) { return i + 3 ; }public static void main ( String [] args ) { var g = twice ( Main :: plusThree );System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }JavaScript
Con funciones de flecha:
"usar estricto" ;const dos veces = f => x => f ( f ( x ));const plusThree = i => i + 3 ;const g = dos veces ( másTres );console.log ( g ( 7 ) ) ; // 13O con sintaxis clásica:
"usar estricto" ;función dos veces ( f ) { return función ( x ) { return f ( f ( x )); }; }función plusThree ( i ) { return i + 3 ; }const g = dos veces ( másTres );console.log ( g ( 7 ) ) ; // 13Julia
julia> función dos veces ( f ) función resultado ( x ) devolver f ( f ( x )) fin devolver resultado fin dos veces (función genérica con 1 método)julia> plusthree ( i ) = i + 3 plusthree (función genérica con 1 método)julia> g = dos veces ( plusthree ) (::var"#result#3"{typeof(plusthree)}) (función genérica con 1 método)julia> g ( 7 ) 13Kotlin
fun twice ( f : ( Int ) -> Int ): ( Int ) -> Int { return { f ( f ( it )) } }fun plusThree ( i : Int ) = i + 3fun main () { val g = twice ( :: plusThree )println ( g ( 7 )) // 13 }Lua
función dos veces ( f ) devolver función ( x ) devolver f ( f ( x )) fin finfunción plusThree ( i ) devuelve i + 3 fing local = dos veces ( más tres )imprimir ( g ( 7 )) -- 13MATLAB
función resultado = dos veces ( f ) resultado = @( x ) f ( f ( x )); finmástres = @( i ) i + 3 ;g = dos veces ( más tres )disp ( g ( 7 )); % 13OCaml
sea dos veces f x = f ( f x )sea plus_three = (+) 3sea () = sea g = dos veces más tres enprint_int ( g 7 ); (* 13 *) print_newline ()PHP
<?phpdeclarar ( tipos_estrictos = 1 );función dos veces ( invocable $f ) : Cierre { return función ( int $x ) use ( $f ) : int { return $f ( $f ( $x )); }; }función plusThree ( int $i ) : int { return $i + 3 ; }$g = dos veces ( 'másTres' );echo $g ( 7 ), " \n " ; // 13o con todas las funciones en variables:
<?phpdeclarar ( tipos_estrictos = 1 );$twice = fn ( callable $f ) : Closure => fn ( int $x ) : int => $f ( $f ( $x ));$plusThree = fn ( int $i ) : int => $i + 3 ;$g = $dos veces ( $másTres );echo $g ( 7 ), " \n " ; // 13Tenga en cuenta que las funciones flecha capturan implícitamente cualquier variable que provenga del ámbito padre, [ 1 ] mientras que las funciones anónimas requieren la usepalabra clave para hacer lo mismo.
Perl
usar estricto ; usar advertencias ;sub dos veces { mi ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; }sub plusThree { my ( $i ) = @_ ; $i + 3 ; }mi $g = dos veces ( \& másTres );print $g -> ( 7 ), "\n" ; # 13o con todas las funciones en variables:
usar estricto ; usar advertencias ;mi $twice = sub { mi ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; };mi $plusThree = sub { mi ( $i ) = @_ ; $i + 3 ; };mi $g = $dos veces -> ( $másTres );print $g -> ( 7 ), "\n" ; # 13Pitón
def twice ( f : Callable [ Any ]) -> Any : def result ( x : Any ) -> Any : return f ( f ( x )) return resultplus_three : Callable [ int ] = lambda i : i + 3g : int = dos veces ( más_tres )imprimir ( g ( 7 )) # imprime 13La sintaxis de decoradores de Python se usa a menudo para reemplazar una función con el resultado de pasar esa función a través de una función de orden superior. Por ejemplo, la función gpodría implementarse de forma equivalente:
@twice def g ( i : int ) -> int : return i + 3imprimir ( g ( 7 )) # imprime 13R
dos veces <- \ ( f ) \ ( x ) f ( f ( x ))plusThree <- function ( i ) i + 3g <- dos veces ( más tres )> g ( 7 ) [ 1 ] 13Raku
sub dos veces ( Callable:D $f ) { return sub { $f ( $f ( $^x )) }; } sub plusThree ( Int:D $i ) { return $i + 3 ; } mi $g = dos veces ( &másTres ); decir $g ( 7 ); # 13En Raku, todos los objetos de código son cierres y, por lo tanto, pueden hacer referencia a variables internas "léxicas" desde un ámbito externo, ya que la variable léxica está "cerrada" dentro de la función. Raku también admite la sintaxis de "bloque puntiagudo" para expresiones lambda, que se pueden asignar a una variable o invocar de forma anónima.
Rubí
def dos veces ( f ) -> ( x ) { f . call ( f . call ( x )) } finmás_tres = -> ( i ) { i + 3 }g = dos veces ( más tres )puts g . call ( 7 ) # 13Óxido
fn dos veces ( f : impl Fn ( i32 ) -> i32 ) -> impl Fn ( i32 ) -> i32 { mover | x | f ( f ( x )) }fn plus_three ( i : i32 ) -> i32 { i + 3 }fn main () { let g = twice ( plus_three );println! ( "{}" , g ( 7 )) // 13 }Scala
objeto Main { def twice ( f : Int => Int ): Int => Int = f componer fdef plusThree ( i : Int ): Int = i + 3def main ( args : Array [ String ]): Unit = { val g = twice ( plusThree )imprimir ( g ( 7 )) // 13 } }Esquema
( define ( compone f g ) ( lambda ( x ) ( f ( g x ))))( definir ( dos veces f ) ( componer f f ))( define ( más tres i ) ( + i 3 ))( definir g ( dos veces más tres ))( mostrar ( g 7 )) ; 13 ( mostrar " \n " )Rápido
func twice ( _ f : @ escaping ( Int ) -> Int ) -> ( Int ) -> Int { return { f ( f ( $0 )) } }sea plusThree = { $0 + 3 }sea g = dos veces ( más tres )imprimir ( g ( 7 )) // 13Tcl
establecer dos veces {{ f x } {aplicar $f [aplicar $f $x ]}} establecer más tres {{ i } {devolver [expr $i + 3 ]}}# resultado: 13 puts [apply $twice $plusThree 7 ]Tcl utiliza el comando apply para aplicar una función anónima (desde la versión 8.6).
XACML
El estándar XACML define funciones de orden superior para aplicar una función a múltiples valores de conjuntos de atributos.
regla allowEntry { permitir condición anyOfAny(función [ cadenaIgual ], ciudadanías , ciudadanías permitidas ) }La lista de funciones de orden superior en XACML se puede encontrar aquí .
XQuery
declare function local:twice ( $ f , $ x ) { $ f ( $ f ( $ x )) };declarar función local:plusthree ( $ i ) { $ i + 3 };local:dos veces ( local:mástres # 1 , 7 ) (: 13 :)Alternativas
Punteros a funciones
En lenguajes como C , C++ , Fortran y Pascal , los punteros a funciones permiten a los programadores pasar referencias a funciones. El siguiente código C calcula una aproximación de la integral de una función arbitraria:
#include <stdio.h>doble cuadrado ( doble x ) { devolver x * x ; }cubo doble ( doble x ) { devolver x * x * x ; }/* Calcula la integral de f() en el intervalo [a,b] */ double integral ( double f ( double x ), double a , double b , int n ) { int i ; double sum = 0 ; double dt = ( b - a ) / n ; for ( i = 0 ; i < n ; ++ i ) { sum += f ( a + ( i + 0.5 ) * dt ); } return sum * dt ; }int main () { printf ( "%g \n " , integral ( square , 0 , 1 , 100 )); printf ( "%g \n " , integral ( cube , 0 , 1 , 100 )); return 0 ; }La función qsort de la biblioteca estándar de C utiliza un puntero a función para emular el comportamiento de una función de orden superior.
Macros
Las macros también pueden utilizarse para lograr algunos de los efectos de las funciones de orden superior. Sin embargo, las macros no pueden evitar fácilmente el problema de la captura de variables; además, pueden generar grandes cantidades de código duplicado, lo que dificulta su optimización por parte del compilador. En general, las macros no son fuertemente tipadas, aunque pueden producir código fuertemente tipado.
Evaluación dinámica del código
En otros lenguajes de programación imperativos , es posible lograr algunos de los mismos resultados algorítmicos que se obtienen mediante funciones de orden superior ejecutando código dinámicamente (a veces llamado operaciones Eval o Execute ) dentro del ámbito de evaluación. Este enfoque puede presentar inconvenientes importantes:
- El código de argumento que se va a ejecutar no suele ser de tipado estático ; estos lenguajes generalmente se basan en el tipado dinámico para determinar la corrección y la seguridad del código que se va a ejecutar.
- El argumento se suele proporcionar como una cadena de texto, cuyo valor puede no conocerse hasta el tiempo de ejecución. Esta cadena debe compilarse durante la ejecución del programa (mediante compilación justo a tiempo ) o evaluarse mediante interpretación , lo que genera una sobrecarga adicional en tiempo de ejecución y, por lo general, un código menos eficiente.
objetos
En los lenguajes de programación orientados a objetos que no admiten funciones de orden superior, los objetos pueden ser una alternativa eficaz. Los métodos de un objeto actúan esencialmente como funciones, y un método puede aceptar objetos como parámetros y devolver otros objetos. Sin embargo, los objetos suelen implicar una mayor sobrecarga en tiempo de ejecución en comparación con las funciones puras, así como código repetitivo adicional para definir e instanciar un objeto y sus métodos. Los lenguajes que permiten objetos o estructuras basados en pila (en lugar de en montón ) ofrecen mayor flexibilidad con este método.
Un ejemplo de cómo usar un registro simple basado en pila en Free Pascal con una función que devuelve otra función:
ejemplo de programa ;tipo int = entero ; Txy = registro x , y : int ; fin ; Tf = función ( xy : Txy ) : int ; función f ( xy : Txy ) : int ; inicio Resultado : = xy.y + xy.x ; fin ;función g ( func : Tf ) : Tf ; inicio resultado := func ; fin ;var a : Tf ; xy : Txy = ( x : 3 ; y : 7 ) ;begin a := g ( @ f ) ; // devuelve una función a "a" writeln ( a ( xy )) ; // imprime 10 end .La función a()toma un Txyregistro como entrada y devuelve el valor entero de la suma de los campos del registro x( y3 + 7).
Desfuncionalización
La desfuncionalización puede utilizarse para implementar funciones de orden superior en lenguajes que carecen de funciones de primera clase :
// Estructuras de datos de función desfuncionalizadas template < typename T > struct Add { T value ; }; template < typename T > struct DivBy { T value ; }; template < typename F , typename G > struct Composition { F f ; G g ; };// Implementaciones de aplicación de funciones desfuncionalizadas template < typename F , typename G , typename X > auto apply ( Composition < F , G > f , X arg ) { return apply ( f . f , apply ( f . g , arg )); }plantilla < typename T , typename X > auto apply ( Add < T > f , X arg ) { return arg + f . value ; }plantilla < typename T , typename X > auto apply ( DivBy < T > f , X arg ) { return arg / f . value ; }// Plantilla de función de composición de orden superior < typename F , typename G > Composition < F , G > compose ( F f , G g ) { return Composition < F , G > { f , g }; }int main ( int argc , const char * argv [ ] ) { auto f = compose ( DivBy <float> { 2.0f }, Add <int> {5} ) ; apply ( f , 3 ) ; // 4.0f apply ( f , 9 ) ; // 7.0f return 0 ; }En este caso, se utilizan diferentes tipos para activar diferentes funciones mediante la sobrecarga de funciones . La función sobrecargada en este ejemplo tiene la firma auto apply.
Véase también
- Función de primera clase
- Lógica combinatoria
- Programación a nivel de funciones
- Programación funcional
- Cálculo kappa : un formalismo para funciones que excluye las funciones de orden superior.
- Patrón de estrategia
- Mensajes de orden superior
Referencias
- ↑ "PHP: Funciones flecha - Manual" . www.php.net . Consultado el 1 de marzo de 2021 .
- Programación funcional
- Cálculo lambda
- Funciones de orden superior
- Subrutinas
- Comparación de lenguajes de programación