튜플(Tuple)은 스위프트에서 제공하는 특별한 성격의 집단 자료형으로, 파이썬에서도 사용되는 자료형이다. 튜플은 한가지 타임의 아이템만 저장할 수 있는 배열과 딕셔너리와는 달리 하나의 튜플에 여러 가지 타입의 아이템을 저장할 수 있지만, 선언 되고 나면 상수적 성격을 띄므로 값의 추가나 삭제, 변경이 불가능하다.
튜플은 대괄호를 사용하는 배열이나 집합과는 달리 소괄호를 이용하여 정의한다.
let tupleValue = ("a", "b", 1, 2.5, true)
이렇게 구성된 튜플의 각 아이템은 인덱스 속성을 이용해 참조가 가능하다. 하지만 배열의 인덱스 참조 표기 방식에 차이가 있다. 배열에서는 대괄호를 사용하지만 튜플에서는 점(dot)을 사용한다.
tupleValue.0 // "a"
tupleValue.1 // "b"
tupleValue.2 // 1
tupleValue.3 // 2.5
tupleValue.4 // true
튜플 역시 없는 인덱스를 참조하면 오류가 발생하며, 인덱스가 0부터 시작한다는 점은 배열의 인덱스와 같다.
튜플은 별도 선언 구문은 없지만 타입 어노테이션을 사용하기위한 타입을 정의할 수 있다. 정의되는 타입 어노테이션의 내용은 튜플의 아이템에 따라 달라진다.
var tpl1: (Int, Int) = (100, 200)
var tpl2: (Int, String, Int) = (100, "a", 200)
var tpl3: (Int, (String, String)) = (100, ("t", "v"))
var tpl4: (String) = ("sample string")
이처럼 타입 어노테이션을 사용하여 튜플을 선언할 때는 들어갈 아이템의 갯수와 순서에 맞게 각각의 타입을 선언해야 한다. 마지막 tpl4를 주의해서 보아야 한다. 문자열 하나만 저장하는 튜플로 선언했지만 실제 저장된 결과를 확인해보면 튜플이 아닌 문자열 변수로 선언된다. 여기서 알 수 있는 점은 하나의 아이템만 있는 튜플은 일반 자료형이 된다는 사실이다.
앞서 정의했던 tupleValue에 타입 어노테이션을 적용하면 아래와 같다.
let tupleValue: (String, Character, Int, Float, Bool) = ("a", "b", 1, 2.5, true)
만약 tupleValue에 타입 어노테이션을 적용해 주지 않는다면 두 번째 인덱스의 값인 "b"는 리터럴 그 자체로 String일 수도, Character일 수도 있다. 하지만 별도의 어노테이션을 작성하지 않으면 타입 추론 원칙에 따라 상위 타입은 String으로 추론된다. 네 번째 인덱스 2.5도 마찬가지로 Float, Double 모두 적용 가능하므로 상위 타입인 Double이 적용된다. 이러면 튜플에서 원치 않는 타입이 만들어지므로 타입 어노테이션을 가급적이면 지정하는 것이 좋다.
튜플에서 인덱스 속성으로만 접근하면 불편할 수 있다. 스위프트에서는 튜플의 아이템을 개별 변수나 상수로 각각 할당받는 바인딩(Binding) 방식의 구문도 제공한다.
let tupleValue: (String, Character, Int, Float, Bool) = ("a", "b", 1, 2.5, true)
let (a, b, c, d, e) = tupleValue
print(a) // a
print(b) // b
print(c + 2) // 3
print(d * 2) // 5.0
print(e) // true
이처럼 바인딩을 통해 개별 아이템에 대응하는 상수나 변수로 각각 할당받을 수 있다.
튜플은 값이 정해지면 추가, 수정, 삭제가 불가능하다. 따라서 프로그램이 실행되는 동안 값이 변하지 않아야 하는 상수 성격이거나, 값이 바뀔 가능성을 제거할 때 사용하면 좋다.
튜플은 배열이나 딕셔너리와 달리 다양한 기능을 하는 메소드가 없다. 단지 인덱스 속성이 유일하다. 튜플의 크기 계산이나 for ~ in 과 같은 loop 기능도없다.
이런 특이한 자료형인 튜플이 사용되기 좋은 곳은 함수나 메소드이다. 둘 이상의 값을 반환할 때 별도의 자료형 객체를 만들거나 딕셔너리 또는 배열을 만들어야 하는데, 이때 튜플을 이용하면 편리하다.
func getTupleValue() -> (String, String, Int) {
return("t", "v", 100)
}
let (a1, b1, c1) = getTupleValue()
print(a1) // t
print(b1) // v
print(c1) // 100
함수에서 (String, String, Int) 타입의 튜플을 반환하고 있다. 하나의 함수를 사용하여 세 개의 데이터를 받을 수 있고 각각 a1, b1, c1로 할당하여 필요한 곳에 사용할 수 있다.
만약 함수를 실행하여 튜플을 반환 받을 때 , 데이터가 전부 필요 없다면 아래와 같이 사용할 수 있다.
let (a2, b2, _) = getTupleValue()
print(a2) // t
print(b2) // v
튜플 내부 세 번째 아이템을 사용하지 않는다면 "_"(언더바)를 사용할 수 있다. 만약 작성하지 않는다면 바인딩하는 튜플 변수 갯수와 아이템 갯수가 일치하지 않아 컴파일 에러가 발생한다.
정리하면 튜플은 반환할 데이터들을 단순히 괄호로 묶는 것만으로도 자료형이 만들어지므로 코드가 매우 단순해진다는 이점이 있다. 아이템 수정, 추가, 삭제 불가능의 제약이 있더라도 튜플을 사용하는 이유이다.