stringtranslate.com

función variadica

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

El término variadic es un neologismo que se remonta a 1936-1937. [1] El término no se utilizó ampliamente hasta la década de 1970.

Descripción general

Hay muchas operaciones matemáticas y lógicas que se presentan naturalmente como funciones variadas. 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 variada en muchos idiomas es el formateo de salida. La función Cprintf y la función Common Lispformat son dos de esos ejemplos. Ambos toman un argumento que especifica el formato de la salida y cualquier número de argumentos que proporcionen los valores a formatear.

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

En los lenguajes de programación funcionales , las variadics 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 proporcionados en esa lista, pasando así un número variable de argumentos a la función. función. [ cita requerida ] En el lenguaje funcional Haskell , las funciones variadas se pueden implementar devolviendo un valor de una clase de tipo 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 las variadics, que son funciones con argumentos, las coberturas son secuencias de argumentos en sí mismas. También pueden tener restricciones ('no tomar más de 4 argumentos', por ejemplo) hasta el punto de que no sean de longitud variable (como 'tomar exactamente 4 argumentos'), por lo que llamarlos variadics puede ser engañoso. Sin embargo, se refieren al mismo fenómeno y, a veces, la redacción se mezcla, lo que da lugar a nombres como variable variadic (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, permitiendo así 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 variadas 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>  promedio doble ( int recuento , ...) { va_list ap ; intj ;doble suma = 0 ;             va_start ( ap , contar ); /* 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 al 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 lo aplica 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 de que el programador proporcione la información correcta. (Como alternativa, se puede usar un valor centinela como NULLpara indicar el número). Si se pasan menos argumentos de los que la función cree, o los tipos de argumentos son incorrectos, esto podría hacer que se lea en áreas no válidas de la memoria y puede llevar a vulnerabilidades como el ataque de cadena de formato .

stdarg.hdeclara un tipo va_listy define cuatro macros: va_start, va_arg, va_copyy va_end. Cada invocación de va_starty va_copydebe ir acompañada de 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 variadas ya no necesitarán un parámetro con nombre antes de los puntos suspensivos. [nota 1] [6] Inicializa el va_listobjeto para que lo utilice va_argo 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 (previamente inicializado) y un descriptor de tipo. Se expande al siguiente argumento de variable y tiene el tipo especificado. Las invocaciones sucesivas de va_argpermiten procesar cada uno de los argumentos de la variable por turno. Se produce un comportamiento no especificado si el tipo es incorrecto o no hay un argumento de variable siguiente.
  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 invocándolo va_endy luego va_startnuevamente sobre él.
  4. va_copytoma dos argumentos, ambos va_listobjetos. Clona el segundo (que debe haber sido 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_startun first va_listy luego usándolo va_copypara clonarlo en un second va_list. Después de escanear los argumentos de la variable por primera vez con va_argy la primera va_list(eliminarlos con va_end), el programador podría escanear los argumentos de la variable una segunda vez con va_argy la segunda va_list. va_endTambién debe llamarse en el clonado va_listantes de que regrese la función contenedora.

Cª#

C# describe funciones variadas usando 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. Usar la forma variada es azúcar sintáctico para este último.

usando Sistema ; class 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. suma entera = 0 ; foreach ( int i en argumentos ) suma += i ; suma devuelta ; } static void Principal ( cadena [] args ) { Consola . WriteLine ( Foo ( 1 , 2 )); // 0 Consola . WriteLine ( Foo ( 1 , 2 , 3 , 10 , 20 )); // 33 int [] muchosValores = nuevo int [] { 13 , 14 , 15 }; Consola . WriteLine ( Foo ( 1 , 2 , muchosValores )); // 42 } }                                                         

En C++

La función variadic básica en C++ es en gran medida 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 variadas sin parámetros con nombre , pero no proporciona forma de acceder a esos argumentos ya que va_startrequiere el nombre del último argumento fijo de la función.

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

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

#incluir <iostream> plantilla < nombre de tipo ... Ts > void foo_print ( Ts ... args ) { (( std :: cout << args << ' ' ), ...); }           int principal () { 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 firmemente el uso de plantillas variadas (paquete de parámetros) en C++ a la función variada de estilo C debido a un menor riesgo de uso indebido. [7]

En marcha

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

paquete principal importar "fmt" // Esta función variada toma un número arbitrario de entradas como argumentos. func suma ( nums ... int ) { fmt . Print ( "La suma de" , nums ) // También una función variada. total := 0 para _ , num := rango nums { total += num } fmt . Println ( "is" , total ) // También es una función variada. }                 func main () { // Las funciones variables se pueden llamar de la forma habitual con // argumentos individuales. suma ( 1 , 2 ) // "La suma de [1 2] es 3" suma ( 1 , 2 , 3 ) // "La suma de [1 2 3] es 6"       // Si ya tiene varios argumentos en un segmento, aplíquelos a una función // variada usando func(slice...) como este. números := [] int { 1 , 2 , 3 , 4 } suma ( números ... ) // "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 con C#, el Objecttipo en Java está disponible como un todo.

public class Programa { // Los métodos variables 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 . afuera . println ( cadena ); } }                      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 variados.

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

También es posible crear una función variada usando el objeto de argumentos, aunque solo se puede usar con funciones creadas con la functionpalabra clave.

función suma () { retorno Matriz . prototipo . reducir . llamar ( argumentos , ( a , b ) => a + b , 0 ); }           consola . iniciar sesión ( suma ( 1 , 2 , 3 )); // 6 consola . iniciar sesión ( suma ( 3 , 2 )); // 5 consola . iniciar sesión ( suma ()); // 0      

en lua

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 variadas utilizando, en Lua versión 5.2 o superior [9] table.unpack , o Lua 5.1 o inferior [10] unpack . Varargs se puede utilizar como tabla construyendo una tabla con vararg como valor.

función  suma (...)  --... designa varargs  suma local  = 0 para _ , v en pares ({...}) do --crear una tabla con varargs es lo mismo que crear una con valores estándar suma = suma + v final devolver suma final          valores = { 1 , 2 , 3 , 4 } suma ( 5 , table.unpack ( valores ) )  -- devuelve 15. table.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 (...)  return  ... + 5  : este es un uso incorrecto de varargs y solo devolverá el primer valor proporcionado .entradas = {} función  proceso_entradas ()  local  procesado = {}  para  i , v  en  pares ( entradas )  procesar  [ i ] = v --código de procesamiento de marcador de posición final devolver table.unpack ( procesado ) --devuelve todas las entradas de una manera que se puede utilizar como un final vararg     print ( process_entries ())  : la función de impresión toma todos los varargs y los escribe en la salida estándar separados por nuevas líneas

En Pascal

Pascal está estandarizado según las normas ISO 7185 (“Pascal estándar”) y 10206 (“Pascal extendido”). Ninguna forma estandarizada de Pascal admite rutinas variadas, 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 rutinas variadas. Delphi define un tipo de datos que puede estar asociado con el último parámetro formal . Dentro de la definición de rutina hay 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 adecuado. El compilador Free Pascal también admite las rutinas variadas de Delphi. [12]array of const array of constarray of TVarRecVTyperecord

Esta implementación, sin embargo, técnicamente requiere un único argumento, que es un archivo array. Pascal impone la restricción de que las matrices deben ser homogéneas. Este requisito se evita utilizando un registro variante. GNU Pascal define una especificación de parámetro formal variable 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 funciones declaradas externamente utilicen una especificación de parámetro formal variable utilizando puntos suspensivos ( ...).

En PHP

A PHP no le importan los tipos de argumentos variables a menos que el argumento esté escrito.

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

Y argumentos variados escritos:

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

En pitón

A Python no le importan los tipos de argumentos variados.

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 variadas se conocen como parámetros de matriz slurpy y se clasifican en tres grupos:

sorbete aplanado

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

sub  foo ( $a , $b , * @args ) { dice  @args . Perla ;}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]

sorbido 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:

subbarra ( $a , $ b , ** @args ) { digamos @args . Perla ; }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]]

sorbido contextual

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

sub  zaz ( $a , $b , + @args ) { dice  @args . Perla ;}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 variados.

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

En óxido

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

macro_reglas! calcular { // El patrón para una sola `eval` ( eval $e : expr ) => {{ { let val : usize = $e ; // ¡Forzar que los tipos sean números enteros println! ( "{} = {}" , ¡stringificar! { $e }, val ); } }};                   // Descomponer múltiples `eval`s recursivamente ( eval $e : expr , $( eval $es : expr ), + ) => {{ calcular ! { eval $e } calcular ! { $( eval $es ), + } }}; }                 fn  principal () { calcular ! { // ¡Mira mamá! Variádico `¡calcular!`! evaluación 1 + 2 , evaluación 3 + 4 , evaluación ( 2 * 3 ) + 1 } }                   

Rust puede interactuar con el sistema variado de C a través de un c_variadicinterruptor de función. Al igual que con otras interfaces C, el sistema se considera unsafeRust. [15]

en escala

object Programa { // Los métodos variados 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. def privada printArgs ( cadenas : Cadena * ): Unidad = { cadenas . foreach ( imprimirln ) }               def principal ( argumentos : Matriz [ Cadena ]): 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 variados, pero el Anytipo general está disponible.

func  greet ( timeOfTheDay :  String ,  nombres :  String ...)  {  // aquí, los nombres son [String]  print ( "Parece que tenemos \( nombres . recuento ) personas" )  para  nombre  en  nombres  {  print ( "Hola \( nombre ) , bien \( horadeldía ) " )  } }saludar ( timeOfTheDay :  "mañana" ,  nombres :  "Joseph" ,  "Clara" ,  "William" ,  "Maria" )// Salida: // Parece que somos 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 variado cuando su último argumento es args: 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  greet { timeOfTheDay args } { puts "Parece que tenemos [llength $args] personas"      foreach  nombre $args { pone "Hola $nombre, buenas $timeOfTheDay" } }     saludar a "mañana" "Joseph" "Clara" "William" "Maria"     # Salida: # Parece que somos 4 personas # Hola Joseph, buenos días # Hola Clara, buenos días # Hola William, buenos días # Hola María, buenos días

Ver 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 un número no especificado de argumentos en C23 después de la eliminación de las definiciones de funciones de estilo K&R. Dado que C++ ya usaba 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 pronunciada 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). 21st Century C: C 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 está terminado: esto es lo que hay en el menú §N2975 - Relajar los requisitos para listas de parámetros variados". 31 de julio de 2022.
  6. ^ Dorado, Alex; Meneide, JeanHeyd (15 de abril de 2022). "WG14-N2975: Relajación de requisitos para listas de parámetros variados, v3" (PDF) .
  7. ^ "DCL50-CPP. No defina una función variada de estilo C".
  8. ^ "Siga el ejemplo: funciones variadas".
  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. ^ "Variadicas". Óxido con el ejemplo .
  15. ^ "2137-variádico". El libro RFC de Rust .
  16. ^ "página del manual de proceso". Documentación Tcl/Tk .
  17. ^ "argumentos". Wiki de Tcler .

enlaces externos