데이터와 제어

컬렉션과 컴프리헨션

Array, Map, Set을 목적에 맞게 고르고 정본 컴프리헨션으로 변환하는 방법.

컬렉션은 프로그램이 보존해야 할 동작에 맞춰 고릅니다. 위치와 중복이 중요한지, 키로 값을 찾아야 하는지, 유일성만 필요한지부터 판단하세요. 컴프리헨션은 각 컬렉션의 순서 규칙을 유지하면서 필터링과 변환을 간결하게 표현합니다.

먼저 컬렉션 고르기

컬렉션알맞은 상황눈에 보이는 동작
Array<T>순서, 위치 접근, 중복 값이 필요할 때인덱스가 증가하는 순서로 순회하고 중복을 유지
Map<K, V>키로 값을 찾을 때키는 삽입 순서를 유지하고 기존 키의 값만 바꾸면 자리 유지
Set<T>값의 유일성이 중요할 때같은 값 가운데 처음 삽입한 값의 자리 유지

예제 실행하기

다음 코드를 collections-comprehensions.tpz로 저장합니다.

TOPAZ
let scores = [10, 25, 10]
let labels = map { "Ada": 10, "Lin": 25 }
let unique = set { 10, 25, 10 }
let focused = [ for score in scores if score >= 20 => score * 2 ]

let first = scores[0]
let missing = scores.get(9)
let hasAda = "Ada" in labels.keys
let missingLabel = match missing {
    case Some(value) => "found {value}"
    case None => "missing"
}

print("{scores}:{unique.toArray()}:{labels.keys}")
print("{focused}:{first}:{hasAda}")
print(missingLabel)
BASH
topaz check collections-comprehensions.tpz
topaz run collections-comprehensions.tpz

출력은 다음과 같습니다.

출력
[10, 25, 10]:[10, 25]:[Ada, Lin]
[50]:10:true
missing

배열은 두 10을 모두 유지합니다. 집합은 두 번째 10을 합치고 첫 번째 값의 삽입 순서를 남깁니다. labels.keys는 키 삽입 순서의 배열이므로 멤버십 검사는 "Ada" in labels.keys로 씁니다.

조회와 변경을 의도에 맞게 고르기

scores[0]은 인덱스로 직접 읽습니다. 음수이거나 범위를 벗어난 인덱스는 실행 오류로 멈춥니다. scores.get(9)는 멈추지 않는 조회이며 Option을 반환하므로, 예제에서는 match로 결과를 처리합니다.

컬렉션을 제자리에서 바꾸려면 뿌리 바인딩이 변경 가능해야 합니다. values.push(...) 같은 변경 연산을 부르기 전에 let mut values = [...]로 시작하세요. 변경할 수 없는 바인딩에서도 새로운 컬렉션을 만드는 식은 사용할 수 있습니다.

맵의 중복 처리는 집합과 다릅니다. 같은 단순 리터럴 키를 반복하면 정적 오류입니다. 실행 중에야 두 키가 같아지는 경우에는 피연산자를 계산한 뒤 덮어쓰지 않고 실행 오류가 납니다. 키를 추가하거나 기존 값을 바꾸려는 의도라면 Map.insert를 명시적으로 사용합니다.

리터럴, 반복문, 컴프리헨션

  • 항목을 직접 적을 때는 [a, b], set { a, b }, map { key: value }를 사용합니다.
  • 본문을 여러 단계로 읽는 편이 쉬울 때는 for item in source { ... }를 사용합니다. 문장 위치에서는 효과만 내고, 식 위치에서는 본문 결과를 Array로 모읍니다.
  • 필터링과 변환이 하나의 분명한 식일 때는 [ for item in source if condition => result ]를 사용합니다. 유일한 결과가 필요하면 바깥을 set { ... }, 키와 값이 필요하면 map { ... }로 바꿉니다.

컴프리헨션 절은 for로 시작하고, 쉼표 없이 forif를 이어 쓴 뒤 =>로 결과를 적습니다. 각 절은 왼쪽에서 오른쪽으로 중첩됩니다. 도달한 모든 절과 본문이 성공한 뒤에만 최종 컬렉션이 만들어집니다.

흔한 실수

컬렉션 타입은 Array<T>, Map<K, V>, Set<T>입니다. [T]는 컬렉션 타입이 아닙니다. 집합과 맵 리터럴에는 setmap이 꼭 필요합니다. #{...}나 맵을 뜻하는 맨 앞 단어가 없는 {key: value}는 해당 문법이 아닙니다.

key in map은 허용되지 않습니다. key in map.keys를 사용하세요. 컴프리헨션에서 실패 가능한 패턴이 맞지 않으면 자동으로 거르는 것이 아니라 실행 오류가 납니다. 항목을 건너뛰려면 if 절을 명시합니다.

정확한 경계

  • 배열 스프레드는 다른 Array만 받습니다. 집합과 맵 리터럴·컴프리헨션에는 스프레드가 없습니다.
  • set {}map {}에는 기대되는 Set<T>Map<K, V> 타입이 필요합니다.
  • 맵 키와 집합 원소는 재귀적으로 키로 사용할 수 있는 타입이어야 합니다.
  • 항목을 삭제하면 순서 자리도 사라지고, 다시 넣으면 끝에 새 자리가 생깁니다. 기존 맵 값만 바꾸면 키 순서는 유지됩니다.
  • forArray, 정수 범위, Set, map.keys를 순회할 수 있습니다. 문자열은 스칼라를 얻는 명시적 연산이 필요합니다.

사용자 정의 반복자, 생성기, 지연 또는 병렬 컴프리헨션, 임의 반복 값의 스프레드, 순서가 없는 컬렉션 의미는 현재 언어에 포함되지 않습니다.

학습 과정의 데이터와 제어에서 먼저 선택 기준을 익힐 수 있습니다. 컴프리헨션 바인딩은 패턴과 제어 흐름, 정확한 일상 연산은 컬렉션 연산을 참고하세요.