Set — 순서 없는 고유 요소의 집합

Set — 순서 없는 고유 요소의 집합 (Immutable collection of distinct elements)

중복이 없고 순서도 중요하지 않은 값들을 담아야 할 때 뭘 쓸까요? Raku에서는 Set이 바로 그 역할을 해요. 요소의 포함 여부를 빠르게 확인하거나 집합 연산을 할 때 유용한 불변 집합 타입이에요.

출처: Raku Documentation — Set

본문

Set은 불변 집합(immutable set)이에요. 다시 말해, 순서 없이 구분되는 요소들의 모음이죠. (변경 가능한 집합이 필요하다면 SetHash를 쓰면 돼요.)

class Set does Setty { }

집합 요소로는 어떤 타입의 객체/값이든 허용돼요. 그리고 Set 안에서 각 요소는 고유함이 보장돼요. 즉 어떤 두 요소도 === 연산자로 같게 비교되지 않는다는 뜻이에요.

my $fruits = set <peach apple orange apple apple>;

say $fruits.elems;      # OUTPUT: «3␤»
say $fruits.keys.sort;  # OUTPUT: «apple orange peach␤»

apple이 네 번 들어갔는데도 elems3, 키는 apple orange peach 세 개뿐이에요. 중복이 자동으로 제거된 거예요.

Set{ } 후치 연산자(postcircumfix)로 객체 해시처럼 다룰 수 있어요. 집합의 요소인 키에는 True, 아닌 키에는 False를 돌려줘요.

my $fruits = set <peach apple orange apple apple>;
say $fruits<apple>;  # OUTPUT: «True␤»
say $fruits<kiwi>;   # OUTPUT: «False␤»

Set 객체 만들기

Setset 서브루틴(또는 그 축약형인 Set.new)으로 만들 수 있어요. 타입과 무관하게 위치 매개변수는 모두 집합의 요소가 돼요.

my $n = set "zero" => 0, "one" => 1, "two" => 2;
say $n.keys.raku;        # OUTPUT: «(:two(2), :zero(0), :one(1)).Seq␤»
say $n.keys.map(&WHAT);  # OUTPUT: «((Pair) (Pair) (Pair))␤»

혹은 기존 객체에 .Set 형변환자(함수형으로는 Set())를 호출해 Set으로 바꿀 수도 있어요. 그 의미는 객체의 타입과 내용에 따라 달라져요. 대체로 객체를 리스트 문맥에서 평가해 그 결과 항목들을 요소로 한 집합을 만들지만, Hash류 객체나 Pair 항목에서는 키만 요소가 되고, False로 boolify되는 값에 매핑된 키는 건너뛰어요.

my $n = ("zero" => 0, "one" => 1, "two" => 2).Set;
say $n.keys.raku;        # OUTPUT: «("one", "two").Seq␤»
say $n.keys.map(&WHAT);  # OUTPUT: «((Str) (Str))␤»

값이 0인 "zero" => 0은 boolify되면 False라서 키에서 빠졌어요. 그 결과 "one""two"만 남았죠.

또한 List 같은 다른 타입의 객체에 집합 연산자를 쓰면, 연산 전에 내부적으로 .Set을 호출하는 것처럼 동작하면서 Set을 얻을 수 있어요. 다만 이런 연산자의 우선순위가 빡빡하다는 점을 주의해야 해요. 인자 주변에 괄호가 필요할 수 있어요.

say (1..5) (^) 4;  # OUTPUT: «Set(1 2 3 5)␤»

.new 메서드로도 Set을 만들 수 있어요.

my $fruits = Set.new( <peach apple orange apple apple> );

6.d(2019.03 이후)부터는 Set을 파라미터화해 어떤 타입의 값을 허용할지 지정할 수도 있어요.

# only allow strings (Str) in the Set
my $fruits = Set[Str].new( <peach apple orange apple apple> );

# only allow whole numbers (Int) in the Set
my $fruits = Set[Int].new( <peach apple orange apple apple> );
# Type check failed in binding; expected Int but got Str ("peach")

Set[Int]에 문자열을 넣으니 타입 검사가 실패해요. 이렇게 타입 제약을 걸면 의도하지 않은 요소가 섞이는 걸 막을 수 있어요.

마지막으로, is 트레이트를 쓰면 Set을 해시처럼 위장시킬 수 있어요. 실제로는 해당 시길을 써서 Associative 변수를 선언하는 거예요.

my %s is Set = <a b c>;
say %s<a>;  # OUTPUT: «True␤»
say %s<d>;  # OUTPUT: «False␤»

6.d부터는 이 문법에서도 허용할 값의 타입을 지정할 수 있어요.

# limit to strings
my %s is Set[Str] = <a b c>;
say %s<a>;  # OUTPUT: «True␤»
say %s<d>;  # OUTPUT: «False␤»

# limit to whole numbers
my %s is Set[Int] = <a b c>;
# Type check failed in binding; expected Int but got Str ("a")

연산자

Set에 적용할 수 있는 "집합 연산자"의 전체 목록은 set semantics 관련 문서에서 확인할 수 있어요. 그중 몇 가지를 예시로 볼게요.

my ($a, $b) = set(1, 2, 3), set(2, 4);

say $a (<) $b;  # OUTPUT: «False␤»
say $a (&) $b;  # OUTPUT: «Set(2)␤»
say $a (^) $b;  # OUTPUT: «Set(1 3 4)␤»

# Unicode versions:
say $a ⊂ $b;  # OUTPUT: «False␤»
say $a ∩ $b;  # OUTPUT: «Set(2)␤»
say $a ⊖ $b;  # OUTPUT: «Set(1 3 4)␤»

<는 부분집합, &는 교집합, ^(또는 유니코드 )는 대칭차집합이에요. 보기 좋게 유니코드 기호로도 같은 연산을 표현할 수 있죠.

서브루틴

sub set

sub set(*@args --> Set)

주어진 @argsSet을 만들어요. 앞에서 본 set <peach ...> 호출이 바로 이 서브루틴을 쓰는 거예요.

더 알아보기 (Learn more)