집합(Set)
`set` 타입은 수학적 집합의 개념을 모델링합니다. `set`의 기본 타입(basetype)은 특정 크기의 `ordinal type`(순서형 타입)만 가능합니다. 즉:
- int8 ~ int16
- uint8 / byte ~ uint16
- char
- enum
- ordinal subrange types (예: `range[-10..10]`)
위와 같은 타입만 가능합니다.
`signed integer literal`(부호 있는 정수 리터럴)로 `set`을 만들면, 기본 타입은 `0 .. DefaultSetElements-1` 범위가 됩니다. 여기서 `DefaultSetElements`는 항상 2^8입니다.
```nim
var s = { 1, 2, 3 } # 범위 : 0 ~ 255
```
`set`의 기본 타입의 최대 범위 길이는 `MaxSetElements`인데, 이는 항상 2^16입니다. 더 큰 범위를 가진 타입은 자동으로 `0 .. MaxSetElements-1` 범위로 변환(coerce)됩니다.
```nim
var s: set[uint16] # 범위 : 0 ~ 65535
```
이는 set이 고성능 비트 벡터(bit vector)로 구현되기 때문입니다. 더 큰 타입으로 set을 선언하면 에러가 발생합니다:
```nim
var s: set[int64]
# Error: set is too large; use `std/sets` for ordinal types
# with more than 2^16 elements
```
⚠️ 참고: Nim은 hash set도 제공합니다. 이 경우 `import std/sets`가 필요하며, 크기 제한은 없습니다.
집합 생성
집합은 `{}`로 생성합니다. 이는 빈 집합(empty set)이며, 어떤 구체적인 집합 타입과도 호환됩니다. 생성자(constructor)를 사용해 요소나 범위를 포함시킬 수도 있습니다:
```nim
type
CharSet = set[char]
var
x: CharSet
x = {'a'..'z', '0'..'9'}
# 'a' ~ 'z', '0' ~ '9'를 포함하는 집합 생성
```
`std/setutils` 모듈을 이용하면 iterable에서 set을 초기화할 수도 있습니다:
```nim
import std/setutils
let uniqueChars = myString.toSet
```
집합 연산
| 연산 | 의미 |
| `A + B` | 두 집합의 합집합 |
A * B |
두 집합의 교집합 |
A - B |
차집합 (A에서 B의 요소 제거) |
A == B |
두 집합이 동일한지 비교 |
A <= B |
부분집합 (A가 B의 부분집합 또는 동일) |
A < B |
진부분집합 (A가 B의 proper subset) |
e in A |
집합 A가 원소 e를 포함하는지 확인 |
e notin A |
집합 A가 원소 e를 포함하지 않는지 확인 |
contains(A, e) |
A가 e를 포함하는지 확인 |
card(A) |
집합 A의 원소 개수(카디널리티) |
incl(A, elem) |
A = A + {elem} |
excl(A, elem) |
A = A - {elem} |
비트 필드(Bit fields)
`set`은 주로 procedure의 flag(플래그) 타입 정의에 사용됩니다. 이는 상수를 정의해서 `or` 연산으로 조합하는 것보다 더 깔끔하고 타입 안전(type-safe)합니다.
예시:
```nim
type
MyFlag* {.size: sizeof(cint).} = enum
A
B
C
D
MyFlags = set[MyFlag]
proc toNum(f: MyFlags): int = cast[cint](f)
proc toFlags(v: int): MyFlags = cast[MyFlags](v)
assert toNum({}) == 0
assert toNum({A}) == 1
assert toNum({D}) == 8
assert toNum({A, C}) == 5
assert toFlags(0) == {}
assert toFlags(7) == {A, B, C}
```
여기서 set은 enum 값을 2의 거듭제곱 값으로 변환합니다.
C와 호환(interoperability)할 때는 distinct cint를 사용해야 하며, bitsize pragma도 참고할 수 있습니다.

배열(Arrays)
배열(array)은 고정 길이(fixed-length)의 단순 컨테이너입니다. 배열의 각 요소는 동일한 타입을 가집니다. 배열의 인덱스 타입은 ordinal type이면 무엇이든 가능합니다.
배열 생성
```nim
type
IntArray = array[0..5, int] # 인덱스가 0..5인 배열
var
x: IntArray
x = [1, 2, 3, 4, 5, 6]
for i in low(x) .. high(x):
echo x[i]
```
배열 접근(`x[i]`)은 항상 범위 검사(bound check)가 실행됩니다(컴파일 시점 또는 런타임). 필요하다면 pragma를 사용하거나 커맨드라인 스위치 `--bound_checks:off` 옵션으로 끌 수 있습니다.
배열은 값 타입(value type)이므로, 할당 시 전체 배열의 내용이 복사됩니다.
내장 함수
- `len(a)` : 배열의 길이 반환
- `low(a)` : 배열의 최솟값 인덱스 반환
- `high(a)` : 배열의 최댓값 인덱스 반환
Enum 기반 Array 예시
```nim
type
Direction = enum
north, east, south, west
BlinkLights = enum
off, on, slowBlink, mediumBlink, fastBlink
LevelSetting = array[north..west, BlinkLights]
var
level: LevelSetting
level[north] = on
level[south] = slowBlink
level[east] = fastBlink
echo level # --> [on, fastBlink, slowBlink, off]
echo low(level) # --> north
echo len(level) # --> 4
echo high(level) # --> west
```
다차원 Array
다른 언어에서 중첩 배열(nested array, 다차원 배열)의 문법은 보통 대괄호를 계속 이어 붙이는 방식입니다. 왜냐하면 대부분의 언어에서는 각 차원이 동일한 index 타입을 가져야 하기 때문입니다.
그러나 Nim에서는 각 차원이 서로 다른 index 타입을 가질 수 있습니다. 따라서 중첩 배열 문법이 약간 다릅니다.
앞선 예제에서 level을 또 다른 enum으로 인덱싱되는 enum 배열(array)로 정의한 것을 기반으로, 정수 index를 통해 접근 가능한 `level` 단위로 나뉜 light tower 타입을 추가하는 예제를 다음과 같이 작성할 수 있습니다.
```nim
type
LightTower = array[1..10, LevelSetting]
var
tower: LightTower
tower[1][north] = slowBlink
tower[1][east] = mediumBlink
echo len(tower) # --> 10
echo len(tower[1]) # --> 4
echo tower # --> [[slowBlink, mediumBlink, ...]]
# 아래는 type mismatch 에러로 인해 컴파일되지 않습니다.
#tower[north][east] = on
#tower[0][1] = on
```
내장 프로시저 `len`은 배열의 첫 번째 차원의 길이만 반환한다는 점에 주목하세요.
LightTower의 중첩된 성격(nested nature)을 더 잘 보여주는 또 다른 정의 방법은, 이전에 정의한 LevelSetting 타입을 생략하고 대신 그것을 첫 번째 차원의 타입 안에 직접 포함(embedded)하는 것입니다:
```nim
type
LightTower = array[1..10, array[north..west, BlinkLights]]
```
배열 선언 단축 문법
0부터 시작하는 배열이 흔하기 때문에, 다음과 같이 단축 문법을 제공합니다: `array[6, int]`
```nim
type
IntArray = array[0..5, int] # 인덱스 0..5
QuickArray = array[6, int] # 인덱스 0..5와 동일
var
x: IntArray
y: QuickArray
x = [1, 2, 3, 4, 5, 6]
y = x
for i in low(x) .. high(x):
echo x[i], y[i]
```
본 설명은 Nim 공식 문서(https://nim-lang.org/docs/tut1.html, Creative Commons Attribution 3.0) 및 코드 예제(MIT License)를 바탕으로 하였습니다.
'Programming Languages > Nim' 카테고리의 다른 글
| [Nim 언어 강좌] slice, object (1) | 2025.10.08 |
|---|---|
| [Nim 언어 강좌] Sequences, Open arrays, Varargs (0) | 2025.10.07 |
| [Nim 언어 강좌] Enum, ordinal types, subrange (0) | 2025.10.05 |
| [Nim 언어 강좌] 내부 타입 표현 (0) | 2025.10.04 |
| [Nim 언어 강좌] 기본 타입과 형 변환 (3) | 2025.10.03 |