Función de orden superior
En matemáticas e informática, una función de orden superior (HOF) es una función que hace al menos una de las siguientes cosas:
- toma una o más funciones como argumentos (es decir, un parámetro de procedimiento, 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 funciones 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 una función a su derivada, también una función. Las funciones de orden superior no deben confundirse con otros usos de la palabra "funtor" a lo largo de las matemáticas, consulte Funtor (desambiguación).
En el cálculo de lambda no tipo, todas las funciones son de mayor orden; en un cálculo de lambda tipo, del cual se derivan la mayoría de los lenguajes de programación funcionales, funciones de mayor orden 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
map
función, encontrada en muchos lenguajes de programación funcionales, es un ejemplo de una función de orden superior. Toma como argumentos una función f y una colección de elementos, y como resultado, devuelve una nueva colección con f aplicado a cada elemento de la colección.- Funciones de clasificación, que tienen una función de comparación como parámetro, permitiendo al programador separar el algoritmo de clasificación de las comparaciones de los elementos que se clasifican. Función estándar C
qsort
es un ejemplo de esto. - filtro
- plegable
- aplicación
- Composición de funciones
- Integración
- Callback
- Traversal de árboles
- Gramática Montague, teoría semántica del lenguaje natural, utiliza funciones de orden superior
Soporte en lenguajes de programación
Soporte directo
Los ejemplos no pretenden comparar y 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 dos veces
toma una función y aplica la función a algún valor dos veces. Si dos veces
tiene que aplicarse varias veces para el mismo f
preferiblemente debería devolver una función en lugar de un valor. Esto está en línea con el "no te repitas" principio.
APL
Dos veces.←{}⍺⍺ ⍺⍺ ⍵} más tres←{}⍵+3} g←{}más tres Dos veces. ⍵} g 713
O de manera tácita:
Dos veces.←⍣2 más tres←+∘3 g←más tres Dos veces. g 713
C++
Usando std::function
en C+ +11:
#include ■iostream#include Identificadoauto Dos veces. = [](const std::función.int()int)■ f){} retorno [f]int x) {} retorno f()f()x)); };};auto plus_tres = [](int i){} retorno i + 3;};int principal(){} auto g = Dos veces.()plus_tres); std::Cout .. g()7) .. 'n '; // 13}
O, con lambdas genéricas proporcionadas por C++14:
#include ■iostreamauto Dos veces. = [](const auto" f){} retorno [f]int x) {} retorno f()f()x)); };};auto plus_tres = [](int i){} retorno i + 3;};int principal(){} auto g = Dos veces.()plus_tres); std::Cout .. g()7) .. 'n '; // 13}
C#
Usando solo delegados:
utilizando Sistema;público clase Programa{} público estática vacío Main()cuerda[] args) {} Func.Func.int, int■, Func.int, int> Dos veces. = f = x = f()f()x)); Func.int, int■ más = i = i + 3; Var g = Dos veces.()más); Consola.WriteLine()g()7)); // 13 }}
O de manera equivalente, con métodos estáticos:
utilizando Sistema;público clase Programa{} privado estática Func.int, int■ Dos veces.()Func.int, int■ f) {} retorno x = f()f()x)); } privado estática int PlusThree()int i) = i + 3; público estática vacío Main()cuerda[] args) {} Var g = Dos veces.()PlusThree); Consola.WriteLine()g()7)); // 13 }}
Cierre
()defn Dos veces. [f] ()f [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) {} retorno función()x) {} retorno f()f()x)); };};más = función()i) {} retorno i + 3;};g = Dos veces.()más);scriptOutput()g()7)); // 13
Ceceo común
()defun Dos veces. ()f) ()lambda ()x) ()funcall f ()funcall f x))) ()defun más tres ()i) ()+ i 3) ()defvar g ()Dos veces. # 'más tres) ()impresión ()funcall g 7)
D
importación std.Idiota. : Writeln;alias Dos veces. = ()f) = ()int x) = f()f()x));alias más = ()int i) = i + 3;vacío principal(){} auto g = Dos veces.()más); Writeln()g()7)); // 13}
Dardo
int Función()int) Dos veces.()int Función()int) f) {} retorno ()x) {} retorno f()f()x)); };}int más()int i) {} retorno i + 3;}vacío principal() {} final g = Dos veces.()más); impresión()g()7)); // 13}
Elixir
En Elixir, puede mezclar definiciones de módulos y funciones anónimas
defmodule Hof do def Dos veces.()f) do f()x) - f.()f.()x) final finalfinalplus_tres = f()i) - i + 3 finalg = Hof.Dos veces.()plus_tres)IO.puts g.()7) # 13
Alternativamente, también podemos componer usando funciones anónimas puras.
Dos veces. = f()f) - f()x) - f.()f.()x) finalfinalplus_tres = f()i) - i + 3 finalg = Dos veces..()plus_tres)IO.puts g.()7) # 13
Erlang
or_else([], _) - falso;or_else[F Silencio Fs] X) - or_else()Fs, X, F()X)).or_else()Fs, X, falso) - or_else()Fs, X);or_else()Fs, _ {}falso, Y}) - or_else()Fs, Y);or_else(_ _ R) - R.or_else[diversión erlang:is_integer/1, diversión erlang:is_atom/1, diversión erlang:is_list/1] 3.23).
En este ejemplo de Erlang, la función de orden superior or_else/ 2
toma una lista de funciones (Fs
) y argumento (X
). Evalúa la función F
con el argumento X
como argumento. Si la función F
devuelve falso, entonces la siguiente función en Se evaluará Fs
. Si la función F
devuelve {false, Y}
luego la siguiente función en Fs
con el argumento Y
. Si la función F
devuelve R
la función de orden superior or_else/2
devolverá R
. Tenga en cuenta que X
, Y
, y R
pueden ser funciones. El ejemplo devuelve false
.
F#
Deja Dos veces. f = f > fDeja plus_tres = (+) 3Deja g = Dos veces. plus_tresg 7 ← printf "%A" // 13
Ir
paquete principalimportación "Fmt"func Dos veces.()f func()int) int) func()int) int {}retorno func()x int) int {}retorno f()f()x)}}func principal() {}más := func()i int) int {}retorno i + 3}g := Dos veces.()más)Fmt.Println()g()7) // 13}
Observe que un literal de función se puede definir con un identificador ( dos veces
) o de forma anónima (asignado a la variable plusThree).
Haskell
Dos veces. :: ()Int - Int) - ()Int - Int)Dos veces. f = f . fmás :: Int - Intmás = ()+3)principal :: IO ()principal = impresión ()g 7) - 13 Donde g = Dos veces. más
J
Explícitamente,
Dos veces.=. adverb : 'uuu ' más tres=. verbo : 'y + 3 ' g=. más tres Dos veces. g 713
o tácitamente,
Dos veces.=. ^:2 más tres=. +3 g=. más tres Dos veces. g 713
Java (1.8+)
Usando solo interfaces funcionales:
importación Java.util. *;clase Main {} público estática vacío principal()String[] args) {} Función.IntUnaryOperator, IntUnaryOperator■ Dos veces. = f - f.y Entonces...()f); IntUnaryOperator más = i - i + 3; Var g = Dos veces..aplicación()más); Sistema.Fuera..println()g.aplicaAsInt()7)); // 13 }}
O de manera equivalente, con métodos estáticos:
importación Java.util. *;clase Main {} privado estática IntUnaryOperator Dos veces.()IntUnaryOperator f) {} retorno f.y Entonces...()f); } privado estática int más()int i) {} retorno i + 3; } público estática vacío principal()String[] args) {} Var g = Dos veces.()Main::más); Sistema.Fuera..println()g.aplicaAsInt()7)); // 13 }}
JavaScript
Con funciones de flecha:
"uso estricto";const Dos veces. = f = x = f()f()x));const más = i = i + 3;const g = Dos veces.()más);consola.log()g()7)); // 13
O con sintaxis clásica:
"uso estricto";función Dos veces.()f) {} retorno función ()x) {} retorno f()f()x)); };}función más()i) {} retorno i + 3;}const g = Dos veces.()más);consola.log()g()7)); // 13
Julia
julia función Dos veces.()f) función resultado()x) retorno f()f()x) final retorno resultado finaldos veces (función genética con 1 método)julia más tres()i) = i + 3más tres (función genética con 1 método)julia g = Dos veces.()más tres)(:var"#result#3"{typeof(plusthree)}) (función genética con 1 método)julia g()7)13
Kotlin
diversión Dos veces.()f: ()Int) - Int): ()Int) - Int {} retorno {} f()f()es) }}diversión más()i: Int) = i + 3diversión principal() {} val g = Dos veces.()::más) println()g()7) // 13}
Lúa
función Dos veces.()f) retorno función ()x) retorno f()f()x) finalfinalfunción más()i) retorno i + 3finallocal g = Dos veces.()más)impresión()g()7) - 13
MATLAB
función resultado = Dos veces.()f)resultado = @(x) f()f()x));finalmás tres = @(i) i + 3;g = Dos veces.()más tres)disp()g()7)); % 13
OCaml
Deja Dos veces. f x = f ()f x)Deja plus_tres = (+) 3Deja () = Deja g = Dos veces. plus_tres dentro print_int ()g 7); (* 13 *) print_newline ()
PHP
Identificado?phpDeclara()strict_types=1);función Dos veces.()callable $f): Clausura {} retorno función ()int $x) uso ()$f): int {} retorno $f()$f()$x)); };}función más()int $i): int {} retorno $i + 3;}$g = Dos veces.()'plus Tres. ');eco $g()7), "n"; // 13
o con todas las funciones en variables:
Identificado?phpDeclara()strict_types=1);Dos veces. = f()callable $f): Clausura = f()int $x): int = $f()$f()$x));$plusThree = f()int $i): int = $i + 3;$g = Dos veces.()$plusThree);eco $g()7), "n"; // 13
Tenga en cuenta que las funciones de flecha capturan implícitamente cualquier variable que provenga del ámbito principal, mientras que las funciones anónimas requieren el use
palabra clave para hacer lo mismo.
Perl
uso estricto;uso advertencias;sub Dos veces. {} # ()$f) = @_; sub {} $f-()$f-()@_)); };}sub más {} # ()$i) = @_; $i + 3;}# $g = Dos veces.()más);impresión $g-()7), "n"; # 13
o con todas las funciones en variables:
uso estricto;uso advertencias;# Dos veces. = sub {} # ()$f) = @_; sub {} $f-()$f-()@_)); };};# $plusThree = sub {} # ()$i) = @_; $i + 3;};# $g = Dos veces.-()$plusThree);impresión $g-()7), "n"; # 13
Pitón
, titulado def Dos veces.()f):... def resultado()x):... retorno f()f()x)... retorno resultado, titulado plus_tres = lambda i: i + 3, titulado g = Dos veces.()plus_tres) , titulado g()7)13
La sintaxis del decorador 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 g
podría implementarse de manera equivalente:
, titulado @twice... def g()i):... retorno i + 3, titulado g()7)13
R
Dos veces. . función()f) {} retorno()función()x) {} f()f()x) })}más . función()i) {} retorno()i + 3)}g . Dos veces.()más)■ impresión()g()7)[1] 13
Raku
sub Dos veces.()Callable:D $f♪
retorno sub {} $f()$f()$^x) };
}
sub más()Int:D $i♪
retorno $i + 3;
}
# $g = Dos veces.()" + tres ");
Di: $g()7); # 13
En Raku, todos los objetos de código son cierres y, por lo tanto, pueden hacer referencia a "lexical" variables de un ámbito externo porque la variable léxica está "cerrada" dentro de la función. Raku también es compatible con "bloque puntiagudo" sintaxis para expresiones lambda que se pueden asignar a una variable o invocar de forma anónima.
Rubí
def Dos veces.()f) -()x) {} f.llamada()f.llamada()x) }finalplus_tres = -()i) {} i + 3 }g = Dos veces.()plus_tres)puts g.llamada()7) # 13
Óxido
f Dos veces.()f: impl Fn()i32) - i32) - impl Fn()i32) - i32 {} Muévanse. SilencioxSilencio f()f()x)}f plus_tres()i: i32) - i32 {} i + 3}f principal() {} Deja g = Dos veces.()plus_tres); ¡Imprimir!()"{}", g()7) // 13}
Escala
objeto Main {} def Dos veces.()f: Int = Int): Int = Int = f compose f def más()i: Int): Int = i + 3 def principal()args: Array[String] Dependencia = {} val g = Dos veces.()más) impresión()g()7) // 13 }}
Esquema
()definir ()Dos veces. f) ()lambda ()x) ()f ()f x)))()definir ()más tres i) ()+ i 3)()definir g ()Dos veces. más tres)()pantalla ()g 7) ; 13()pantalla "n")
Rápido
func Dos veces.()¿Qué? f: @escapar ()Int) - Int) - ()Int) - Int {} retorno {} f()f()$0) }}Deja más = {} $0 + 3 }Deja g = Dos veces.()más)impresión()g()7) // 13
Tcl
set Dos veces. {} {}f x} {apply} $f [apply] $f $x]set más {} {}i} Retorno [exprador] $i + 3]# result: 13puts [apply] Dos veces. $plusThree 7]
Tcl usa el comando apply para aplicar una función anónima (desde 8.6).
XACML
El estándar XACML define funciones de orden superior en el estándar para aplicar una función a múltiples valores de paquetes de atributos.
Regla permitir Entrada{} permiso condición cualquier(función)[stringEqual] ciudadanías, permitidoCitizenships)}
La lista de funciones de orden superior en XACML se puede encontrar aquí.
XQuery
Declara función local: dos veces()$f, $x) {} $f()$f()$x)};Declara función local: más de tres()$i) {} $i + 3};local: dos veces()local: más de tres#1, 7) (: 13:)
Alternativas
Puntero de función
Los punteros de función en lenguajes como C, C++ y Pascal 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 Identificado.hdoble cuadrado()doble x){} retorno x * x;}doble cube()doble x){} retorno x * x * x;}/* Computar la integral de f() dentro del intervalo [a,b] */doble integral()doble f()doble x), doble a, doble b, int n){} int i; doble suma = 0; doble ♪ = ()b - a) / n; para ()i = 0; i . n; ++i) {} suma += f()a + ()i + 0.5) * ♪); } retorno suma * ♪;}int principal(){} printf()#n", integral()cuadrado, 0, 1, 100)); printf()#n", integral()cube, 0, 1, 100)); retorno 0;}
La función qsort de la biblioteca estándar de C utiliza un puntero de función para emular el comportamiento de una función de orden superior.
Macros
Las macros también se pueden usar 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; también pueden generar grandes cantidades de código duplicado, lo que puede ser más difícil de optimizar para un compilador. Las macros generalmente no están fuertemente tipadas, aunque pueden producir código fuertemente tipado.
Evaluación de código dinámico
En otros lenguajes de programación imperativos, es posible lograr algunos de los mismos resultados algorítmicos que se obtienen a través de funciones de orden superior mediante la ejecución dinámica de código (a veces llamado Eval o Execute operaciones) en el ámbito de evaluación. Puede haber inconvenientes significativos en este enfoque:
- El código argumental a ejecutar normalmente no es de tipo estatico; estos idiomas generalmente dependen de la escritura dinámica para determinar la forma y seguridad del código a ejecutar.
- El argumento generalmente se proporciona como una cadena, cuyo valor puede no ser conocido hasta el tiempo de ejecución. Esta cadena debe ser compilada durante la ejecución del programa (utilizando la compilación justo a tiempo) o evaluada por interpretación, causando algunos gastos generales adicionales en tiempo de ejecución, y generalmente generando 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 un sustituto eficaz. Los métodos de un objeto actúan en esencia como funciones, y un método puede aceptar objetos como parámetros y producir objetos como valores devueltos. Sin embargo, los objetos a menudo conllevan una sobrecarga de tiempo de ejecución adicional en comparación con las funciones puras y un código repetitivo agregado para definir e instanciar un objeto y sus métodos. Los lenguajes que permiten estructuras o objetos basados en pila (en lugar de en montón) pueden proporcionar más flexibilidad con este método.
Un ejemplo del uso de un registro basado en una pila simple en Free Pascal con una función que devuelve una función:
programa ejemplo;Tipo int = entero; Txy = récord x, Sí.: int; final; Tf = función ()xy: Txy): int; función f()xy: Txy): int; comenzar Resultado := xy.Sí. + xy.x; final;función g()func: Tf): Tf; comenzar resultado := func; final;Var a: Tf; xy: Txy = ()x: 3; Sí.: 7);comenzar a := g()@f); // devolver una función a "a" Writeln()a()xy); //impresión 10final.
La función a()
toma un registro Txy
como entrada y devuelve el valor entero de la suma de las x
del registro > y y
campos (3 + 7).
Desfuncionalización
La desfuncionalización se puede utilizar para implementar funciones de orden superior en lenguajes que carecen de funciones de primera clase:
// Estructuras de datos de función desfuncionalizadaplantilla.nombre T■ struct Añadir {} T valor; };plantilla.nombre T■ struct DivBy {} T valor; };plantilla.nombre F, nombre G■ struct Composition {} F f; G g; };// Implementaciones de aplicaciones de función desfuncionalizadaplantilla.nombre F, nombre G, nombre X■auto aplicación()Composition.F, G■ f, X arg) {} retorno aplicación()f.f, aplicación()f.g, arg));}plantilla.nombre T, nombre X■auto aplicación()Añadir.T■ f, X arg) {} retorno arg + f.valor;}plantilla.nombre T, nombre X■auto aplicación()DivBy.T■ f, X arg) {} retorno arg / f.valor;}// Función de composición de orden superiorplantilla.nombre F, nombre G■Composition.F, G■ compose()F f, G g) {} retorno Composition.F, G■ {}f, g};}int principal()int argc, const char* argv[]) {} auto f = compose()DivBy.flotador■{} 2.0f } Añadir.int■{} 5 }); aplicación()f, 3); // 4.0f aplicación()f, 9); // 7.0f retorno 0;}
En este caso, se utilizan diferentes tipos para activar diferentes funciones a través de la sobrecarga de funciones. La función sobrecargada en este ejemplo tiene la firma auto apply
.
Contenido relacionado
Lógica resistencia-transistor
Programación entera
Singleton (matemáticas)