Función variádica

Función con número variable de argumentos

En matemáticas y en programación informática , una función variádica es una función de aridad indefinida , es decir, una que acepta un número variable de argumentos . El soporte para funciones variádicas difiere ampliamente entre los lenguajes de programación .

El término variádico es un neologismo que data de 1936-1937. [1] El término no se usó ampliamente hasta la década de 1970.

Descripción general

Existen muchas operaciones matemáticas y lógicas que se presentan de manera natural como funciones variádicas. Por ejemplo, la suma de números o la concatenación de cadenas u otras secuencias son operaciones que pueden considerarse aplicables a cualquier número de operandos (aunque formalmente en estos casos se aplica la propiedad asociativa ).

Otra operación que se ha implementado como función variádica en muchos lenguajes es el formato de salida. La función Cprintf y la función Common Lispformat son dos ejemplos de ello. Ambas toman un argumento que especifica el formato de la salida y cualquier número de argumentos que proporcionan los valores que se van a formatear.

Las funciones variádicas pueden exponer problemas de seguridad de tipos en algunos lenguajes. Por ejemplo, las funciones variádicas de C printf, si se usan sin precaución, pueden dar lugar a una clase de agujeros de seguridad conocidos como ataques de cadenas de formato . El ataque es posible porque el soporte del lenguaje para las funciones variádicas no es seguro en cuanto a tipos: permite que la función intente sacar de la pila más argumentos de los que se colocaron allí, corrompiendo la pila y dando lugar a un comportamiento inesperado. Como consecuencia de esto, el Centro de Coordinación CERT considera que las funciones variádicas en C son un riesgo de seguridad de alta gravedad. [2]

En los lenguajes de programación funcional , las funciones variádicas pueden considerarse complementarias a la función apply , que toma una función y una lista/secuencia/matriz como argumentos, y llama a la función con los argumentos suministrados en esa lista, pasando así un número variable de argumentos a la función. [ cita requerida ] En el lenguaje funcional Haskell , las funciones variádicas pueden implementarse devolviendo un valor de un tipo class T ; si las instancias de Tson un valor de retorno final ry una función (T t) => x -> t, esto permite cualquier número de argumentos adicionales x. [ se necesita más explicación ]

Un tema relacionado en la investigación de reescritura de términos se llama coberturas o variables de cobertura . [3] A diferencia de los variádicos, que son funciones con argumentos, las coberturas son secuencias de argumentos en sí mismas. También pueden tener restricciones ('no tome más de 4 argumentos', por ejemplo) hasta el punto en que no son de longitud variable (como 'tome exactamente 4 argumentos'), por lo que llamarlos variádicos puede ser engañoso. Sin embargo, se refieren al mismo fenómeno y, a veces, la redacción es mixta, lo que resulta en nombres como variable variádica (sinónimo de cobertura). Tenga en cuenta el doble significado de la palabra variable y la diferencia entre argumentos y variables en programación funcional y reescritura de términos. Por ejemplo, un término (función) puede tener tres variables, una de ellas una cobertura, lo que permite que el término tome tres o más argumentos (o dos o más si se permite que la cobertura esté vacía).

Ejemplos

Para implementar de forma portátil funciones variádicas en el lenguaje Cstdarg.h , se utiliza el archivo de encabezado estándar . El varargs.hencabezado anterior ha quedado obsoleto en favor de stdarg.h. En C++, cstdargse utiliza el archivo de encabezado. [4]

#incluir <stdarg.h> #incluir <stdio.h>  doble promedio ( int recuento , ...) { va_list ap ; int j ; doble suma = 0 ;             va_start ( ap , count ); /* Antes de C23: Requiere el último parámetro fijo (para obtener la dirección) */ for ( j = 0 ; j < count ; j ++ ) { sum += va_arg ( ap , int ); /* Incrementa ap hasta el siguiente argumento. */ } va_end ( ap );                   devolver suma / recuento ; }   int main ( int argc , char const * argv []) { printf ( "%f \n " , promedio ( 3 , 1 , 2 , 3 )); devolver 0 ; }             

Esto calculará el promedio de un número arbitrario de argumentos. Tenga en cuenta que la función no conoce el número de argumentos ni sus tipos. La función anterior espera que los tipos sean int, y que el número de argumentos se pase en el primer argumento (este es un uso frecuente pero de ninguna manera impuesto por el lenguaje o el compilador). En algunos otros casos, por ejemplo printf , el número y los tipos de argumentos se calculan a partir de una cadena de formato. En ambos casos, esto depende del programador para proporcionar la información correcta. (Alternativamente, se puede usar un valor centinela como NULLo nullptrpara indicar el final de la lista de parámetros). Si se pasan menos argumentos de los que cree la función, o los tipos de argumentos son incorrectos, esto podría hacer que lea en áreas no válidas de la memoria y puede conducir a vulnerabilidades como el ataque de cadena de formato . Dependiendo del sistema, incluso el uso NULLcomo centinela puede encontrar tales problemas; nullptro se puede usar un puntero nulo dedicado del tipo de destino correcto para evitarlos.

stdarg.hdeclara un tipo, va_list, y define cuatro macros: va_start, va_arg, va_copy, y va_end. Cada invocación de va_starty va_copydebe coincidir con una invocación correspondiente de va_end. Cuando se trabaja con argumentos variables, una función normalmente declara una variable de tipo va_list( apen el ejemplo) que será manipulada por las macros.

  1. va_starttoma dos argumentos, un va_listobjeto y una referencia al último parámetro de la función (el que está antes de los puntos suspensivos; la macro usa esto para orientarse). En C23 , el segundo argumento ya no será necesario y las funciones variádicas ya no necesitarán un parámetro nombrado antes de los puntos suspensivos. [nota 1] [6] Inicializa el va_listobjeto para que lo use va_argor va_copy. El compilador normalmente emitirá una advertencia si la referencia es incorrecta (por ejemplo, una referencia a un parámetro diferente al último, o una referencia a un objeto completamente diferente), pero no impedirá que la compilación se complete normalmente.
  2. va_argtoma dos argumentos, un va_listobjeto (inicializado previamente) y un descriptor de tipo. Se expande al siguiente argumento variable y tiene el tipo especificado. Las invocaciones sucesivas de va_argpermiten procesar cada uno de los argumentos variables por turno. Se produce un comportamiento no especificado si el tipo es incorrecto o no hay un siguiente argumento variable.
  3. va_endtoma un argumento, un va_listobjeto. Sirve para limpiar. Si uno quisiera, por ejemplo, escanear los argumentos de la variable más de una vez, el programador reinicializaría su va_listobjeto invocando va_endy luego va_startnuevamente en él.
  4. va_copytoma dos argumentos, ambos va_listobjetos. Clona el segundo (que debe haberse inicializado) en el primero. Volviendo al ejemplo de "escanear los argumentos de la variable más de una vez", esto se podría lograr invocando va_starten un primero va_listy luego usando va_copypara clonarlo en un segundo va_list. Después de escanear los argumentos de la variable una primera vez con va_argy el primero va_list(descartándolo con va_end), el programador podría escanear los argumentos de la variable una segunda vez con va_argy el segundo va_list. va_endtambién debe llamarse en el clonado va_listantes de que la función que lo contiene regrese.

Cª#

C# describe las funciones variádicas mediante la paramspalabra clave. Se debe proporcionar un tipo para los argumentos, aunque object[]se puede utilizar como un comodín. En el sitio de llamada, puede enumerar los argumentos uno por uno o entregar una matriz preexistente que tenga el tipo de elemento requerido. El uso de la forma variádica es una sintaxis simplificada para esto último.

usando Sistema ; clase Program { static int Foo ( int a , int b , params int [ ] args ) { // Devuelve la suma de los números enteros en args , ignorando a y b . int suma = 0 ; foreach ( int i in args ) suma + = i ; return suma ; } static void Main ( string [ ] args ) { Console.WriteLine ( Foo ( 1,2 ) ) ; // 0 Console.WriteLine ( Foo ( 1,2,3,10,20 ) ) ; // 33 int [ ] manyValues ​​= new int [ ] { 13,14,15 } ; Console.WriteLine ( Foo ( 1,2 , manyValues ) ) ; // 42 } }                                                         

En C++

La función variádica básica de C++ es prácticamente idéntica a la de C. La única diferencia está en la sintaxis, donde se puede omitir la coma antes de los puntos suspensivos. C++ permite funciones variádicas sin parámetros nombrados , pero no ofrece ninguna forma de acceder a esos argumentos, ya que va_startrequiere el nombre del último argumento fijo de la función.

#include <iostream> #include <cstdarg>  void simple_printf ( const char * fmt ...) // El estilo C "const char* fmt, ..." también es válido { va_list args ; va_start ( args , fmt ); while ( * fmt != '\0' ) { if ( * fmt == 'd' ) { int i = va_arg ( args , int ); std :: cout << i << '\n' ; } else if ( * fmt == 'c' ) { // tenga en cuenta la conversión automática al tipo integral int c = va_arg ( args , int ); std :: cout << static_cast < char > ( c ) << '\n' ; } else if ( * fmt == 'f' ) { double d = va_arg ( args , double ); std :: cout << d << '\n' ; } ++ fmt ; } va_end ( argumentos ); }                                                                     int principal () { simple_printf ( "dcff" , 3 , 'a' , 1.999 , 42.5 ); }       

Las plantillas variádicas (paquete de parámetros) también se pueden usar en C++ con expresiones de pliegue integradas en el lenguaje .

#include <flujo de datos> plantilla < nombre_tipo ... Ts > void foo_print ( Ts ... args ) { (( std :: cout << args << ' ' ), ...); }           int main () { std :: cout << std :: boolalpha ; foo_print ( 1 , 3.14f ); // 1 3.14 foo_print ( "Foo" , 'b' , verdadero , nullptr ); // Foo b verdadero nullptr }            

Los estándares de codificación CERT para C++ prefieren fuertemente el uso de plantillas variádicas (paquete de parámetros) en C++ sobre la función variádica de estilo C debido a un menor riesgo de mal uso. [7]

En Go

Las funciones variádicas en Go se pueden llamar con cualquier número de argumentos finales. [8] fmt.Println es una función variádica común; utiliza una interfaz vacía como un tipo general.

paquete principal importar "fmt" // Esta función variádica toma una cantidad arbitraria de enteros como argumentos. func sum ( nums ... int ) { fmt.Print ( "La suma de " , nums ) // También es una función variádica. total : = 0 for _ , num := range nums { total += num } fmt.Println ( "is" , total ) // También es una función variádica. }                 func main () { // Las funciones variádicas se pueden llamar de la forma habitual con argumentos individuales. sum ( 1 , 2 ) // "La suma de [1 2] es 3" sum ( 1 , 2 , 3 ) // "La suma de [1 2 3] es 6"       // Si ya tiene varios argumentos en una porción, aplíquelos a una función variádica usando func(slice...) de esta manera. nums := [] int { 1 , 2 , 3 , 4 } sum ( nums ... ) // "La suma de [1 2 3 4] es 10" }      

Producción:

La suma de [1 2] es 3La suma de [1 2 3] es 6La suma de [1 2 3 4] es 10

En Java

Al igual que en C#, el Objecttipo en Java está disponible como un comodín.

public class Program { // Los métodos variádicos almacenan cualquier argumento adicional que reciben en una matriz. // En consecuencia, `printArgs` es en realidad un método con un parámetro: una matriz de longitud variable de `String`s. private static void printArgs ( String ... strings ) { for ( String string : strings ) { System . out . println ( string ); } }                      public static void main ( String [] args ) { printArgs ( "hola" ); // abreviatura de printArgs(["hola"]) printArgs ( "hola" , "mundo" ); // abreviatura de printArgs(["hola", "mundo"]) } }           

En JavaScript

A JavaScript no le importan los tipos de argumentos variádicos.

función suma (... números ) { devolver números . reducir (( a , b ) => a + b , 0 ); }          consola.log ( suma ( 1,2,3 ) ) ; // 6 consola.log ( suma ( 3,2 ) ) ; // 5 consola.log ( suma ( ) ) ; // 0      

También es posible crear una función variádica utilizando el objeto de argumentos, aunque solo se puede utilizar con funciones creadas con la functionpalabra clave.

función suma ( ) { return Array.prototype.reduce.call ( argumentos , ( a , b ) = > a + b , 0 ) ; }           consola.log ( suma ( 1,2,3 ) ) ; // 6 consola.log ( suma ( 3,2 ) ) ; // 5 consola.log ( suma ( ) ) ; // 0      

EnLua

Las funciones de Lua pueden pasar varargs a otras funciones de la misma manera que otros valores usando la returnpalabra clave. Las tablas se pueden pasar a funciones variádicas usando, en la versión Lua 5.2 o superior [9] table.unpack , o Lua 5.1 o inferior [10] unpack . Los varargs se pueden usar como una tabla construyendo una tabla con el vararg como valor.

función  suma (...)  --... designa varargs  local  suma = 0  para  _ , v  en  pares ({...})  hacer  --crear una tabla con varargs es lo mismo que crear una con valores estándar  suma = suma + v  fin  devolver  suma finvalues ​​= { 1 , 2 , 3 , 4 } suma ( 5 , tabla.unpack ( valores ))  --devuelve 15. tabla.unpack debe ir después de cualquier otro argumento, de lo contrario no todos los valores se pasarán a la función.función  add5 (...)  retorna  ... + 5  - este es un uso incorrecto de varargs y solo retornará el primer valor proporcionadoentradas = {} función  process_entries ()  local  procesado = {}  para  i , v  en  pares ( entradas )  hacer  procesado [ i ] = v  --placeholder código de procesamiento  fin  retorno  tabla.unpack ( procesado )  --devuelve todas las entradas de una manera que se pueda usar como un vararg finprint ( process_entries ())  - la función de impresión toma todas las variables y las escribe en stdout separadas por nuevas líneas

En Pascal

Pascal está estandarizado por las normas ISO 7185 (“Pascal estándar”) y 10206 (“Pascal extendido”). Ninguna de las formas estandarizadas de Pascal admite rutinas variádicas, excepto ciertas rutinas integradas ( read/ readLny write/ writeLn, y adicionalmente en EP readStr / writeStr).

No obstante, los dialectos de Pascal implementan mecanismos que se asemejan a las rutinas variádicas. Delphi define un tipo de datos que puede asociarse con el último parámetro formal . Dentro de la definición de rutina, el es un , una matriz de registros variantes . [11] El miembro del tipo de datos antes mencionado permite la inspección del tipo de datos del argumento y su posterior manejo apropiado. El compilador Free Pascal también admite las rutinas variádicas de Delphi. [12]array of const array of constarray of TVarRecVTyperecord

Sin embargo, esta implementación requiere técnicamente un único argumento, es decir, un array. Pascal impone la restricción de que las matrices deben ser homogéneas. Este requisito se evita utilizando un registro de variantes. GNU Pascal define una especificación de parámetro formal variádico real utilizando puntos suspensivos ( ...), pero a partir de 2022 no se ha definido ningún mecanismo portátil para utilizarlo. [13]

Tanto GNU Pascal como FreePascal permiten que las funciones declaradas externamente utilicen una especificación de parámetros formales variádicos mediante puntos suspensivos ( ...).

En PHP

A PHP no le importan los tipos de argumentos variádicos a menos que el argumento esté tipificado.

función  suma ( ... $nums ) :  int {  return  array_sum ( $nums ); }eco  suma ( 1 ,  2 ,  3 );  // 6

Y escribió argumentos variádicos:

función  suma ( int  ... $nums ) :  int {  return  array_sum ( $nums ); }echo  sum ( 1 ,  'a' ,  3 );  // TypeError: El argumento 2 pasado a sum() debe ser del tipo int (desde PHP 7.3)

En Python

A Python no le importan los tipos de argumentos variádicos.

def  foo ( a ,  b ,  * args ):  print ( args )  # args es una tupla (secuencia inmutable).foo ( 1 ,  2 )  # () foo ( 1 ,  2 ,  3 )  # (3,) foo ( 1 ,  2 ,  3 ,  "hola" )  # (3, "hola")

Los argumentos de palabras clave se pueden almacenar en un diccionario, por ejemplo def bar(*args, **kwargs).

En Raku

En Raku , el tipo de parámetros que crean funciones variádicas se conocen como parámetros de matriz slurpy y se clasifican en tres grupos:

Slurpy aplanado

Estos parámetros se declaran con un solo asterisco ( *) y aplanan los argumentos disolviendo una o más capas de elementos que se pueden iterar (es decir, Iterables).

sub  foo ( $a , $b , * @args ) { decir  @args . perl ;}foo ( 1 , 2 ) # [] foo ( 1 , 2 , 3 ) # [3] foo ( 1 , 2 , 3 , "hola" ) # [3 "hola"] foo ( 1 , 2 , 3 , [ 4 , 5 ], [ 6 ]); # [3, 4, 5, 6]

Slurpy sin aplanar

Estos parámetros se declaran con dos asteriscos ( **) y no aplanan ningún argumento iterable dentro de la lista, sino que mantienen los argumentos más o menos como están:

sub  barra ( $a , $b , ** @args ) { decir  @args . perl ;}barra ( 1 , 2 ); # [] barra ( 1 , 2 , 3 ); # [3] barra ( 1 , 2 , 3 , "hola" ); # [3 "hola"] barra ( 1 , 2 , 3 , [ 4 , 5 ], [ 6 ]); # [3, [4, 5], [6]]

Contextual sorbete

Estos parámetros se declaran con un +signo más ( ) y aplican la "regla del argumento único" , que decide cómo manejar el argumento slurpy en función del contexto. En pocas palabras, si solo se pasa un argumento único y ese argumento es iterable, ese argumento se usa para completar la matriz de parámetros slurpy. En cualquier otro caso, +@funciona como **@(es decir, slurpy sin aplanar).

sub  zaz ( $a , $b , + @args ) { decir  @args . perl ;}zaz ( 1 , 2 ); # [] zaz ( 1 , 2 , 3 ); # [3] zaz ( 1 , 2 , 3 , "hola" ); # [3 "hola"] zaz ( 1 , 2 , [ 4 , 5 ]); # [4, 5], un solo argumento llena la matriz zaz ( 1 , 2 , 3 , [ 4 , 5 ]); # [3, [4, 5]], comportándose como **@ zaz ( 1 , 2 , 3 , [ 4 , 5 ], [ 6 ]); # [3, [4, 5], [6]], comportándose como **@

En rubí

A Ruby no le importan los tipos de argumentos variádicos.

def foo ( * args ) imprimir args fin   foo ( 1 ) # imprime `[1]=> nil`foo ( 1 , 2 ) # imprime `[1, 2]=> nil` 

En óxido

Rust no admite argumentos variádicos en funciones. En su lugar, utiliza macros . [14]

macro_rules! calculate { // El patrón para un solo `eval` ( eval $e : expr ) => {{ { let val : usize = $e ; // Fuerza a que los tipos sean enteros println! ( "{} = {}" , stringify! { $e }, val ); } }};                   // Descomponer múltiples `eval`s recursivamente ( eval $e : expr , $( eval $es : expr ), + ) => {{ calculate ! { eval $e } calculate ! { $( eval $es ), + } }}; }                 fn  main () { calcular ! { // Mira ma! Variadic `calculate!`! eval 1 + 2 , eval 3 + 4 , eval ( 2 * 3 ) + 1 } }                   

Rust puede interactuar con el sistema variádico de C a través de un c_variadicconmutador de características. Al igual que con otras interfaces de C, el sistema se considera unsafeparte de Rust. [15]

En Scala

objeto Programa { // Los métodos variádicos almacenan cualquier argumento adicional que reciben en una matriz. // En consecuencia, `printArgs` es en realidad un método con un parámetro: una matriz de longitud variable de `String`s. private def printArgs ( strings : String * ) : Unit = { strings.foreach ( println ) }               def main ( args : Array [ String ]): Unidad = { printArgs ( "hola" ); // abreviatura de printArgs(["hola"]) printArgs ( "hola" , "mundo" ); // abreviatura de printArgs(["hola", "mundo"]) } }           

En rápido

A Swift le importa el tipo de argumentos variádicos, pero el tipo general Anyestá disponible.

func  saludo ( timeOfTheDay :  String ,  names :  String ...)  {  // aquí, names es [String]  print ( " Parece que tenemos \( nombres.contar ) personas" )  para  nombre  en  nombres  {  print ( "Hola \( nombre ) , buenas \( horaDelDía ) " )  } }Saludo ( horaDelDía :  "mañana" ,  nombres :  "José" ,  "Clara" ,  "William" ,  "María" )// Salida: // Parece que tenemos 4 personas // Hola Joseph, buenos días // Hola Clara, buenos días // Hola William, buenos días // Hola María, buenos días

En Tcl

Un procedimiento Tcl o lambda es variádico cuando su último argumento es args: esto contendrá una lista (posiblemente vacía) de todos los argumentos restantes. Este patrón es común en muchos otros métodos similares a procedimientos. [16] [17]

proc  greeting { timeOfTheDay args } { puts "Parece que tenemos [llength $args] personas"      foreach  nombre $args { puts "Hola $nombre, buen $tiempoDelDía" } }     Saludos "Buenos días" "José" "Clara" "William" "María"     # Salida: # Parece que tenemos 4 personas # Hola Joseph, buenos días # Hola Clara, buenos días # Hola William, buenos días # Hola María, buenos días

Véase también

Notas

  1. ^ Era necesario hacer que el parámetro nombrado fuera opcional, ya que no había forma de especificar una función que tomara una cantidad no especificada de argumentos en C23 después de la eliminación de las definiciones de funciones del estilo K&R. Dado que C++ ya estaba usando esta sintaxis para el mismo propósito, este cambio también fue una forma de aumentar la compatibilidad entre los lenguajes. [5]

Referencias

  1. ^ Henry S. Leonard y HN Goodman, Un cálculo de individuos . Resumen de una charla dada en la Segunda Reunión de la Asociación de Lógica Simbólica, celebrada en Cambridge, MA, del 28 al 30 de diciembre de 1936, [1], Journal of Symbolic Logic 2 (1) 1937, 63.
  2. ^ Klemens, Ben (2014). El siglo XXI: consejos de la nueva escuela . O'Reilly Media, Inc., pág. 224. ISBN 978-1491904442.
  3. ^ CLP (H): Programación lógica de restricciones para coberturas
  4. ^ "<cstdarg> (stdarg.h) - Referencia de C++". www.cplusplus.com .
  5. ^ "C23 ha finalizado: esto es lo que hay en el menú §N2975 - Flexibilizar los requisitos para las listas de parámetros variádicos". 31 de julio de 2022.
  6. ^ Gilding, Alex; Meneide, JeanHeyd (15 de abril de 2022). "WG14-N2975: Relajar requisitos para listas de parámetros variádicos, v3" (PDF) .
  7. ^ "DCL50-CPP. No defina una función variádica de estilo C".
  8. ^ "Vaya por ejemplo: funciones variádicas".
  9. ^ "Manual de referencia de Lua 5.2". www.lua.org . Consultado el 5 de febrero de 2023 .
  10. ^ "Manual de referencia de Lua 5.1". www.lua.org . Consultado el 5 de febrero de 2023 .
  11. ^ "Parámetros (Delphi)" . Consultado el 28 de agosto de 2023 .
  12. ^ "Free Pascal - Guía de referencia" . Consultado el 28 de agosto de 2023 .
  13. ^ "El manual de GNU Pascal" . Consultado el 28 de agosto de 2023 .
  14. ^ "Variadics". El óxido a través del ejemplo .
  15. ^ "2137-variadic". El libro RFC de Rust .
  16. ^ "página del manual de procedimientos". Documentación de Tcl/Tk .
  17. ^ "args". Wiki de Tcler .
  • Función variádica. Tarea de código Rosetta que muestra la implementación de funciones variádicas en más de 120 lenguajes de programación.
  • Funciones con argumentos variables: un tutorial sobre funciones con argumentos variables para C++
  • Manual de GNU libc
Retrieved from "https://en.wikipedia.org/w/index.php?title=Variadic_function&oldid=1246501833"