Fonction variadique - Variadic function
En mathématiques et en programmation informatique , une fonction variadique est une fonction d' arité indéfinie , c'est-à-dire qui accepte un nombre variable d' arguments . La prise en charge des fonctions variadiques diffère considérablement d' un langage de programmation à l'autre .
Le terme variadique est un néologisme , datant de 1936-1937. Le terme n'a pas été largement utilisé jusqu'aux années 1970.
Aperçu
Il existe de nombreuses opérations mathématiques et logiques qui apparaissent naturellement comme des fonctions variadiques. Par exemple, la sommation de nombres ou la concaténation de chaînes ou d'autres séquences sont des opérations qui peuvent être considérées comme applicables à un nombre quelconque d'opérandes (même si formellement dans ces cas la propriété associative est appliquée).
Une autre opération qui a été implémentée en tant que fonction variadique dans de nombreuses langues est le formatage de la sortie. La C fonction printfet la Common Lisp fonction formatsont deux exemples. Les deux prennent un argument qui spécifie le formatage de la sortie et un nombre quelconque d'arguments qui fournissent les valeurs à formater.
Les fonctions variadiques peuvent exposer des problèmes de sécurité de type dans certains langages. Par exemple, les C printf, s'ils sont utilisés avec prudence, peuvent donner lieu à une classe de failles de sécurité appelées attaques de chaîne de format . L'attaque est possible car le support du langage pour les fonctions variadiques n'est pas de type sécurisé : il permet à la fonction d'essayer de retirer plus d'arguments de la pile qu'il n'y a été placé, corrompant la pile et conduisant à un comportement inattendu. En conséquence, le centre de coordination CERT considère les fonctions variadiques en C comme un risque de sécurité de haute gravité.
Dans les langages fonctionnels, les variables peuvent être considérées comme complémentaires à la fonction apply , qui prend une fonction et une liste/séquence/tableau comme arguments, et appelle la fonction avec les arguments fournis dans cette liste, passant ainsi un nombre variable d'arguments à la fonction. Dans le langage fonctionnel Haskell , les fonctions variadiques peuvent être implémentées en retournant une valeur d'une classe de type T ; si les instances de Tsont une valeur de retour finale ret une fonction (T t) => x -> t, cela permet un nombre illimité d'arguments supplémentaires x.
Un sujet connexe dans la recherche sur la réécriture de termes s'appelle les couvertures , ou variables de couverture . Contrairement aux variadiques, qui sont des fonctions avec des arguments, les haies sont elles-mêmes des séquences d'arguments. Ils peuvent également avoir des contraintes ('prendre pas plus de 4 arguments', par exemple) au point où ils ne sont pas de longueur variable (comme 'prendre exactement 4 arguments') - ainsi, les appeler variadiques peut être trompeur. Cependant, ils font référence au même phénomène, et parfois la formulation est mixte, ce qui entraîne des noms tels que variable variadique (synonyme de hedge). Notez le double sens du mot variable et la différence entre les arguments et les variables dans la programmation fonctionnelle et la réécriture de termes. Par exemple, un terme (fonction) peut avoir trois variables, dont une haie, permettant ainsi au terme de prendre trois arguments ou plus (ou deux ou plus si la haie est autorisée à être vide).
Exemples
En C
Pour implémenter de manière portable des fonctions variadiques dans le langage de programmation C , le stdarg.hfichier d'en-tête standard est utilisé. L'ancien en- varargs.htête a été déprécié au profit de stdarg.h. En C++, le fichier d'en-tête cstdargest utilisé.
#include <stdarg.h>
#include <stdio.h>
double average(int count, ...) {
va_list ap;
int j;
double sum = 0;
va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
for (j = 0; j < count; j++) {
sum += va_arg(ap, int); /* Increments ap to the next argument. */
}
va_end(ap);
return sum / count;
}
int main(int argc, char const *argv[]) {
printf("%f\n", average(3, 1, 2, 3));
return 0;
}
Cela calculera la moyenne d'un nombre arbitraire d'arguments. Notez que la fonction ne connaît pas le nombre d'arguments ou leurs types. La fonction ci-dessus s'attend à ce que les types soient int, et que le nombre d'arguments soit passé dans le premier argument (c'est une utilisation fréquente mais en aucun cas imposée par le langage ou le compilateur). Dans certains autres cas, par exemple printf , le nombre et les types d'arguments sont déterminés à partir d'une chaîne de format. Dans les deux cas, cela dépend du programmeur pour fournir les informations correctes. (Alternativement, une valeur sentinelle comme NULLpeut être utilisée pour indiquer le nombre.) Si moins d'arguments sont transmis que la fonction ne le croit, ou si les types d'arguments sont incorrects, cela peut entraîner la lecture dans des zones de mémoire invalides et peut conduire à vulnérabilités comme l' attaque de chaîne de format .
stdarg.hdéclare un type, va_listet définit quatre macros: va_start, va_arg, va_copyet va_end. Chaque appel de va_startet va_copydoit correspondre à un appel correspondant de va_end. Lorsque vous travaillez avec des arguments variables, une fonction déclare normalement une variable de type va_list( apdans l'exemple) qui sera manipulée par les macros.
-
va_startprend deux arguments, unva_listobjet et une référence au dernier paramètre de la fonction (celui avant les points de suspension ; la macro l'utilise pour se repérer). Il initialise l'va_listobjet à utiliser parva_argouva_copy. Le compilateur émettra normalement un avertissement si la référence est incorrecte (par exemple une référence à un paramètre différent du précédent, ou une référence à un objet totalement différent), mais n'empêchera pas la compilation de se terminer normalement. -
va_argprend deux arguments, unva_listobjet (précédemment initialisé) et un descripteur de type. Il se développe jusqu'à l'argument de variable suivant et a le type spécifié. Les appels successifs deva_argpermettent de traiter chacun des arguments de la variable à tour de rôle. Un comportement non spécifié se produit si le type est incorrect ou s'il n'y a pas d'argument de variable suivant. -
va_endprend un argument, unva_listobjet. Il sert à nettoyer. Si l'on voulait, par exemple, scanner les arguments des variables plus d'une fois, le programmeur réinitialiserait votreva_listobjet en l'invoquantva_endpuis deva_startnouveau dessus. -
va_copyprend deux arguments, les deux étant desva_listobjets. Il clone le second (qui doit avoir été initialisé) dans le premier. Pour en revenir à l'exemple "analyser les arguments de la variable plus d'une fois", cela pourrait être réalisé en appelantva_startsur un premierva_list, puis en utilisantva_copypour le cloner dans un secondva_list. Après avoir scanné les arguments des variables une première fois avecva_arget le premierva_list(en les débarrassant avecva_end), le programmeur pourrait scanner les arguments des variables une seconde fois avecva_arget le secondva_list. N'oubliez pasva_endle clonéva_list.
En C#
C# décrit les fonctions variadiques à l'aide du paramsmot - clé. Un type doit être fourni pour les arguments, bien qu'il object[]puisse être utilisé comme fourre-tout.
using System;
class Program
{
static int Foo(int a, int b, params int[] args)
{
// Return the sum of the integers in args, ignoring a and b.
int sum = 0;
foreach (int i in args)
sum += i;
return sum;
}
static void Main(string[] args)
{
Console.WriteLine(Foo(1, 2)); // 0
Console.WriteLine(Foo(1, 2, 3, 10, 20)); // 33
}
}
En C++
La fonction variadique de base en C++ est en grande partie identique à celle en C. La seule différence réside dans la syntaxe, où la virgule avant les points de suspension peut être omise.
#include <iostream>
#include <cstdarg>
void simple_printf(const char* fmt...) // C-style "const char* fmt, ..." is also valid
{
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') {
// note automatic conversion to integral type
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(args);
}
int main()
{
simple_printf("dcff", 3, 'a', 1.999, 42.5);
}
Les modèles variadiques (pack de paramètres) peuvent également être utilisés en C++ avec des expressions de repli intégrées au langage .
#include <iostream>
template <typename... 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', true, nullptr); // Foo b true nullptr
}
Les normes de codage CERT pour C++ préfèrent fortement l'utilisation de modèles variadiques (pack de paramètres) en C++ à la fonction variadique de style C en raison d'un risque moindre d'abus.
En Go
Les fonctions variadiques dans Go peuvent être appelées avec n'importe quel nombre d'arguments de fin. fmt.Printlnest une fonction variadique commune ; il utilise une interface vide comme type fourre-tout.
package main
import "fmt"
// This variadic function takes an arbitrary number of ints as arguments.
func sum(nums ...int) {
fmt.Print("The sum of ", nums) // Also a variadic function.
total := 0
for _, num := range nums {
total += num
}
fmt.Println(" is", total) // Also a variadic function.
}
func main() {
// Variadic functions can be called in the usual way with individual
// arguments.
sum(1, 2) // "The sum of [1 2] is 3"
sum(1, 2, 3) // "The sum of [1 2 3] is 6"
// If you already have multiple args in a slice, apply them to a variadic
// function using func(slice...) like this.
nums := []int{1, 2, 3, 4}
sum(nums...) // "The sum of [1 2 3 4] is 10"
}
Sortir:
The sum of [1 2] is 3 The sum of [1 2 3] is 6 The sum of [1 2 3 4] is 10
En Java
Comme avec C#, le Objecttype en Java est disponible en tant que fourre-tout.
public class Program {
// Variadic methods store any additional arguments they receive in an array.
// Consequentially, `printArgs` is actually a method with one parameter: a
// variable-length array of `String`s.
private static void printArgs(String... strings) {
for (String string : strings) {
System.out.println(string);
}
}
public static void main(String[] args) {
printArgs("hello"); // short for printArgs(["hello"])
printArgs("hello", "world"); // short for printArgs(["hello", "world"])
}
}
En JavaScript
JavaScript ne se soucie pas des types d'arguments variadiques.
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(3, 2)); // 5
console.log(sum()); // 0
en pascal
Pascal a quatre procédures intégrées qui sont définies comme variadiques, qui, en raison de cette condition spéciale, sont intrinsèques au compilateur. Ce sont les lire , readln , écriture et writeln procédures. Cependant, il existe des spécifications alternatives permettant des arguments par défaut aux procédures ou aux fonctions qui les font fonctionner de manière variadique, ainsi qu'un polymorphisme qui permet à une procédure ou à une fonction d'avoir des paramètres différents.
Les procédures read[ln] et write[ln] ont toutes le même format :
- read[ln] [( [fichier ,] variable [, variable ...] )] ;
- write[ln] [( [fichier][, valeur [, valeur ...] )] ;
où
- file est une variable de fichier facultative, qui, si elle est omise, est par défaut en entrée pour read et readln , ou par défaut en sortie pour write et writeln ;
- la variable est un scalaire tel qu'un char (caractère), un entier ou un réel (ou pour certains compilateurs, certains types d'enregistrements ou de tableaux tels que des chaînes), et
- valeur est une variable ou une constante.
Exemple:
var
f: text;
ch: char;
n,a,I,B: Integer;
S: String;
begin
Write('Enter name of file to write results: ');
readln(s);
assign(f,S);
rewrite(f);
Write('What is your name? ');
readln(Input,S);
Write('Hello, ',S,'! Enter the number of calculations you want to do:');
writeln(output);
Write('? ');
readln(N);
Write('For each of the ',n,' formulas, enter ');
write('two integers separated by one or more spaces');
writeln;
for i := 1 to N do
begin
Write('Enter pair #',i,'? ');
read(a,b);
READLN;
WRITELN(Out,'A [',a,'] + B [',B,'] =',A+B);
end;
close(OUT);
end.
Dans l'exemple ci-dessus, en ce qui concerne le compilateur, les lignes 9 et 13 sont identiques, car si l' entrée est la variable de fichier lue par une instruction read ou readln, la variable de fichier peut être omise. De plus, le compilateur considère que les lignes 15 et 20 sont identiques, car si la variable de fichier dans laquelle on écrit est sortie, elle peut être omise, ce qui signifie (à la ligne 20) puisqu'il n'y a pas d'arguments passés à la procédure les parenthèses répertoriant les arguments peut être omis. La ligne 26 montre que l'instruction writeln peut avoir n'importe quel nombre d'arguments, et ils peuvent être une chaîne entre guillemets, une variable ou même un résultat de formule.
Le Pascal Objet prend en charge les procédures et fonctions polymorphes , où différentes procédures ou fonctions peuvent avoir le même nom mais se distinguent par les arguments qui leur sont fournis.
Pascal prend également en charge les arguments par défaut , où la valeur d'un argument, s'il n'est pas fourni, reçoit une valeur par défaut.
Pour le premier exemple, le polymorphisme, considérez ce qui suit :
function add(a1,a2:integer):Integer; begin add := a1+a2 end;
function add(r1,r2:real):real; begin add := a1+a2 end;
function add(a1:integer;r2:real):real; begin add := real(a1)+a2 end;
function add(r1:real,a2:integer):real; begin add := a1+real(a2) end;
Dans l'exemple ci-dessus, si add as est appelé avec deux valeurs entières, la fonction déclarée à la ligne 1 sera appelée ; si l'un des arguments est un entier et l'autre est réel, la fonction de la ligne 3 ou 4 est appelée selon l'entier. Si les deux sont réels, la fonction de la ligne 2 est appelée.
Pour les paramètres par défaut, tenez compte des éléments suivants :
const
Three = 3;
var
K: Integer;
function add(i1: integer = 0;
i2: integer = 0;
i3: integer = 0;
i4: integer = 0;
i5: integer = 0;
i6: integer = 0;
i7: integer = 0;
i8: integer = 0): integer;
begin
add := i1+i2+i3+I4+I5+i6+I7+I8;
end;
begin
K := add; { K is 0}
K := add(K,1); { K is 1}
K := add(1,2); { K is 3}
K := add(1,2,Three); { K is 6, etc.}
end.
Sur la ligne 6 (et les lignes ci-dessous), le paramètre = 0 indique au compilateur : « si aucun argument n'est fourni, supposez que l'argument est zéro ». À la ligne 19, aucun argument n'a été donné, donc la fonction renvoie 0. À la ligne 20, un nombre ou une variable peut être fourni pour n'importe quel argument, et comme indiqué à la ligne 22, une constante.
En PHP
PHP ne se soucie pas des types d'arguments variadiques à moins que l'argument ne soit tapé.
function sum(...$nums): int
{
return array_sum($nums);
}
echo sum(1, 2, 3); // 6
Et tapé des arguments variadiques :
function sum(int ...$nums): int
{
return array_sum($nums);
}
echo sum(1, 'a', 3); // TypeError: Argument 2 passed to sum() must be of the type int (since PHP 7.3)
En Python
Python ne se soucie pas des types d'arguments variadiques.
def foo(a, b, *args):
print(args) # args is a tuple (immutable sequence).
foo(1, 2) # ()
foo(1, 2, 3) # (3,)
foo(1, 2, 3, "hello") # (3, "hello")
Les arguments des mots-clés peuvent être stockés dans un dictionnaire, par exemple def bar(*args, **kwargs).
En Raku
Dans Raku , les types de paramètres qui créent des fonctions variadiques sont appelés paramètres de tableau slurpy et ils sont classés en trois groupes :
- Slurpy aplati
- Ces paramètres sont déclarés avec un seul astérisque (
*) et ils aplatissent les arguments en dissolvant une ou plusieurs couches d'éléments qui peuvent être itérés (c'est-à-dire, Iterables ).sub foo($a, $b, *@args) { say @args.perl; } foo(1, 2) # [] foo(1, 2, 3) # [3] foo(1, 2, 3, "hello") # [3 "hello"] foo(1, 2, 3, [4, 5], [6]); # [3, 4, 5, 6]
- Slurpy non aplati
- Ces paramètres sont déclarés avec deux astérisques () et ils n'aplatissent aucun argument itérable dans la liste, mais conservent les arguments plus ou moins tels quels :
sub bar($a, $b, **@args) { say @args.perl; } bar(1, 2); # [] bar(1, 2, 3); # [3] bar(1, 2, 3, "hello"); # [3 "hello"] bar(1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]]
- Slurpy contextuel
- Ces paramètres sont déclarés avec un signe plus (
+) et ils appliquent la " règle d'argument unique " , qui décide comment gérer l'argument slurpy en fonction du contexte. En termes simples, si un seul argument est passé et que cet argument est itérable, cet argument est utilisé pour remplir le tableau de paramètres slurpy. Dans tous les autres cas,+@fonctionne comme**@(c'est-à-dire, slurpy non aplati).sub zaz($a, $b, +@args) { say @args.perl; } zaz(1, 2); # [] zaz(1, 2, 3); # [3] zaz(1, 2, 3, "hello"); # [3 "hello"] zaz(1, 2, [4, 5]); # [4, 5], single argurment fills up array zaz(1, 2, 3, [4, 5]); # [3, [4, 5]], behaving as **@ zaz(1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]], behaving as **@
En Rubis
Ruby ne se soucie pas des types d'arguments variadiques.
def foo(*args)
print args
end
foo(1)
# prints `[1]=> nil`
foo(1, 2)
# prints `[1, 2]=> nil`
En rouille
Rust ne prend pas en charge les arguments variadiques dans les fonctions. Au lieu de cela, il utilise des macros .
macro_rules! calculate {
// The pattern for a single `eval`
(eval $e:expr) => {{
{
let val: usize = $e; // Force types to be integers
println!("{} = {}", stringify!{$e}, val);
}
}};
// Decompose multiple `eval`s recursively
(eval $e:expr, $(eval $es:expr),+) => {{
calculate! { eval $e }
calculate! { $(eval $es),+ }
}};
}
fn main() {
calculate! { // Look ma! Variadic `calculate!`!
eval 1 + 2,
eval 3 + 4,
eval (2 * 3) + 1
}
}
Rust est capable d'interagir avec le système variadique de C via un c_variadiccommutateur de fonction. Comme pour les autres interfaces C, le système est considéré comme unsafeRust.
En Swift
Swift se soucie du type d'arguments variadiques, mais le type fourre-tout Anyest disponible.
func greet(timeOfTheDay: String, names: String...) {
// here, names is [String]
print("Looks like we have \(names.count) people")
for name in names {
print("Hello \(name), good \(timeOfTheDay)")
}
}
greet(timeOfTheDay: "morning", names: "Joseph", "Clara", "William", "Maria")
// Output:
// Looks like we have 4 people
// Hello Joseph, good morning
// Hello Clara, good morning
// Hello William, good morning
// Hello Maria, good morning
Voir également
- Varargs dans le langage de programmation Java
- Macro variadique (langage de programmation C)
- Modèle variadique
Les références
Liens externes
- Fonction variadique . Tâche Rosetta Code montrant l'implémentation de fonctions variadiques dans plus de cinquante langages de programmation.
- Fonctions d'arguments variables — Un didacticiel sur les fonctions d'arguments variables pour C++
- manuel GNU libc