가변 인자 함수

수학 및 컴퓨터 프로그래밍에서 **가변 인자 함수**(variadic function)는 항수가 부정인 함수, 즉 가변 개수의 인자를 받는 함수이다. 가변 인자 함수에 대한 지원은 프로그래밍 언어마다 크게 다르다. 가변 인자(variadic)라는 용어는 1936년/1937년으로 거슬러 올라가는 신조어이다.Henry S. Leonard and H. N. Goodman, A calculus of individuals. Abstract of a talk given at the Second Meeting of the Association for Symbolic Logic, held in Cambridge MA on December 28–30, 1936, Journal of Symbolic Logic **2**(1) 1937, 63. 이 용어는 1970년대까지 널리 사용되지 않았다.
# 개요
자연스럽게 가변 인자 함수로 나타나는 많은 수학적 및 논리적 연산이 있다. 예를 들어, 숫자의 합계 또는 문자열이나 다른 시퀀스의 문자열 연결은 임의의 수의 피연산자에 적용될 수 있다고 생각할 수 있는 연산이다 (이러한 경우 공식적으로 결합법칙이 적용되더라도). 많은 언어에서 가변 인자 함수로 구현된 또 다른 연산은 출력 포맷팅이다. C 함수 printf와 커먼 리스프 함수 format이 두 가지 예이다. 둘 다 출력의 포맷팅을 지정하는 하나의 인수를 받고, 포맷팅할 값을 제공하는 임의의 수의 인수를 받는다. 가변 인자 함수는 일부 언어에서 자료형 안전 문제를 노출할 수 있다. 예를 들어, C의 printf는 부주의하게 사용하면 포맷 스트링 공격으로 알려진 보안 취약점의 한 종류를 유발할 수 있다. 이 공격은 가변 인자 함수에 대한 언어 지원이 자료형 안전하지 않기 때문에 가능하다. 즉, 함수가 스택에 배치된 것보다 더 많은 인수를 꺼내려고 시도하여 스택을 손상시키고 예상치 못한 동작을 유발한다. 이로 인해 CERT 코디네이션 센터는 C의 가변 인자 함수를 높은 심각도의 보안 위험으로 간주한다.isbn=978-1491904442 함수형 프로그래밍 언어에서 가변 인자 함수는 함수와 목록/시퀀스/배열을 인수로 받아 해당 목록에 제공된 인수로 함수를 호출하는 apply 함수와 상호 보완적으로 간주될 수 있다. 이로써 함수에 가변 개수의 인수를 전달한다. 함수형 언어인 하스켈에서는 가변 인자 함수가 타입 클래스 T의 값을 반환함으로써 구현될 수 있다. 만약 T의 인스턴스가 최종 반환 값 r과 함수 1=(T t) => x -> t라면, 이는 임의의 수의 추가 인수 x를 허용한다. 항 재작성 연구에서 관련 주제는 **hedges** 또는 **hedge variables**라고 불린다. 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" } **출력:**

The sum of [1 2] is 3
The sum of [1 2 3] is 6
The sum of [1 2 3 4] is 10

## 자바에서
C#과 마찬가지로, 자바의 Object 타입은 모든 것을 포괄하는 타입으로 사용할 수 있다. 자바에서 매개변수는 생략 부호 표기법을 사용하여 가변 인자로 지정할 수 있다. 이는 본질적으로 배열과 동일하지만 배열로 래핑할 필요가 없다. 예를 들어, `String... args`와 `String[] args`는 본질적으로 동일하다. 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 s : strings) { System.out.println(s); } } public static void main(String[] args) { printArgs("hello"); // short for printArgs(new String[] {"hello"}) printArgs("hello", "world"); // short for printArgs(new String[] {"hello", "world"}) } }
## 자바스크립트에서
자바스크립트는 가변 인자의 타입에 신경 쓰지 않는다. 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 function 키워드로 생성된 함수에서만 사용할 수 있지만, arguments 객체를 사용하여 가변 인자 함수를 만드는 것도 가능하다. function sum() { return Array.prototype.reduce.call(arguments, (a, b) => a + b, 0); } console.log(sum(1, 2, 3)); // 6 console.log(sum(3, 2)); // 5 console.log(sum()); // 0
## 루아에서
루아 함수는 return 키워드를 사용하여 다른 값들과 동일한 방식으로 가변 인자를 다른 함수에 전달할 수 있다. 테이블은 Lua 버전 5.2 이상에서website=www.lua.org table.unpack, 또는 Lua 5.1 이하에서website=www.lua.org unpack을 사용하여 가변 인자 함수로 전달될 수 있다. 가변 인자는 가변 인자를 값으로 사용하여 테이블을 구성함으로써 테이블로 사용될 수 있다. function sum(...) --... designates varargs local sum=0 for _,v in pairs({...}) do --creating a table with a varargs is the same as creating one with standard values sum=sum+v end return sum end values={1,2,3,4} sum(5,table.unpack(values)) --returns 15. table.unpack should go after any other arguments, otherwise not all values will be passed into the function. function add5(...) return ...+5 --this is incorrect usage of varargs, and will only return the first value provided end entries={} function process_entries() local processed={} for i,v in pairs(entries) do processed[i]=v --placeholder processing code end return table.unpack(processed) --returns all entries in a way that can be used as a vararg end print(process_entries()) --the print function takes all varargs and writes them to stdout separated by newlines
## 파스칼에서
파스칼은 ISO 표준 7185("표준 파스칼") 및 10206("확장 파스칼")에 의해 표준화되었다. 표준화된 파스칼 형식은 특정 내장 함수(pascal/pascal 및 pascal/pascal, 그리고 추가적으로 확장 파스칼에서 pascal/pascal)를 제외하고는 가변 인자 루틴을 지원하지 않는다. 그럼에도 불구하고, 파스칼 방언들은 가변 인자 루틴과 유사한 메커니즘을 구현한다. 델파이는 마지막 형식 매개변수와 연결될 수 있는 delphi 데이터 타입을 정의한다. 루틴 정의 내에서 delphi는 배열인 variant record의 delphi이다.access-date=2023-08-28 앞서 언급된 delphi 데이터 타입의 delphi 멤버는 인수의 데이터 타입을 검사하고 적절하게 처리할 수 있게 한다. 프리 파스칼 컴파일러도 델파이의 가변 인자 루틴을 지원한다.access-date=2023-08-28 그러나 이 구현은 기술적으로 단일 인수, 즉 pascal를 필요로 한다. 파스칼은 배열이 동질적이어야 한다는 제한을 부과한다. 이 요구사항은 variant record를 활용하여 우회된다. GNU 파스칼은 생략 부호(pascal)를 사용하여 실제 가변 인자 형식 매개변수 사양을 정의하지만, 2022년 현재 그러한 것을 사용하기 위한 이식 가능한 메커니즘은 정의되지 않았다.access-date=2023-08-28 GNU 파스칼과 프리파스칼 모두 외부에서 선언된 함수가 생략 부호(pascal)를 사용하여 가변 인자 형식 매개변수 사양을 사용할 수 있도록 한다.
## PHP에서
PHP는 인수가 타입이 지정되지 않은 한 가변 인자의 타입에 신경 쓰지 않는다. function sum(...$nums): int { return array_sum($nums); } echo sum(1, 2, 3); // 6 그리고 타입이 지정된 가변 인자: 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)
## 파이썬에서
파이썬은 가변 인자의 타입에 신경 쓰지 않는다. from typing import Any def foo(a: Any, b: Any, *args: tuple[Any, ...]) -> None: print(args) // args는 튜플 (변경 불가능한 시퀀스)이다. if __name__ == "__main__": foo(1, 2) // () foo(1, 2, 3) // (3,) foo(1, 2, 3, "hello") // (3, "hello") 키워드 인수는 딕셔너리에 저장될 수 있다. def bar(*args: tuple[Any, ...], **kwargs: dict[str, Any]]) -> Any: // 함수 본문
## 라쿠에서
라쿠에서 가변 인자 함수를 생성하는 매개변수의 타입은 슬러피 배열 매개변수라고 알려져 있으며, 세 가지 그룹으로 분류된다:
### 평탄화된 슬러피
이 매개변수는 단일 별표(`*`)로 선언되며, 반복 가능한 요소(즉, 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]
### 비평탄화된 슬러피
이 매개변수들은 두 개의 별표(`**`)로 선언되며, 목록 내의 반복 가능한 인수를 평탄화하지 않고 인수를 거의 있는 그대로 유지한다: 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]]
### 문맥적 슬러피
이 매개변수들은 더하기(`+`) 기호로 선언되며, 문맥에 따라 슬러피 인수를 처리하는 방법을 결정하는 "단일 인수 규칙"을 적용한다. 간단히 말해서, 단일 인수만 전달되고 그 인수가 반복 가능하면, 그 인수는 슬러피 매개변수 배열을 채우는 데 사용된다. 다른 경우에는 `+@`가 `**@`처럼 작동한다(즉, 비평탄화된 슬러피). 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], 단일 인수가 배열을 채운다 zaz(1, 2, 3, [4, 5]); # [3, [4, 5]], **@처럼 작동한다 zaz(1, 2, 3, [4, 5], [6]); # [3, [4, 5], [6]], **@처럼 작동한다
## 루비에서
루비는 가변 인자의 타입에 신경 쓰지 않는다. def foo(*args) print args end foo(1) # prints `[1]=> nil` foo(1, 2) # prints `[1, 2]=> nil`
## 러스트에서
러스트는 함수에서 가변 인수를 지원하지 않는다. 대신, 가변 인수를 지원하는 매크로를 사용한다.website=Rust By Example 이것이 println!이 함수가 아닌 매크로인 이유이다. 매크로는 형식을 지정하기 위해 가변 인수를 받기 때문이다. 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! {\ eval 1 + 2, eval 3 + 4, eval (2 * 3) + 1 } } 러스트는 c_variadic 기능 스위치를 통해 C의 가변 시스템과 상호작용할 수 있다. 다른 C 인터페이스와 마찬가지로, 이 시스템은 러스트에게 unsafe로 간주된다.website=The Rust RFC Book
## 스칼라에서
object 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 def printArgs(strings: String*): Unit = { strings.foreach(println) } def main(args: Array[String]): Unit = { printArgs("hello"); // short for printArgs(["hello"]) printArgs("hello", "world"); // short for printArgs(["hello", "world"]) } }
## 스위프트에서
스위프트는 가변 인자의 타입에 신경 쓰지만, 모든 것을 포괄하는 Any 타입을 사용할 수 있다. 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
## Tcl에서
Tcl 프로시저 또는 람다는 마지막 인수가 args일 때 가변 인자이다. 이것은 나머지 모든 인수의 목록(비어 있을 수도 있음)을 포함할 것이다. 이 패턴은 많은 다른 프로시저와 유사한 메서드에서 흔하다.website=Tcl/Tk Documentationwebsite=Tcler's Wiki proc greet {timeOfTheDay args} { puts "Looks like we have [llength $args] people" foreach name $args { puts "Hello $name, good $timeOfTheDay" } } greet "morning" "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