튜플
고친 사람 github-actions[bot]
튜플은 뜻이 서로 다른 값 몇 개를 한 덩어리로 묶어 한 번에 건네는 자료구조입니다. 몇 번째 칸에 무슨 뜻의 값이 오는지를 미리 약속해 둡니다. 칸 수는 만들 때 정해집니다. 나중에 늘리거나 줄이지 않습니다. 관계형 데이터베이스는 표의 한 행을 튜플이라고 부릅니다.
쉽고 빠른 이해
값 몇 개를 한 덩어리로 묶어 한 번에 건네는 그릇입니다. 나눗셈이 몫과 나머지를 같이 돌려줄 때 둘을 묶어 한 번에 돌려줍니다.
묶을 값의 뜻이 서로 다르면 같은 종류만 담는 그릇으로는 담기 어렵습니다. 값마다 이름을 붙인 새 타입을 만들자니 한 번 쓰고 버릴 묶음에까지 이름이 생깁니다. 튜플은 그 사이를 메웁니다.
도는 모양은 셋입니다.
- 만들 때 값의 개수와 순서를 정합니다.
- 몇 번째 칸에 무슨 뜻의 값이 오는지를 넘기는 쪽과 받는 쪽이 같이 약속합니다.
- 꺼낼 때는 번호로 꺼내거나 여러 변수에 한꺼번에 풀어 담습니다.
대가는 칸에 이름이 없다는 점입니다. 둘째 값이 무엇이었는지를 코드만 봐서는 모릅니다. 칸이 늘어나면 대개 이름을 붙인 그릇으로 옮깁니다.
상세
이 절은 튜플이 값을 담는 모양과, 칸에 이름을 안 붙인 대신 무엇을 얻고 무엇을 잃는지를 다룹니다.
칸마다 뜻이 정해진 값 묶음
튜플은 값 몇 개를 정해진 순서로 이어 담습니다. 담긴 값 하나하나를 원소라고 부릅니다. 원소가 놓이는 칸마다 무슨 뜻의 값이 오는지가 미리 정해집니다.
아래는 사용자 한 명을 튜플로 담은 모습입니다. 첫 칸은 사용자 번호, 둘째 칸은 이름, 셋째 칸은 가입한 날입니다.
block-beta columns 3 a["첫 칸 · 사용자 번호"] b["둘째 칸 · 이름"] c["셋째 칸 · 가입한 날"]
칸 수를 그 튜플의 차수라고 부릅니다. 위 튜플은 차수가 셋입니다. 차수가 셋인 튜플을 3-튜플이라고 적기도 합니다.
배열도 값을 순서대로 담습니다. 다만 배열은 같은 종류의 값을 원하는 만큼 담습니다. 튜플은 종류가 서로 달라도 되는 대신 칸 수가 만들 때 굳습니다.
이름 없는 레코드
레코드는 값마다 이름을 붙여 담는 자료구조입니다. 그 이름 붙은 칸 하나하나를
필드라고 부릅니다. 필드가 있으면 user.name 처럼 이름으로 값을 꺼냅니다.
튜플은 그 이름을 안 붙이고 번호만 씁니다. 값 두 개를 돌려주자고 타입을 하나 새로 짓는 것은 품이 큽니다. 튜플은 그 품을 없앱니다.
대신 읽는 쪽이 몇 번째가 무슨 뜻인지를 따로 알고 있어야 합니다. 그 약속은 코드에 안 적힙니다. 튜플이 멀리 돌아다닐수록 지키기 어려워집니다.
둘의 차이를 모으면 이렇습니다.
| 튜플 | 레코드 | |
|---|---|---|
| 값을 꺼내는 법 | 번호 | 이름 |
| 칸마다 종류 | 달라도 됩니다 | 달라도 됩니다 |
| 칸 수 | 만들 때 굳습니다 | 타입을 만들 때 굳습니다 |
| 미리 이름을 지어야 하나 | 아니요 | 예 |
한 번 쓰고 버릴 묶음이면 튜플이 편합니다. 여러 곳에서 오래 돌아다닐 묶음이면 이름을 붙이는 편이 읽기 쉽습니다.
관계형 데이터베이스가 부르는 튜플
관계형 데이터베이스는 데이터를 표로 다룹니다. 그 표의 한 행이 튜플입니다. 표의 열 하나가 튜플의 칸 하나입니다. 격자 위에서 가로 한 줄이 튜플 하나입니다.
block-beta columns 4 t0["표"] h1["열 · 사용자 번호"] h2["열 · 이름"] h3["열 · 도시"] t1["행 · 튜플 하나"] a1["7"] a2["나래"] a3["서울"] t2["행 · 튜플 하나"] b1["8"] b2["도윤"] b3["부산"]
어떤 값이 그 칸에 올 수 있는지를 미리 정해 둔 값의 모임을 도메인이라고 부릅니다. 도시 이름이 오는 칸의 도메인은 도시 이름 전부입니다. 도메인마다 값을 하나씩 골라 순서대로 이으면 튜플 하나가 만들어집니다.
같은 모양의 튜플을 모은 것이 릴레이션입니다. 릴레이션은 테이블을 수학 쪽에서 부르는 이름입니다.
flowchart TD
subgraph 도메인
D1["사용자 번호"]
D2["이름"]
D3["도시"]
end
D1 --> T["튜플 · 7 · 나래 · 서울"]
D2 --> T
D3 --> T
T --> R["릴레이션 · 같은 모양의 튜플 모임"]
그림에서 도메인 셋이 값을 하나씩 내어 튜플 하나를 이룹니다. 그런 튜플이 모이면 릴레이션이 됩니다. 릴레이션에 담긴 튜플은 전부 차수가 같습니다. 그래서 릴레이션 자체의 차수도 셋입니다.
코드로 보는 튜플
아래는 파이썬으로 위 사용자 튜플을 담은 모습입니다. 줄마다 나오는 값을 오른쪽
주석에 적었습니다. 파이썬은 칸 번호를 0부터 셉니다. 첫 칸이 user[0] 입니다.
user = (7, "나래", "서울")
user[0] # 7
len(user) # 3
num, name, city = user
city # "서울"
user[0] = 8 # 바꿀 수 없어 오류
넷째 줄은 튜플의 값을 변수 셋에 한꺼번에 풀어 담습니다. 이렇게 묶음을 헐어 변수에 나눠 담는 것을 구조 분해 할당이라고 부릅니다. 변수를 두 개만 두면 개수가 안 맞아 오류가 납니다.
마지막 줄은 첫 칸의 값을 바꾸려 한 경우입니다. 파이썬 튜플은 한 번 만들면 원소를 못 바꿉니다. 바꾼 값이 필요하면 새 튜플을 만듭니다.
값을 바꾸지 않는다는 약속
앞 절의 파이썬 튜플처럼, 여러 언어가 튜플을 한 번 만들면 못 바꾸게 막습니다. 만든 뒤에 내용이 안 바뀌는 이 성질을 불변성이라고 부릅니다.
해시는 값을 정해진 길이의 수 하나로 줄인 것입니다. 내용이 안 바뀌면 그 내용에서 계산해 낸 해시도 안 바뀝니다.
해시테이블은 키 하나로 값을 바로 찾아가는 그릇입니다. 해시가 고정이라 튜플은 그 키로 쓸 수 있습니다. 위도와 경도를 묶은 튜플을 키로 두고 그 지점의 날씨를 값으로 두는 식입니다.
단 안에 담긴 값 자체가 바뀔 수 있는 물건이면 이 약속이 반쪽이 됩니다. 튜플 안에 리스트를 담으면 튜플 자체는 그대로여도 그 리스트의 내용은 바뀝니다. 튜플은 그 값을 가리키기만 할 뿐, 가리킨 값이 바뀌는 것까지 막지는 못합니다.
flowchart TD
subgraph 튜플
C1["첫 칸 · 7"]
C2["둘째 칸 · 나래"]
C3["셋째 칸"]
end
C3 --> M["가리킨 값 · 바뀔 수 있는 물건"]
첫 칸과 둘째 칸은 값을 칸 안에 담고 있습니다. 셋째 칸만 바깥을 가리킵니다. 그 바깥 값이 바뀌면 튜플이 내주는 값도 같이 달라집니다.
연산마다 걸리는 시간
값의 개수 n 이 늘 때 걸리는 시간이 어떻게 늘어나는지는 빅오 표기법으로 적습니다. O(1) 은 개수와 상관없이 시간이 일정하다는 뜻입니다. O(n) 은 개수에 비례해 늘어난다는 뜻입니다.
| 연산 | 시간 | 까닭 |
|---|---|---|
| 번호로 읽기 | O(1) | 칸이 순서대로 붙어 있어 위치를 계산으로 구합니다 |
| 칸 수 세기 | O(1) | 만들 때 정해져 있어 그 수를 읽기만 합니다 |
| 값으로 찾기 | O(n) | 앞에서부터 하나씩 견줍니다 |
| 값 하나만 바꾼 튜플 만들기 | O(n) | 나머지 값을 전부 새 튜플로 옮겨 담습니다 |
마지막 줄은 튜플을 못 바꾸는 언어에서 값을 고칠 때 벌어지는 일입니다. 고친 것처럼 보여도 새 묶음을 하나 더 만든 것입니다.
원래 튜플입니다.
block-beta columns 3 a["첫 칸"] b["둘째 칸"] c["셋째 칸"]
첫 칸만 고쳐 새로 만든 튜플입니다. 나머지 두 칸은 옮겨 담은 것입니다.
block-beta columns 3 d["첫 칸 · 새로 넣음"] e["둘째 칸 · 옮겨 담음"] f["셋째 칸 · 옮겨 담음"]
튜플을 고르는 때와 피하는 때
튜플이 잘 맞는 경우는 셋입니다.
| 이런 경우 | 까닭 |
|---|---|
| 값 두세 개를 한 번에 돌려준다 | 돌려주자고 새 타입을 짓지 않아도 됩니다 |
| 묶음이 한 함수 안에서만 돈다 | 몇 번째가 무슨 뜻인지 헷갈릴 범위가 좁습니다 |
| 묶음 자체를 키로 쓴다 | 안 바뀌는 묶음이라 해시 값이 고정입니다 |
반대로 묶음이 여러 모듈을 돌아다니면 번호로 꺼내는 코드가 사방에 흩어집니다. 칸 하나를 더하는 순간 그 코드를 전부 찾아 고쳐야 합니다. 그럴 때는 이름을 붙인 레코드로 옮깁니다.
칸에 담을 값의 개수가 실행 중에 늘거나 줄어야 할 때도 튜플은 맞지 않습니다. 튜플은 칸 수가 뜻의 일부라 개수가 바뀌면 다른 모양이 됩니다. 그런 데이터는 리스트처럼 개수가 자유로운 그릇에 담습니다.
셋 중 무엇을 고를지는 이렇게 갈립니다.
flowchart TD
Q1{"값의 개수가 실행 중에 바뀌나"}
Q1 -->|예| L["리스트"]
Q1 -->|아니오| Q2{"묶음이 여러 모듈을 돌아다니나"}
Q2 -->|예| R["레코드"]
Q2 -->|아니오| T["튜플"]
관련 항목
튜플과 나란히 쓰이는 값 묶음 자료구조
배열 · 레코드 · 구조체 · 리스트 · 집합 · 해시테이블 · 시퀀스 · 컬렉션
관계형 모델에서 튜플과 함께 쓰는 용어
릴레이션 · 테이블 · 행 · 열 · 셀 · 필드 · 도메인 · 차수 · 기본 키 · 결과 집합
튜플을 다루는 언어 기능
구조 분해 할당 · 이름 붙은 튜플 · 다중 반환값 · 패턴 매칭 · 제네릭
튜플이 지키는 성질
불변성 · 해시 · 값 동등성 · 고정 길이
튜플이 속하는 상위 분류
다른 이름: tuple · 투플 · n-튜플