// 과니입니다
// 2008.04.25 오늘의 팁은 누구나 다 아는 SWAP 함수입니다.
// 두 가지의 데이터를 서로 교체할 때 많이 만들어서 쓰죠.
// 일반적으로는 swap(a, b)이런식으로...
// swap도 알게 모르게 분석할 수 있는 부분이 있더라구요...ㅎㅎ

// 자 그럼 만들어 봅시다. swap 함수

// 기본형 (실수)
procedure swap(a : Integer; b : Integer);
var
    t : Integer;
begin
    t := a;  // 임시 변수 t
    a := b;
    b := t;
end;

procedure TForm2.Button1Click(Sender: TObject);
var   
    a, b : Integer;
begin
    a := 10;
    b := 20;

    swap(a, b);

   
ShowMessage('a 변수값: ' + IntToStr(a) + ', b 변수값: ' + IntToStr(b));
end;

// 이렇게 하면 swap 함수가 완성 됩니다. 그럼 결과는 어떨까요? 잘 나올까요?
// 잘 나오겠다고 생각하셨다면 가장 중요한걸 놓쳐버린 겁니다.
// 제가 기본형 (실수)라고 적어논건 많이 실수하는 부분이라서 그렇게 적어놓았습니다.
// swap함수의 값은 그걸 호출 하는 변수의 값에 적용 되지 않기 때문에 결과적으로는
// a 변수값: 10, b변수값: 20 이 나오게 될 것 입니다.
// 이것을 가장 간단히 해결할 수 잇는 방법 swap함수 인수에 var를 붙이는 걸로 해결할 수 있습니다.

// 수정 : 1
procedure swap(var a : Integer; var b : Integer);
var
    t : Integer;
begin
    t := a;  // 임시 변수 t
    a := b;
    b := t;
end;

// var은 많은 분들이 알고 계셔서 간단히만 말하자면 인자는 CallByValueCallByReference 로 분류 하는데
// CallByValue는 함수에 값만을 넘겨받는 거라서 함수가 끝나면 그 값은 그냥 사라져 버린다고 보시면 됩니다.
// 생략하면 CallByValue 가 됩니다.
// CallByReference 가 여기서 var에 해당되는 데 함수에 변수의 주소를 넘겨받아서 함수에서 그 값을 변경한다면
// 함수가 종료되도 그 변수가 변경된 값을 가지게 되는 것입니다.

// 여기까지 설명하려고 강좌를 쓴 건 물론 아니겠죠. var을 설명하기 위해서가 아니기 때문에
// swap 함수를 포인터로 변경해보겟습니다.

// 수정 : 2
procedure swap(a : PInteger; b : PInteger);
var
    t : Integer;
begin
    t := a^;  // 임시 변수 t
    a^ := b^;
    b^ := t;
end;

// swap 함수를 포인터로 변경하였습니다. 함수의 인자가 포인터로 변경되었기 때문에 호출시에는
    Swap(@a, @b);

// 으로 해주시면 됩니다.

// 하지만 임시변수 t가 있다는게 맘에 안들기 때문에 다시 한번 더 swap 함수를 수정해 보겠습니다.

// 수정 : 3

procedure swap(a : PInteger; b : PInteger);
begin
    a^ := a^ + b^;
    b^ := a^ - b^;
    a^ := a^ - b^;
end;


// 이렇게 하면 임시변수 없이도 값이 변경되는 것을 확인 하실수가 있습니다.
// 하지만 이 함수에는 가장 위험한 요소가 있습니다. 값을 더하고 빼는 부분이 있기 때문에
// 인자의 값이 Integer형 변수의 한계에 도달하게 되는 부분에서
// 오버플로우 현상이 발생할 수 있습니다.

// 자 그럼 어떻게 하느냐

// 수정 : 4
procedure swap(a : PInteger; b : PInteger);
begin
    a^ := a^ xor b^;
    b^ := a^ xor b^;
    a^ := a^ xor b^;

end;

// 위와 같이 swap 함수를 변경해주시면 임시변수 t도 필요없고 변수의 값이 크더라도 오버플로우 현상이 없습니다.

// 누구나 알고 있는 swap이지만 그냥 지나 칠수 있는 가장 간단한 함수도 연구하면
// 더 좋은 함수로 리팩토리 될수있습니다.^^;;
//
// 이상 과니였습니다.


// 추가사항
// Lyn님이 알려주신 사항입니다.
[수정 : 2]번 함수가 [수정 : 4]함수보다 알아보기 힘들정도의 시간이긴 하지만 더 빠릅니다.ㅎ

// ------------------------------------------------------------------------
// 추가사항 : 2008-05-14
// ------------------------------------------------------------------------
위의 사항은 C/C++언어로 포팅 시
void swap(int *a, int *b)
{
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}


로 되고 이것은 다시 정리하면 다음과 같이 됩니다.

#define swap(a,b) (a)^=(b)^=(a)^=(b);

과정 1, 2, 3은 바로 위 define 함수로 만드는 과정을 보여주고 있는 부분으로

마지막 define은 C/C++에 해당하는 부분이라서 Delphi Swap설명에는 첨부하지 않았었습니다...ㅎ

이것을 다시 템플릿으로 선언하는 부분도 있는 데 그것은 추후에 ^^;;