Функция с переменными числами

редактировать

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

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

Содержание
  • 1 Обзор
  • 2 Примеры
    • 2.1 В C
    • 2.2 В C #
    • 2.3 В C ++
    • 2.4 В Go
    • 2.5 В Java
    • 2.6 В JavaScript
    • 2.7 В PHP
    • 2.8 В Python
    • 2.9 В Raku
    • 2.10 В Ruby
    • 2.11 В Swift
  • 3 См. Также
  • 4 Ссылки
  • 5 Внешние ссылки
Обзор

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

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

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

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

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

Примеры

В C

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

#include #include двойное среднее (int count,...) {va_list ap; int j; двойная сумма = 0; va_start (ap, count); / * Требуется последний фиксированный параметр (для получения адреса) * / 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; }

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

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

  1. va_startпринимает два аргумента: объект va_listи ссылку на последний параметр функции (тот, который стоит перед многоточием; макрос использует его, чтобы ориентироваться). Он инициализирует объект va_listдля использования va_argили va_copy. Обычно компилятор выдает предупреждение, если ссылка неверна (например, ссылка на параметр, отличный от последнего, или ссылка на совершенно другой объект), но не препятствует нормальному завершению компиляции.
  2. va_argпринимает два аргумента: объект va_list(ранее инициализированный) и дескриптор типа. Он расширяется до следующего аргумента переменной и имеет указанный тип. Последовательные вызовы va_argпозволяют по очереди обрабатывать каждый из переменных аргументов. Неопределенное поведение возникает, если тип неверен или отсутствует следующий аргумент переменной.
  3. va_endпринимает один аргумент, объект va_list. Он служит для уборки. Если вы хотите, например, просканировать аргументы переменных более одного раза, вы должны повторно инициализировать свой объект va_list, вызвав va_end, а затем снова va_startна it.
  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 # описывает функции с переменным числом аргументов с помощью ключевого слова params. Для аргументов должен быть указан тип, хотя объектможет использоваться как универсальный.

с использованием системы; class Program {static int Foo (int a, int b, params int args) {// Возвращает сумму целых чисел в args, игнорируя a и b. int sum = 0; foreach (int i в args) sum + = i; сумма возврата; } static void Main (строковые аргументы) {Console.WriteLine (Foo (1, 2)); // 0 Console.WriteLine (Foo (1, 2, 3, 10, 20)); // 33}}

В C ++

#include #include void simple_printf (const char * fmt...); int main () {simple_printf ("dcff", 3, 'а', 1.999, 42.5); } void simple_printf (const char * fmt...) // "const char * fmt,..." в стиле C также допустимо {va_list args; va_start (аргументы, fmt); в то время как (* fmt! = '\ 0') {если (* fmt == 'd') {int я = 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(c) << '\n'; } else if (*fmt == 'f') { double d = va_arg(args, double); std::cout << d << '\n'; } ++fmt; } va_end(args); }

В Go

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

основной импорт пакета "fmt" // Эта вариативная функция принимает произвольное количество целых чисел в качестве аргументов. func sum (nums... int) {fmt.Print ("The sum of", nums) // Также вариативная функция. total: = 0 for _, num: = range nums {total + = num} fmt.Println ("is", total) // Также вариативная функция. } func main () {// Функции с переменным числом аргументов могут быть вызваны обычным способом с отдельными // аргументами. sum (1, 2) // «Сумма [1 2] равна 3» sum (1, 2, 3) // «Сумма [1 2 3] равна 6» // Если у вас уже есть несколько аргументов в срез, примените их к // функции с переменным числом аргументов, используя функцию func (slice...) следующим образом. nums: = int {1, 2, 3, 4} sum (nums...) // «Сумма [1 2 3 4] равна 10»}

Вывод:

Сумма [1 2 ] равно 3 Сумма [1 2 3] равна 6. Сумма [1 2 3 4] составляет 10

В Java

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

программа открытого класса {private static void printArgs (String... strings) {for (String string: strings) {System.out.println (string); }} public static void main (String args) {// компилятор помещает аргумент (ы), переданный в printArgs, внутрь массива // что означает, что printArgs - это просто метод, который принимает единственный аргумент, который представляет собой массив строк переменной длины printArgs (" Здравствуйте"); // сокращение от printArgs (["hello"]) printArgs ("hello", "world"); // сокращение от printArgs (["hello", "world"])}}

В JavaScript

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

функция sum (... числа) {return numbers.reduce ((a, b) =>a + b); } sum (1, 2, 3) // 6 sum (3, 2) // 5

В PHP

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

функция sum (... $ nums) {return array_sum ($ nums); } эхо-сумма (1, 2, 3); // 6

В Python

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

def foo (a, b, * args): print (args) # args - это кортеж (неизменяемая последовательность). foo (1, 2) # () foo (1, 2, 3) # (3,) foo (1, 2, 3, "привет") # (3, "привет")

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

В Raku

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

  1. Уплотненная слизь . Эти параметры объявляются с помощью одной звездочки (*), и они сглаживают аргументы, растворяя один или несколько слоев элементов, которые можно повторять (например, Iterables ).
    sub foo ($ a, $ b, * @ args) {say @ args.perl;} foo (1, 2) # foo (1, 2, 3) # [3] foo (1, 2, 3, "привет") # [3 " hello "] foo (1, 2, 3, [4, 5], [6]); # [3, 4, 5, 6]
  2. Unflattened slurpy . Эти параметры объявлены с двумя звездочками () и они не сглаживают какие-либо итерируемые аргументы в списке, но сохраняют аргументы более или менее как есть:
    subbar ($ a, $ b, ** @ args) {say @ args.perl; } bar (1, 2); # bar (1, 2, 3); # [3] bar (1, 2, 3, "привет"); # [3 "привет"] bar (1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]]
  3. Contextual slurpy . Эти параметры объявлены с плюсом (+) знак, и они применяют «правило одного аргумента », которое решает, как обрабатывать аргумент slurpy на основе контекста. Проще говоря, если передан только один аргумент и этот аргумент является повторяемым, этот аргумент используется для заполнения массива параметров slurpy. В любом другом случае + @работает как ** @(т. Е. Не сглаженное болтовня).
    sub zaz ($ a, $ b, + @ args) {скажем @ args.perl; } zaz (1, 2); # zaz (1, 2, 3); # [3] zaz (1, 2, 3, "привет"); # [3 "привет"] zaz (1, 2, [4, 5]); # [4, 5], единственный аргумент заполняет массив zaz (1, 2, 3, [4, 5]); # [3, [4, 5]], ведет себя как ** @ zaz (1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]], поведение как ** @

В Ruby

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

def foo (* args) print args end foo (1) # выводит `[1] =>nil` foo (1, 2) # выводит` [1, 2] =>nil`

В Swift

Swift заботится о типах вариативных аргументов, но доступен универсальный тип Any.

func greet (timeOfTheDay: String, names: String...) {// здесь имена [String] print ("Похоже, у нас есть \ (names.count) people") вместо имени в именах {print ( "Hello \ (name), good \ (timeOfTheDay)")}} greet (timeOfTheDay: "morning", names: "Joseph", "Clara", "William", "Maria") // Вывод: // Похож на у нас 4 человека // Привет Джозеф, доброе утро // Привет, Клара, доброе утро // Привет, Уильям, доброе утро // Привет, Мария, доброе утро
См. также
Ссылки
Внешние ссылки
Последняя правка сделана 2021-06-18 09:52:51
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте