Вариативная функция - Variadic function

В математика И в компьютерное программирование, а вариативная функция это функция неопределенного арность, то есть тот, который принимает переменное количество аргументы. Поддержка вариативных функций сильно различается среди языки программирования.

Период, термин вариативный это неологизм, восходящий к 1936-1937 гг.[1] Этот термин не использовался широко до 1970-х годов.

Обзор

Есть много математических и логических операций, которые естественно воспринимаются как функции с переменными числами. Например, суммирование чисел или конкатенация строк или других последовательностей - это операции, которые можно рассматривать как применимые к любому количеству операндов (даже если формально в этих случаях ассоциативное свойство применены).

Другая операция, реализованная в виде вариативной функции на многих языках, - это форматирование вывода. В C функция printf и Common Lisp функция формат два таких примера. Оба принимают один аргумент, определяющий форматирование вывода, и любой номер аргументов, которые предоставляют значения для форматирования.

Функции с переменным числом аргументов могут открывать безопасность типа проблемы на некоторых языках. Например, C printfпри неосторожном использовании может привести к появлению класса дыр в системе безопасности, известного как атаки на строку формата. Атака возможна, потому что языковая поддержка вариативных функций не является типобезопасной: она позволяет функции попытаться извлечь больше аргументов из стек чем были помещены туда, что привело к повреждению стека и неожиданному поведению. Вследствие этого Координационный центр CERT рассматривает вариативные функции в C как угрозу безопасности с высокой степенью серьезности.[2]

В функциональных языках вариативность можно рассматривать как дополнение к применять function, которая принимает функцию и список / последовательность / массив в качестве аргументов и вызывает функцию с аргументами, указанными в этом списке, тем самым передавая функции переменное количество аргументов.[нужна цитата ] На функциональном языке Haskell, вариативные функции могут быть реализованы путем возврата значения тип класс Т; если экземпляры Т являются окончательным возвращаемым значением р и функция (T t) => x -> t, это позволяет использовать любое количество дополнительных аргументов Икс.[требуется дальнейшее объяснение ]

Связанная тема в переписывание терминов исследование называется живые изгороди, или переменные хеджирования.[3] В отличие от вариативных функций, которые представляют собой функции с аргументами, хеджирование - это сами последовательности аргументов. Они также могут иметь ограничения (например, `` принимать не более 4 аргументов '') до точки, в которой они не имеют переменной длины (например, `` принимать ровно 4 аргумента '') - таким образом вызывая их вариативность может вводить в заблуждение. Однако они относятся к одному и тому же явлению, и иногда формулировка смешана, в результате чего появляются такие имена, как вариативная переменная (синоним хеджирования). Обратите внимание на двойное значение слова переменная и разница между аргументами и переменными в функциональном программировании и переписывании терминов. Например, терм (функция) может иметь три переменных, одна из которых является хеджированием, что позволяет термину принимать три или более аргумента (или два или более, если хеджирование может быть пустым).

Примеры

В C

Для переносимой реализации вариативных функций на языке программирования C стандарт stdarg.h используется заголовочный файл. Старший varargs.h заголовок был устарел в пользу stdarg.h. В C ++ заголовочный файл cstdarg используется.[4]

#включают <stdarg.h>#включают <stdio.h>двойной средний(int считать, ...) {    va_list ap;    int j;    двойной сумма = 0;    va_start(ap, считать); / * Требуется последний фиксированный параметр (для получения адреса) * /    для (j = 0; j < считать; j++) {        сумма += va_arg(ap, int); / * Увеличивает ap до следующего аргумента. * /    }    va_end(ap);    вернуть сумма / считать;}int основной(int argc, char const *argv[]) {    printf("% f п", средний(3, 1, 2, 3));    вернуть 0;}

Это вычислит среднее значение произвольного количества аргументов. Обратите внимание, что функция не знает количество аргументов или их типы. Вышеупомянутая функция ожидает, что типы будут int, и что количество аргументов передается в первом аргументе (это частое использование, но ни в коем случае не обеспечивается языком или компилятором). В некоторых других случаях, например printf, количество и типы аргументов вычисляются из строки формата. В обоих случаях это зависит от программиста, который предоставит правильную информацию. Если передано меньше аргументов, чем полагает функция, или типы аргументов неверны, это может привести к чтению в недопустимые области памяти и может привести к уязвимостям, таким как атака на строку формата.

stdarg.h объявляет тип, va_list, и определяет четыре макроса: va_start, va_arg, va_copy, и va_end. Каждый призыв va_start и va_copy должен сопровождаться соответствующим вызовом va_end. При работе с переменными аргументами функция обычно объявляет переменную типа va_list (ap в примере), которым будут управлять макросы.

  1. va_start принимает два аргумента, a va_list объект и ссылку на последний параметр функции (тот, который стоит перед многоточием; макрос использует его, чтобы ориентироваться). Он инициализирует va_list объект для использования va_arg или va_copy. Компилятор обычно выдает предупреждение, если ссылка неверна (например, ссылка на параметр, отличный от последнего, или ссылка на совершенно другой объект), но не препятствует нормальному завершению компиляции.
  2. va_arg принимает два аргумента, a va_list объект (ранее инициализированный) и дескриптор типа. Он расширяется до следующего аргумента переменной и имеет указанный тип. Последовательные призывы va_arg разрешить обработку каждого из переменных аргументов по очереди. Неопределенное поведение возникает, если тип неверен или отсутствует следующий аргумент переменной.
  3. va_end принимает один аргумент, va_list объект. Он служит для уборки. Если вы хотите, например, сканировать переменные аргументы более одного раза, вы бы повторно инициализировали свой va_list объект, вызвав va_end а потом va_start снова на нем.
  4. va_copy принимает два аргумента, оба из них va_list объекты. Он клонирует второй (который должен быть инициализирован) в первый. Возвращаясь к примеру «сканировать переменные аргументы более одного раза», этого можно добиться, вызвав va_start на первом va_list, затем используя va_copy клонировать в секунду va_list. После первого сканирования переменных аргументов с помощью va_arg и первый va_list (избавившись от него с va_end), вы можете сканировать переменные аргументы второй раз с помощью va_arg а второй va_list. Не забывай va_end клон va_list.

В C #

C # описывает вариативные функции с помощью параметры ключевое слово. Для аргументов должен быть указан тип, хотя объект [] может использоваться как универсальный.

с помощью Система;класс Программа{    статический int Фу(int а, int б, параметры int[] аргументы)    {        // Возвращаем сумму целых чисел в args, игнорируя a и b.        int сумма = 0;        для каждого (int я в аргументы)            сумма += я;        вернуть сумма;    }            статический пустота Основной(строка[] аргументы)    {        Консоль.WriteLine(Фу(1, 2));  // 0        Консоль.WriteLine(Фу(1, 2, 3, 10, 20));  // 33    }}

В C ++

#включают <iostream>#включают <cstdarg>пустота simple_printf(const char* fmt...) ;int основной(){    simple_printf("dcff", 3, 'а', 1.999, 42.5); }пустота simple_printf(const char* fmt...)      // C-стиль "const char * fmt, ..." также допустим{    va_list аргументы;    va_start(аргументы, fmt);     в то время как (*fmt != '\0') {        если (*fmt == 'd') {            int я = va_arg(аргументы, int);            стандартное::cout << я << ' n';        } еще если (*fmt == 'c') {            // обратите внимание на автоматическое преобразование в целочисленный тип            int c = va_arg(аргументы, int);            стандартное::cout << static_cast<char>(c) << ' n';        } еще если (*fmt == 'f') {            двойной d = va_arg(аргументы, двойной);            стандартное::cout << d << ' n';        }        ++fmt;    }     va_end(аргументы);}

In Go

Функции с переменным числом аргументов можно вызывать с любым количеством завершающих аргументов.[5] fmt.Println - обычная вариативная функция; он использует пустой интерфейс как универсальный тип.

пакет основнойимпорт "fmt"// Эта вариативная функция принимает в качестве аргументов произвольное количество целых чисел.func сумма(числа ...int) {	fmt.Распечатать("Сумма ", числа) // Также вариативная функция.	Всего := 0	для _, число := ассортимент числа {		Всего += число	}	fmt.Println(" является", Всего) // Также вариативная функция.}func основной() {	// Функции Variadic могут вызываться обычным способом с индивидуальными	// аргументы.	сумма(1, 2)  // "Сумма [1 2] равна 3"	сумма(1, 2, 3) // "Сумма [1 2 3] равна 6"	// Если у вас уже есть несколько аргументов в срезе, примените их к вариативному	// функция с использованием func (slice ...) вот так.	числа := []int{1, 2, 3, 4}	сумма(числа...) // "Сумма [1 2 3 4] равна 10"}

Вывод:

Сумма [1 2] равна 3 Сумма [1 2 3] равна 6 Сумма [1 2 3 4] равна 10

В Java

Как и в случае с C #, Объект type доступен как универсальный.

общественный класс Программа {    частный статический пустота printArgs(Строка... струны) {        для (Строка строка : струны) {            Система.вне.println(строка);        }    }    общественный статический пустота основной(Строка[] аргументы) {        // компилятор помещает аргументы, переданные в printArgs, внутрь массива        // означает, что printArgs - это просто метод, который принимает единственный аргумент, который представляет собой массив строк переменной длины                printArgs("Здравствуйте");                 // сокращение от printArgs (["привет"])        printArgs("Здравствуйте", "Мир");        // сокращение от printArgs (["привет", "мир"])    }}

В JavaScript

JavaScript не заботится о типах вариативных аргументов.

функция сумма(...числа) {    вернуть числа.уменьшить((а, б) => а + б);}сумма(1, 2, 3) // 6сумма(3, 2) // 5

В PHP

PHP не заботится о типах вариативных аргументов.

функция сумма(...$ nums): целое число{    вернуть array_sum($ nums);}эхо сумма(1, 2, 3); // 6

В Python

Python не заботится о типах вариативных аргументов.

def фу(а, б, *аргументы):    Распечатать(аргументы)  # args - это кортеж (неизменяемая последовательность).фу(1, 2) # ()фу(1, 2, 3) # (3,)фу(1, 2, 3, "Здравствуйте") # (3, "привет")

Аргументы ключевого слова могут быть сохранены в словаре, например def bar (* аргументы, ** kwargs).

В Раку

В Raku параметры, которые создают вариативные функции, известны как хлюпающий параметры массива, и они подразделяются на три группы:

  1. Сплющенный хлюпающий. Эти параметры объявляются одной звездочкой (*), и они сглаживают аргументы, растворяя один или несколько слоев элементов, которые можно повторять (т. е. Итерируемые объекты ).
    суб фу($ а, $ млрд, *@args) {    сказать @args.Perl;}фу(1, 2)                  # []фу(1, 2, 3)               # [3]фу(1, 2, 3, "Здравствуйте")      # [3 "привет"]фу(1, 2, 3, [4, 5], [6]); # [3, 4, 5, 6]
  2. Непроверенный хлюпающий. Эти параметры объявляются с двумя звездочками (), и они не сглаживают какие-либо итерируемые аргументы в списке, но сохраняют аргументы более или менее как есть:
    суб бар($ а, $ млрд, **@args) {    сказать @args.Perl;}бар(1, 2);                 # []бар(1, 2, 3);              # [3]бар(1, 2, 3, "Здравствуйте");     # [3 "привет"]бар(1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]]
  3. Контекстуальная чушь. Эти параметры объявлены с плюсом (+) подписать, и они применяют "правило единственного аргумента ", который решает, как обрабатывать аргумент slurpy в зависимости от контекста. Проще говоря, если передан только один аргумент и этот аргумент является итеративным, этот аргумент используется для заполнения массива параметров slurpy. В любом другом случае, +@ работает как **@ (т. е. невыровненная глотка).
    суб заз($ а, $ млрд, +@args) {    сказать @args.Perl;}заз(1, 2);                 # []заз(1, 2, 3);              # [3]заз(1, 2, 3, "Здравствуйте");     # [3 "привет"]заз(1, 2, [4, 5]);         # [4, 5], единственный аргумент заполняет массивзаз(1, 2, 3, [4, 5]);      # [3, [4, 5]], ведет себя как ** @заз(1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]], ведет себя как ** @

В Ruby

Ruby не заботится о типах вариативных аргументов.

def фу(*аргументы)  Распечатать аргументыконецфу(1)# выводит `[1] => nil`фу(1, 2)# выводит `[1, 2] => nil`

В Swift

Swift заботится о типах вариативных аргументов, но все Любые тип есть в наличии.

func приветствовать(timeOfTheDay: Строка, имена: Строка...) {    // здесь имена [String]        Распечатать("Похоже, у нас есть \(имена.считать) люди")        для имя в имена {        Распечатать("Здравствуйте \(имя), хорошо \(timeOfTheDay)")    }}приветствовать(timeOfTheDay: "утро", имена: "Иосиф", "Клара", "Уильям", "Мария")// Вывод:// Похоже, у нас 4 человека// Привет Джозеф, доброе утро// Привет Клара, доброе утро// Привет Уильям, доброе утро// Привет Мария, доброе утро

Смотрите также

использованная литература

  1. ^ Генри С. Леонард и Х. Н. Гудман, Подсчет людей. Резюме выступления на втором собрании Ассоциации символической логики, состоявшемся в Кембридже 28-30 декабря 1936 г. [1], Журнал символической логики 2(1) 1937, 63.
  2. ^ Клеменс, Бен (2014). 21st Century C: C Советы от новой школы. O'Reilly Media, Inc. стр. 224. ISBN  1491904445.
  3. ^ CLP (H): Программирование логики ограничений для хеджирования
  4. ^ « (stdarg.h) - Справочник по C ++». www.cplusplus.com.
  5. ^ https://gobyexample.com/variadic-functions

внешние ссылки