덧글은 게시글 제목 혹은 본문 아래 덧글 갯수 부분을 클릭하면 작성 할 수 있습니다.
본문 중의 소스 코드를 복사할 경우 더블클릭 후에 복사해야 한줄로 복사되지 않고 LineFeed가 포함됩니다.

set 타입의 내부 구조와 엘리먼트 수 알아내기


집합형(set type)은 내부적으로 bit array 라고 할 수 있고,
엘리먼트 갯수가 8개 이하이면 1바이트를 할당해 각 비트를 세팅해 표현하고
9개 이상 16개 이하면 2바이트를 할당, 17개 이상 24개 이하면 3바이트를 할당하는 식으로
32바이트까지, 즉 256개까지의 엘리먼트를 사용할 수 있는 구조이다(FPC에서는 256바이트까지 가능).

선뜻 이해가 안간다면...

TMyEnum = (me1, me2, me3, me4, me5);
    TMySet = set of TMyEnum;

의 경우 TMySet은 엘리먼트가 8개 이하(5개)이므로 SizeOf(TMySet) 하면 1이 되고,

TMySet = set of Char;

의 경우 TMySet은 엘리먼트가 256개이므로 SizeOf(TMySet) 하면 32가 된다는 말이다.
각 비트로 엘리먼트를 표현하므로 256개의 엘리먼트를 표현하기 위해서는 256bit 즉, 32Byte가 필요하기 때문이다.

전자의 경우

MySet: TMySet = [me1, me3];

와 같이 했다면 MySet은 내부적으로 이진수 00000101, 즉 십진수 5의 값을 가진다. 이해했는가?

후자의 경우

MySet: TMySet = ['A'];

와 같이 했다면 MySet은 내부적으로

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000010 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000

의 값을 가진다. 'A'의 아스키 코드가 65임을 기억한다면 66번째 비트가 1인 것을 알아차릴 것이다.

이것을 알고 있다면 TMySet 형의 변수가 담고있는 엘리먼트 수를 구하는 것은 쉽게 다음 두가지의 처리로 가능하다
1, 크기를 알아낸다.
2. set 된 비트수를 알아낸다.

크기는 SizeOf(MySet) 하면 된다. set된 비트수는


Result := 0;
    for i := 0 to SizeOf(MySet) - 1 do begin
        Temp := Ord(PChar(@MySet)[i]);
        for j := 0 to 7 do begin
            if (Temp and 1) = 1 then Inc(Result);
            Temp := Temp shr 1;
        end;
    end;

와 같이하면 알아낼 수 있을 것이다(당연하지만, i, j, Result는 Integer, Temp는 Byte이다).



set 타입의 구조에 대해서 알아보고 엘리먼트 갯수를 카운팅해보았지만, 프로그래밍에서는 때때로 이것만으로는 부족하다. 위 코드는 특정 set 타입에 대해서는 동작하지만 여러 가지 set 타입에 대해서 엘리먼트 갯수를 구하는 범용 함수로는 사용할 수 없다. 이것이 가능하려면 Ordinal 타입에 대해서도 클래스 타입과 같은 레퍼런스형이 존재해야하는데 이는 지원되지 않는다.

그럼 어떤 방법이 있을까? 그래서 한가지 기법을 더 소개하겠다. 아래를 보자.

function GetElementsCountOfSet(const aTypeInfo: PTypeInfo; const aVar): Integer;
    var
        TypeData: PTypeData;
        Temp: Byte;
        i, j: Integer;
    begin
        TypeData := GetTypeData(GetTypeData(aTypeInfo)^.CompType^);
        Result := 0;
        for i := 0 to TypeData^.MaxValue div 8 do begin
            Temp := Ord(PChar(@aVar)[i]);
            for j := 0 to 7 do begin
                if (Temp and 1) = 1 then Inc(Result);
                Temp := Temp shr 1;
            end;
        end;

위 함수를 다음과 같이 호출하면 MySet의 엘리먼트 갯수를 되돌려준다.

GetElementsCountOfSet(TypeInfo(TMySet), MySet);


P.S. 위 코드 중에 사용된 함수를 모르는 초심자들이 있을 것 같아 노파심에 추가하는데 위 함수는 uses 절에 TypInfo 유닛을 추가해줘야한다. 질문 사항은 덧글로...

댓글 없음:

댓글 쓰기