Из Википедии, свободной энциклопедии
  (Перенаправлено из списка аргументов переменной )
Перейти к навигации Перейти к поиску

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

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

Обзор [ править ]

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

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

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

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

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

Примеры [ править ]

В C [ править ]

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

#include  <stdarg.h>#include  <stdio.h>двойной  средний ( ИНТ  счетчик ,  ...)  {  va_list  ар ;  int  j ;  двойная  сумма  =  0 ; va_start ( ap ,  count );  / * Требуется последний фиксированный параметр (для получения адреса) * /  for  ( j  =  0 ;  j  <  count ;  j ++ )  {  sum  + =  va_arg ( ap ,  int );  / * Увеличивает ap до следующего аргумента. * /  }  va_end ( ap );  сумма  возврата /  счетчик ; }int  main ( int  argc ,  char  const  * argv [])  {  printf ( "% f \ n " ,  среднее ( 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принимает два аргумента: 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снова.
  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ключевого слова. Для аргументов должен быть указан тип, хотя object[]его можно использовать как универсальный.

используя  Систему ;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 ( string []  args )  { Консоль . WriteLine ( Foo ( 1 , 2 )); // 0 Консоль . WriteLine ( Foo ( 1 , 2 , 3 , 10 , 20 )); // 33 } }          

В C ++ [ править ]

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

In Go [ править ]

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

 основной пакетимпорт  "FMT"// Эта функция с переменным числом аргументов принимает произвольное количество целых чисел в качестве аргументов. func  sum ( nums  ... int )  { fmt . Print ( "Сумма" ,  nums )  // Также вариативная функция. total  : =  0 для  _ ,  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 #, Objectтип в Java доступен как универсальный.

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

В JavaScript [ править ]

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

function  sum (... числа )  {  возвращать  числа . уменьшить (( a ,  b )  =>  a  +  b ); }sum ( 1 ,  2 ,  3 )  // 6 sum ( 3 ,  2 )  // 5

В Паскале [ править ]

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

Все процедуры чтения [ln] и записи [ln] имеют одинаковый формат:

чтение [ln] [([файл,] переменная [, переменная ...])];
write [ln] [([файл] [, значение [, значение ...])];

где

  • Файл является необязательным переменным файлом, который , если не указан, по умолчанию входа для чтения и ReadLn , или по умолчанию к выходу для записи и WriteLn ;
  • переменная - это скаляр, такой как char (символ), целое число или вещественное число (или для некоторых компиляторов, определенные типы записей или типы массивов, такие как строки), и
  • значение - это переменная или константа.

Пример:

вар  f :  текст ; ch :  char ; n , a , I , B :  целое число ; S :  строка ;начинать Write ( 'Введите имя файла для записи результатов:' ) ; readln ( s ) ;  присваивать ( f , S ) ; переписать ( е ) ; Напишите ( 'Как тебя зовут?' ) ; readln ( Ввод , S ) ;  Write ( 'Hello,' , S , '! Введите количество вычислений, которое вы хотите выполнить:' ) ; Writeln ( вывод ) ; Напишите ( '?' ) ; readln ( N ) ; Напишите ( 'Для каждой формулы ' , n , 'введите' ) ; write ( 'два целых числа, разделенных одним или несколькими пробелами' ) ; Writeln ; для  i  : = от  1  до  N  делать начинать  Write ( 'Введите номер пары' , i , '?' ) ; читать ( а , б ) ; READLN ; WRITELN ( Out , 'A [' , a , '] + B [' , B , '] =' , A + B ) ; конец ; закрыть ( OUT ) ;конец .

В приведенном выше примере, что касается компилятора, строки 9 и 13 идентичны, потому что, если ввод - это файловая переменная, считываемая оператором read или readln, файловая переменная может быть опущена. Кроме того, компилятор считает строки 15 и 20 идентичными, потому что, если записываемая файловая переменная выводится, ее можно опустить, что означает (в строке 20), поскольку в процедуру не передаются аргументы, в скобках перечислены аргументы. можно не указывать. Строка 26 показывает, что оператор Writeln может иметь любое количество аргументов, и они могут быть строкой в ​​кавычках, переменной или даже результатом формулы.

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

Паскаль также поддерживает аргументы по умолчанию , где значению аргумента, если оно не указано, присваивается значение по умолчанию.

Для первого примера полиморфизма рассмотрим следующее:

функция  add ( a1 , a2 : integer ) : Integer ;  начало  добавить  : =  a1 + a2  конец ; функция  add ( r1 , r2 : real ) : real ;  начало  добавить  : =  a1 + a2  конец ; функция  add ( a1 : целое ; r2 : вещественное ) : вещественное ;  начало  добавить  : =  реальный ( a1 ) + a2  конец ; функция  add ( r1 : real , a2 : integer ) : real ;  начало  добавить  : =  а1 + реальный ( а2 )  конец ;

В приведенном выше примере, если добавить как вызываемое с двумя целочисленными значениями, будет вызвана функция, объявленная в строке 1; если один из аргументов является целым числом, а один - действительным, вызывается функция в строке 3 или 4 в зависимости от того, какое из них является целым. Если оба действительны, вызывается функция в строке 2.

В качестве параметров по умолчанию примите во внимание следующее:

const Три  =  3 ;вар  K :  целое число ; функция  add ( i1 :  integer  =  0 ;  i2 :  целое число  =  0 ; i3 :  целое число  =  0 ;  i4 :  целое число  =  0 ;  i5 :  целое число  =  0 ;  i6 :  целое число  =  0 ;  i7 :  целое число  =  0 ;  i8 :  integer  =  0 ) :  integer ;начинать сложить  : =  i1 + i2 + i3 + I4 + I5 + i6 + I7 + I8 ;конец ;начинать K  : =  добавить ;  {K равно 0} K  : =  добавить ( K , 1 ) ;  {K равно 1} K  : =  добавить ( 1 , 2 ) ;  {K равно 3} K  : =  добавить ( 1 , 2 , Три ) ;  {K равно 6 и т. Д.}конец .

В строке 6 (и строках ниже) параметр = 0 сообщает компилятору: «Если аргумент не указан, предполагается, что аргумент равен нулю». В строке 19 не было задано никаких аргументов, поэтому функция возвращает 0. В строке 20 для любого аргумента может быть указано число или переменная и, как показано в строке 22, константа.

В PHP [ править ]

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

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

И набрал вариативные аргументы:

функция  sum ( int  ... $ nums ) :  int {  return  array_sum ( $ nums ); }эхо-  сумма ( 1 ,  'a' ,  3 );  // TypeError: аргумент 2, переданный в sum (), должен иметь тип int (начиная с PHP 7.3)

В 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).

В Раку [ править ]

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

  1. Сглаженный хлюпающий . Эти параметры объявляются с помощью одной звездочки ( *), и они сглаживают аргументы, растворяя один или несколько слоев элементов, которые можно повторять (например, Iterables ).
    sub  foo ( $ a , $ b , * @args ) { скажем  @args . perl ;}foo ( 1 , 2 ) # [] foo ( 1 , 2 , 3 ) # [3] foo ( 1 , 2 , 3 , "привет" ) # [3 "привет"] foo ( 1 , 2 , 3 , [ 4 , 5 ], [ 6 ]); # [3, 4, 5, 6]
  2. Непроверенная хлюпань . Эти параметры объявляются с двумя звездочками (), и они не сглаживают какие-либо итерируемые аргументы в списке, но сохраняют аргументы более или менее как есть:
    sub  bar ( $ a , $ b , ** @args ) { скажем,  @args . perl ;}бар ( 1 , 2 ); # [] бар ( 1 , 2 , 3 ); # [3] bar ( 1 , 2 , 3 , "привет" ); # [3 "привет"] bar ( 1 , 2 , 3 , [ 4 , 5 ], [ 6 ]); # [3, [4, 5], [6]]
  3. Контекстуальная чушь . Эти параметры объявляются со +знаком плюс ( ), и они применяют « правило единственного аргумента » , которое решает, как обрабатывать аргумент slurpy в зависимости от контекста. Проще говоря, если передан только один аргумент и этот аргумент является итеративным, этот аргумент используется для заполнения массива параметров slurpy. В любом другом случае +@работает как **@(т. Е. Несглаженная чахлость).
    sub  zaz ( $ a , $ b , + @args ) { скажите  @args . perl ;}заз ( 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 endfoo ( 1 ) # выводит `[1] => nil`foo ( 1 ,  2 ) # выводит `[1, 2] => nil`

В Rust [ править ]

Rust не поддерживает вариативные аргументы в функциях. Вместо этого он использует макросы .

macro_rules! вычислить {   // Шаблон для одиночного `eval` ( eval $ e : expr ) => {{     { пусть val : usize = $ e ; // Сделать типы целыми числами println! ( "{} = {}" , строка! { $ e }, val );        } }}; // Рекурсивно разбираем несколько `eval` ( eval $ e : expr , $ ( eval $ es : expr ), + ) => {{       рассчитать ! { eval $ e }     рассчитать ! { $ ( eval $ es ), + }     }};}fn  main () {  рассчитать ! { // Смотри, мама! Вариативное `вычислить!`! оценка 1 + 2 ,       оценка 3 + 4 ,    eval ( 2 * 3 ) + 1      }}

В Swift [ править ]

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

func  greet ( timeOfTheDay :  String ,  names :  String ...)  {  // здесь имена [String]  print ( "Похоже, у нас \ ( names . count ) человек" )  для  имени  в  именах  {  print ( "Hello \ ( name ) , good \ ( timeOfTheDay ) " )  } }Приветствуйте ( timeOfTheDay :  "утро" ,  имена :  "Иосиф" ,  "Клара" ,  "Уильям" ,  "Мария" )// Вывод: // Похоже, у нас 4 человека // Привет Джозеф, доброе утро // Привет Клара, доброе утро // Привет Уильям, доброе утро // Привет Мария, доброе утро

См. Также [ править ]

  • Varargs на языке программирования Java
  • Variadic macro (язык программирования C)
  • Вариативный шаблон

Ссылки [ править ]

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

Внешние ссылки [ править ]

  • Вариативная функция . Задача Rosetta Code, показывающая реализацию вариативных функций на более чем пятидесяти языках программирования.
  • Функции с переменными аргументами - Учебное пособие по функциям с переменными аргументами для C ++
  • Руководство по GNU libc