Articulo de referencia

Función de orden superior

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á...

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:

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(τ1τ2)τ3{\displaystyle (\tau _{1}\to \tau _{2})\to \tau _{3}}.

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 13

O de manera tácita:

dos veces 2más tres + 3g más tres dos veces g 7 13

C++

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 )) ; 13

Lenguaje 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 )); // 13

Lisp 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 ) # 13

Alternativamente, 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 ) # 13

Erlang

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" // 13

Ir

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 ) // 13

Haskell

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 tres

J

Explícitamente,

dos veces =. adverbio : 'uu y'más tres = verbo : 'y + 3' g = más tres dos veces g 7 13

o tácitamente,

dos veces =. ^: 2más tres =. +& 3 g =. más tres dos veces g 7 13

Java (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 ) ) ; // 13

O 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 ) ) ; // 13

Julia

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 ) 13

Kotlin

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 )) -- 13

MATLAB

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 )); % 13

OCaml

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 " ; // 13

o 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 " ; // 13

Tenga 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" ; # 13

o 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" ; # 13

Pitó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 13

La 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 13

R

dos veces <- \ ( f ) \ ( x ) f ( f ( x ))plusThree <- function ( i ) i + 3g <- dos veces ( más tres )> g ( 7 ) [ 1 ] 13

Raku

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 ); # 13

En 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 )) // 13

Tcl

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

Referencias

  1. "PHP: Funciones flecha - Manual" . www.php.net . Consultado el 1 de marzo de 2021 .