setbagmix — 집합·가방·믹스 컬렉션 타입
setbagmix — 집합·가방·믹스 컬렉션 타입
객체들을 모아 두되 순서는 신경 쓰고 싶지 않을 때, Raku가 제공하는 여섯 개의 비순서 컬렉션 타입이 있어요. Set, SetHash, Bag, BagHash, Mix, MixHash가 바로 그것인데, 모두 비슷한 의미 구조를 공유해요. 이 페이지에서는 이 여섯 타입이 무엇이 다른지, 그리고 어떤 일을 할 때 어떤 타입을 골라야 하는지 살펴볼게요.
서론
여섯 컬렉션 클래스는 Set, SetHash, Bag, BagHash, Mix, MixHash예요. 모두 비슷한 의미를 갖죠.
간단히 말해 이 클래스들은 객체 해시처럼, 일반적으로 순서 없는 객체 컬렉션을 담아요. 이 클래스들이 공통으로 구현하는 역할이 QuantHash인데, 그래서 이들을 묶어 QuantHash라고 부르기도 해요.
역할(role) 기준으로 다시 나누면 이렇게 돼요.
Set과SetHash는Setty역할을 구현해요.Bag과BagHash는Baggy역할을 구현해요.Mix와MixHash는Mixy역할을 구현하는데,Mixy는 스스로Baggy역할을 구현하고 있어요.
Set 계열은 컬렉션 안에 객체가 있는지 없는지만 따져요. Bag 계열은 같은 종류의 객체를 여러 개 담을 수 있고요. Mix 계열은 여기에 더해 분수·음수 가중치까지 허용해요. 그리고 접미사가 붙지 않은 (Set, Bag, Mix) 쪽은 불변, -Hash가 붙은 (SetHash, BagHash, MixHash) 쪽은 가변 타입이에요.
조금 더 풀어 볼게요. 객체를 담되 순서는 중요하지 않다면 이 비순서 컬렉션 타입을 쓰면 돼요. 순서가 없기 때문에 요소를 찾거나 반복된 항목을 다룰 때 List나 Array보다 효율적일 수 있어요.
중복을 없애고 요소가 있는지 없는지만 알고 싶다면 Set이나 SetHash를 쓰면 되고, 중복은 없애되 순서는 유지하고 싶다면 List의 unique 루틴을 보면 돼요.
각 객체가 몇 번이나 나왔는지 추적하고 싶다면 Bag이나 BagHash를 써요. 이 Baggy 컨테이너에서는 각 요소가 가중치(부호 없는 정수)를 갖는데, 그 값이 같은 객체가 컬렉션에 몇 번 포함됐는지를 나타내요.
Mix와 MixHash는 Bag/BagHash와 비슷하지만, 여기에 더해 분수·음수 가중치를 허용해요.
Set, Bag, Mix는 불변 타입이라, 컨테이너를 만든 뒤에 요소를 더하거나 빼야 한다면 가변 버전인 SetHash, BagHash, MixHash를 써야 해요.
이 타입들을 이해할 때 두 가지를 기억하면 좋아요.
첫째, 동일한 객체는 같은 요소를 가리켜요. 여기서 동일성(identity)은 WHICH 메서드 기준으로 판정되는데, === 연산자가 identity를 확인하는 바로 그 방식이에요. 값 타입(Str 같은)이라면 값이 같으면 되고, 참조 타입(Array 같은)이라면 같은 객체 인스턴스를 가리킬 때만 같다고 봐요.
둘째, 해시처럼 생긴 인터페이스를 제공해요. 즉 컬렉션의 실제 요소(어떤 타입이든 될 수 있어요)가 '키'가 되고, 거기에 딸린 가중치가 '값'이 돼요:
$a의 타입 |
$b가 요소일 때 $a{$b}의 값 |
$b가 요소가 아닐 때 $a{$b}의 값 |
|---|---|---|
| Set / SetHash | True | False |
| Bag / BagHash | 양의 정수 | 0 |
| Mix / MixHash | 0이 아닌 실수 | 0 |
집합 연산자 (set operators)
QuantHash 의미를 이용한 흔한 연산을 수행하는 중위 연산자들이 여럿 있어요. 이름이 길어서 보통 "집합 연산자"라고 부르죠.
여기서 중요한 점은 이 연산자의 파라미터가 반드시 Set이거나 더 일반적인 QuantHash일 필요는 없다는 거예요. 그냥 이 연산자에 적용되는 논리가 집합론(set theory)의 논리일 뿐이에요.
이 중위 연산자들은 함수를 나타내는 유니코드 문자(∈, ∪ 같은)로 쓰거나, 동등한 ASCII 버전((elem), (|) 같은)으로 쓸 수 있어요. ASCII 버전 앞에 !를 붙이면 부정(!(elem), !(|))이 돼요.
그래서 이 연산자들을 쓸 때 굳이 Set(또는 Bag, Mix) 객체를 명시적으로 만들 필요는 없어요. 모든 집합 연산자는 모든 가능한 인자에서 동작하고, 필요하면 내부에서 강제 변환이 일어나지만 실제로 필요하지 않은 경우도 많아요.
다만 Bag이나 Mix가 파라미터 중 하나로 들어오면 의미가 그 타입으로 승격돼요 (두 타입이 함께 쓰이면 Mix가 Bag을 덮어써요).
Bool을 돌려주는 집합 연산자
infix (elem), infix ∈ — $a가 $b의 요소이면 True, 아니면 False. (자세한 정의는 Wikipedia 참조)
infix !(elem), infix ∉ — $a가 $b의 요소가 아니면 True, 아니면 False.
infix (cont), infix ∋ — $a가 $b를 요소로 포함하면 True, 아니면 False.
infix !(cont), infix ∌ — $a가 $b를 요소로 포함하지 않으면 True, 아니면 False.
infix (<=), infix ⊆ — $a가 $b의 부분집합이거나 같으면 True, 아니면 False.
infix !(<=), infix ⊈ — $a가 $b의 부분집합도, 같지도 않으면 True, 아니면 False.
infix (<), infix ⊂ — $a가 $b의 진부분집합(strict subset)이면 True, 아니면 False.
infix !(<), infix ⊄ — $a가 $b의 진부분집합이 아니면 True, 아니면 False.
infix (>=), infix ⊇ — $a가 $b의 상위집합이거나 같으면 True, 아니면 False.
infix !(>=), infix ⊉ — $a가 $b의 상위집합도, 같지도 않으면 True, 아니면 False.
infix (>), infix ⊃ — $a가 $b의 진상위집합(strict superset)이면 True, 아니면 False.
infix !(>), infix ⊅ — $a가 $b의 진상위집합이 아니면 True, 아니면 False.
infix (==), infix ≡ — $a와 $b가 동일(identical)하면 True, 아니면 False. 2020.07 Rakudo 컴파일러 릴리스부터 사용 가능하며, 이전 버전 사용자는 Set::Equality 모듈을 설치하면 같은 기능을 쓸 수 있어요.
infix !(==), infix ≢ — $a와 $b가 동일하지 않으면 True, 아니면 False. (사용 가능 시점은 위와 동일)
QuantHash를 돌려주는 집합 연산자
infix (|), infix ∪ — 모든 인자의 합집합(union)을 돌려줘요.
infix (&), infix ∩ — 모든 인자의 교집합(intersection)을 돌려줘요.
infix (-), infix ∖ — 모든 인자의 차집합(set difference)을 돌려줘요.
infix (^), infix ⊖ — 모든 인자의 대칭 차집합(symmetric set difference)을 돌려줘요.
Baggy를 돌려주는 집합 연산자
infix (.), infix ⊍ — 인자의 Baggy 곱셈(multiplication)을 돌려줘요.
infix (+), infix ⊎ — 인자의 Baggy 덧셈(addition)을 돌려줘요.
집합 연산자 관련 term
term ∅ — 공집합(the empty set).