메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

Advanced C# 11. 방정식, 미적분법 그리고 delegate

한빛미디어

|

2003-03-26

|

by HANBIT

37,040

작성: 한동훈(traxacun at unitel.co.kr)

작성일: 2003. 3. 12 - 26

지난 시간에는 분할 정복(Divide & Conquer)에 대해서 살펴보았으며 이번 시간에는 분할 정복의 연장선상에 있는 근찾기(Root Finding)와 미적분법에 대해서 살펴보고, 이러한 알고리즘을 보다 유연성있게 적용하기 위해 delegate를 어떻게 사용하는지 살펴볼 것이다.

근찾기 알고리즘(Root Finding)

방정식의 근을 찾는 알고리즘은 다양하지만 여기서는 이분법(Bisection)과 뉴튼-랩슨(Newton-Rahpson)법에 대해서만 소개할 것이다. 이외에도 방정식의 근을 찾는 알고리즘으로는 Falsi 법이나 Secant 법 등이 소개되고 있으나 실제로 가장 널리 쓰이는 뉴튼-랩슨 법을 자세히 살펴볼 것이다.

이분법(Bisection)

이분법은 중간값 정리에서 시작한다.

중간값 정리

구간 에서 연속인 구간 이에 적어도 하나 이상의 실근이 존재한다.

중간값 정리를 그림으로 나타내면 다음과 같다.


그림1. 중간값 정리

위와 같은 함수에서 x1과 x2의 값을 곱하면 음수이므로(즉, 두 함수의 부호가 서로 다르므로) 두 구간 사이에 근이 있다는 것을 알 수 있다. 다시 구간을 mid와 x2로 정의하고 값을 비교하면 결과를 구할 수 있으며, 두 구간의 차이가 0.00001 보다 작으면 근을 찾은 것으로 간주하면 된다. 다음과 같은 루프안에서 코드를 수행하면 된다.

while( Math.Abs( x1 - x2 ) > 0.00001 )

이분법에서는 중간값과 x1, x2 중에 어느 구간에 근이 존재하는지 결정해야 한다. 그러나 이분법은 근의 개수가 짝수, 중근, 발산의 경우에는 올바른 근을 찾아내지 못한다. 이분법은 이미 함수의 형태를 알고, 근이 존재하는 구간을 구한 경우에 쓸 수 있는 방법이지만, 근에 수렴하는 속도 즉, 근을 찾아내는 시간이 오래걸리는 방법이므로 다른 알고리즘을 사용한다.

이분법 또한 잘 알려진 분할 정복 기법의 예라는 것을 기억하기 바란다.

뉴튼-랩슨(Newton-Rahpson) 법

이 방법은 뉴튼법으로 소개되기도 하며, 미적분학의 미분의 소개부분에서 소개되는 방법이기도 하다. 이 방법은 근을 찾기 위해 미분을 이용하기 때문에 매우 빠른 수렴 속도를 갖는다. 실제로 뉴튼-랩슨법은 대부분의 경우에 이분법, Falsi, Secant 보다 빠른 계산 속도를 가지며, 여러분이 사용하는 공학용 계산기에도 쓰인다.


그림2. 뉴튼-랩슨법

그림에서 알 수 있는 것처럼 초기값 x0를 정해주면 그 위치에서의 기울기(미분값)를 구해서 새로운 점 x1을 구하며, 점 x1에서도 같은 방법으로 점 x2를 구하는 것을 알 수 있다. 그림에서 볼 수 있는 것처럼 근으로 수렴하는 속도가 매우 빠르다는 것을 알 수 있다. 뉴튼-랩슨 법의 정의는 다음과 같다.


그림3. 뉴튼-랩슨법의 정의

수식만으로는 이해가 잘 가지 않을테니 를 구하는 것을 예로 들어보자. 이 값을 어떻게 구하는지 궁금하지 않은가? 의 값을 구한다는 것은 다음 방정식을 푸는 것과 같다.

뉴튼-랩슨법은 함수의 미분을 필요로 한다. 위 함수를 f(x)라 하면 전체를 다음과 같이 정의할 수 있다.

초기값을 이라 할 때 그림3의 뉴튼-랩슨법의 정의대로 계산한 결과를 표로 나타내면 다음과 같다.


표1. 뉴튼-랩슨법을 이용한 를 계산하는 과정

위 계산이 맞는지 확인하기 위해 뉴튼-랩슨법으로 방정식을 구하는 C# 코드를 작성하면 다음과 같다.


그림4. newton.cs의 소스

이 오차를 나타내기 때문에 이 값이 0.00001 보다 작으면 충분히 근에 근접했다고 판단하여 루프를 끝낸다. 허용한계(Tolerance)를 보다 작게하면 보다 정확한 값을 구할 수 있다. 여기서는 0.0000001을 허용한계로 정의했다. C#에서 1E-07과 같은 지수 표기법을 사용하였다.

while( ( Function1( x ) / Function2( x ) ) > 1E-07 )

while 루프에서 직접 값을 계산하여 허용한계보다 값이 큰가를 결정하고 있는데 최적화를 위해서 계산 결과를 루프안에서 임시 변수에 저장하여 이 변수를 조건문에 사용할 수도 있다. 다음은 소스 코드를 컴파일하고 실행한 결과다.

C:\works\learn\numerical>csc newton.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (c) Microsoft Corp 2000-2001. All rights reserved.


C:\works\learn\numerical>newton
In loop: 1.5
In loop: 1.41666666666667
In loop: 1.41421568627451
In loop: 1.41421356237469
Result: 1.41421356237469

공학용 계산기에서 구한 값과 최종 결과값이 같다는 것을 알 수 있으며 공학용 계산기보다 더 정확한 값을 구할 수 있다는 것을 알 수 있다. 보다 정확한 값을 구하기 위해 허용한계값을 늘릴 수 있으나 머신 입실론(Machine Epsilon)에 의해 허용한계값을 보다 작게 했을 때 오차가 오히려 커지는 경우도 발생할 수 있다. 머신 입실론에 대해서는 C, C++, C#으로 알아본 머신 입실론을 참고하기 바라며, C#에서는 System.Double.Epsilon 속성을 통해 이 값을 출력할 수 있다.

방정식 풀이 심화

근을 찾기 위해 프로그래밍을 이용하는 경우는 비선형 방정식의 근에 근접한 값을 얻기 위한 것이다. 비선형 방정식의 근을 구하는 알고리즘으로는 선형 반복법, 이분법, 뉴튼-랩슨법, Bailey법, Aitken의 델타 제곱법등이 있다. 또한, 여기에서 소개한 것처럼 실근을 구하는 경우가 아닌 중근과 복소근을 구하기 위해서 뉴튼-랩슨법을 사용할 수 있다. 비선형 방정식과 다항 방정식의 실근과 허근을 구하는데 모두 쓸 수 있는 알고리즘이 뉴튼-랩슨법이며 성능 또한 가장 좋다. 이외에 다항 방정식을 풀기위한 알고리즘으로는 조립 제법을 이용한 Lin법이 있다. 관심있는 독자는 수치해석(Numerical Method)과 관련된 도서를 참고하기 바란다.

미분법

어떤 함수의 미분을 계산하는 알고리즘 역시 다양하다. 미분법은 고등학교때 배운 미분법의 정의를 프로그래밍에 적용하면 계산된다. 실제로는 이러한 단순한 미분법 보다는 복잡한 상미방을 풀기 위해 프로그래밍을 사용한다. 여기서는 상미방에 대해서는 소개하지 않는다.(기회가 된다면 Runge-Kutta법 중에 2차 상미방 풀이를 소개하겠지만 독자가 공업수학이나 다른 경로를 통해서 상미방의 풀이에 대해서 알고 있어야하므로 건너뛴다.) 상미방 풀이법에 관심이 있는 독자는 Euler법, Taylor 급수전개, Heun법, Runge-Kutta를 찾아보기 바라며 실제로는 Runge-Kutta법을 가장 많이 사용한다. Runge-Kutta는 가장 강력한 방법이며, 초기치, 경계치 문제등의 해법에 적용된다.

미분법은 Taylor 전개, 차분연산자, 보간다항식을 이용한 방법이 잘 알려져 있으며, 여기서는 테일러(Taylor) 급수의 전개를 이용한다.


그림5. 미분의 정의

많은 독자가 알고 있는 것처럼 미분의 정의는 위와 같다. 위 정의를 프로그램으로 옮길 때 문제가 되는 것은 컴퓨터에는 극한의 개념이 없다는 것이다. 테일러 급수 전개는 어떤 값 h의 근방에 근접한 다항식을 만들어 내는 방법이다.


그림6. 테일러 전개

그림6에서 두번째 항까지 이용하여 에 대해서 정리하면 다음과 같다.(테일러 전개는 두번째 항까지 이용해도 충분히 근사한 값이 나오며 보다 정확한 계산이 필요한 경우에 세번째 항까지 이용한다. 세번째 이상의 항을 이용하여 근사식을 구하는 방법은 잘 이용되지 않는다. 이 방법은 계산을 복잡하게 만들고, 연산 과정이 많아지는 만큼 오차가 쌓이기 때문에 보다 정밀한 정확도를 구하기가 어려워지기 때문이다.)

위 식에 O(h)는 오차한계를 나타낸다. 위와 같은 식을 다음 그림에서 알 수 있는 것처럼 전향차분(Forward Difference)이라 한다.


그림7. 전향차분(Forward Difference)


그림8. 후향차분(Backward Difference)


그림9. 중앙차분(Center Difference)

여기서는 보다 정확한 미분값을 얻을 수 있는 중앙 차분을 이용한다. 미분의 정의에 있는 lim을 컴퓨터가 풀 수 있는 수식으로 표현하기 위해 테일러 전개를 사용하였다. 각 수식의 전개는 다음과 같다.

식 (3)을 이용하면 x에서의 미분값을 계산할 수 있다. 다음 소스 코드는 에서 일 때 미분값을 구한다. h는 구간을 나타내며 이 구간값이 작을 수록 보다 정확한 미분값을 정할 수 있다. 대부분의 경우에 이면 충분히 적용할 수 있는 미분값을 구할 수 있다.


그림10. Differ.cs

실행결과는 다음과 같다.

C:\works\learn\numerical>csc differ.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (c) Microsoft Corp 2000-2001. All rights reserved.


C:\works\learn\numerical>differ
Result: 68.00000

결과와 직접 손으로 계산한 결과를 비교하면 맞게 계산 된다는 것을 알 수 있다. 그렇다면 등은 어떻게 하면 계산할 수 있을까? 이들 역시 테일러 전개를 사용하여 계산할 수 있다.

적분법

적분법 역시 미분과 마찬가지로 수학시간에 배웠던 적분의 정의를 그대로 적용하여 계산할 수 있다.


그림11. 적분법

적분의 기본 개념은 면적을 구하기 위해 구간을 잘게 나누고, 나눈 구간의 넓이를 구하여 더하는 것이다. 컴퓨터에는 극한의 개념이 없기 때문에 구간을 얼마나 작게 나누느냐에 따라서 계산 결과의 정확도가 결정된다. 위 그림에서처럼 a와 a+h 구간의 면적을 개산할 때 오차를 줄이기 위해 두 구간의 높이에 대한 평균 즉,

을 높이로 사용한다.

위와 같은 간단한 적분은 사각형을 이용하여 면적을 계산하는 것이며 이를 나타낸 그림과 소스는 다음과 같다.


그림12. 사각형을 이용한 적분


그림13. 사각형을 이용한 적분 소스

실행결과를 보면 정확한 22가 나오지는 않지만 그와 근접한 21.9999999...로 결과가 나오는 것을 알 수 있다. 간격 h를 더 작게하면 실행시간도 오래걸리면서 정확도가 오히려 떨어지는 것을 관찰할 수 있다. 이러한 결과는 루프를 반복한만큼 머신 임계값(machine epsilon)이 오차로 쌓이기 때문이다. 사각형 대신 사다리꼴을 이용하는 방법도 있다. 사다리꼴을 이용하면 사각형을 이용한 방법보다 면적에 근접한 값을 얻을 수 있다고 생각되지만 실제로는 사각형을 이용한 방법이 더 정확한 근을 구해준다. 사각형법은 구간에 따라 실제값보다 더해지거나 빼지는 값이 있어서 어느 정도 보완이 되지만, 사다리꼴은 대부분의 구간에서 실제 면적보다 더 적은 면적을 구하게 되기 때문에 오차가 더 커진다. 이 두 방법 보다 더 정확한 적분을 구하는 방법은 심슨법(Simpson Method)을 이용하는 것이다.

심슨법(Simpson"s Rule)

심슨법은 3개의 점을 지나는 2차 방정식을 구하고, 4개의 점을 지나는 3차 방정식을 구하기 위해 사용하는 라그랑지 보간법(Lagrange interpolation)을 이용한다. 라그랑지 보간법을 이용해서 두 구간의 좌표와 중점(즉, 세 점)을 지나는 2차 방정식을 구하고, 이 방정식을 이용해서 두 구간 사이의 넓이를 구한다. 다시말해, 심슨법은 두 구간 사이의 곡선을 따라서 적분하는 방법이다.

심슨법은 1/3 Rule과 3/8 Rule로 나뉘는데 1/3 Rule은 구간의 시작점과 끝점, 중점의 y 좌표를 이용하여 2차원 곡선을 지나는 곡선의 적분값을 구하는 방법이다. 3/8 Rule은 위와 비슷한 방법으로 구간을 3개로 나누고 4개의 점을 지나는 3차 곡선을 구한뒤에 그 곡선의 적분값을 구하는 방법이다. 자세한 증명은 생략하며 1/3 Rule과 3/8 Rule을 유도한 결과는 다음과 같다.

심슨법을 사용할 때 한가지 주의할 점은 반드시 구간의 수가 짝수개여야한다는 것이다. 구간의 경우가 홀수개인 경우에는 3/8 Rule을 사용할 수 있다. 1/3 rule과 3/8 rule은 구간의 수에 따라서 함께 사용한다. 위와 같은 방법은 3차식까지 정확한 적분값을 구해낼 수 있다. 심슨법을 이용한 중적분의 계산도 있다.

이외에 6차까지 정확한 값을 구해내는 Bode"s Rule도 있다.

지금까지 살펴본 방정식 풀이, 미적분 알고리즘에 대해서는 다양한 참고자료를 통해서 보다 자세히 알아볼 수 있다.

소스코드의 문제점

지금까지 작성한  소스 코드의 문제점을 살펴보자. 바로 위에서 살펴본 그림 14의 적분법의 경우 다른 함수의 적분값을 알기위해서는 Function()의 수식을 수정하거나 새로운 Function2()에 수식을 작성하고, 소스 코드를 편집하고 다시 컴파일해야한다.

위 소스코드를 컴파일한 어셈블리를 사용자가 사용할 수 있게 하는 것은 왠지 불가능해 보인다. 수식을 정의하는 함수의 이름이 소스 코드 안에 직접 정의되어 있다. 이와 같은 방법으로 작성한 소스 코드는 범용적으로 이용할 방법이 없다. 어떻게 하면 위와 같은 클래스를 사용자에게 제공하고, 사용자는 클래스의 메서드만을 이용해서 미적분을 계산할 수 있게할 수 있을까?

정답은 delegate 키워드로 정의할 수 있는 "대리자"에 있다.

대리자(delegate) - 변화하는 세상을 위한 코드

지금까지 살펴본 기나긴 설명들은 대리자(delegate)를 설명하기 위한 것이다. 위에서처럼 Function과 같은 메서드 이름을 직접 코드안에 작성하지 않고 메서드를 인자로 전달할 수 있는 방법이 없을까?

즉, 지금 사용할 메서드를 정의하지는 않지만 이러한 형식의 메서드를 정의해서 쓸 것이다. 지금은 다만 "이러한 형식을 쓴다는 것만 알아다오"라고 얘기하기 위해 사용하는 것이 delegate이다.

적분을 계산하기 위해 사용한 메서드는 다음과 같았다.

public static double Function( double x )

{

  return x * x * x;

}

여기서는 메서드 정의는 클래스 사용자에게 미루고, 다만 위와 같은 형식만 쓴다는 것을 정의하면 된다. 따라서 대리자는 다음과 같이 정의한다.

public delegate double Function( double x );

이 정의를 이용해서 적분을 계산하는 Solve 메서드는 다음과 같이 정의한다.


그림15. Integral 클래스와 일반화한 Function 메서드

일반화한 Integral 클래스를 이용하여 적분을 계산하는 소스 코드는 다음과 같으며, 여기서는 한 번에 두 개의 함수를 정의해서 각각의 적분을 구하고 있다.


그림16. f1과 f2를 적분한다.

앞에서 작성한 적분 알고리즘을 클래스로 만들어, 다른 클래스에서 범용적으로 사용하려면 메서드의 내용과 정의를 분리시켜야한다. 사용자가 어떤 동작을 하는 코드를 정의할지 알 수 없기 때문이다. 다만 사용자에게 이 메서드를 사용하려면 이러한 정의만을 지켜달라고 얘기하는 것이 delegate이다.

따라서 메서드의 정의를 delegate로 정의하고, 클래스 내에서는 기존에 메서드를 이용하던 것처럼 코드를 작성하면 된다. Main()을 보면 한 가지 흥미로운 점은 new Integral.Function( f1 )과 같은 코드를 사용하고 있다는 것이다. 이것은 Integral 클래스의 인스턴스를 반환해야하기 때문이다.

대리자(delegate)

지금까지 살펴본 것처럼 메서드를 정의하지 않고 마치 메서드가 있는 것처럼 가정하여 프로그래밍을 할 수 있게 해주고, 실행시간에 동적으로 실제 구현된 메서드를 이용할 수 있게 해주는 것이 대리자이다. 여러분이 delegate를 사용하여 메서드의 원형을 정의하기만 하면되고 다른 사람들은 delegate로 선언된 메서드의 원형대로 실제 메서드를 정의하고 구현하면 된다. 그 이하의 모든 물밑 작업은 닷넷 프레임워크가 알아서 해줄 것이기 때문이다.

실제로 윈도우 프로그래밍이나 네트워크, 리모팅과 관련된 프로그래밍을 할 때는 대리자를 생각하지 않고는 프로그래밍을 할 수 없다. 왜 대리자가 없어서는 안되는지 생각해보자.

일상적으로 마우스를 사용하고 있다. 마우스를 사용한다는 행위는 마우스 포인터의 위치를 이동시키는 MOUSE_MOVE라는 메시지를 운영체제에 던지는 역할을 할 것이며, 사용자가 마우스 왼쪽 버튼을 누르는 동작과 버튼에서 손을 떼는 동작은 MOUSE_LEFT_BUTTON_DOWN과 MOUSE_LEFT_BUTTON_UP 이라는 메시지를 운영체제에 보낸다. 또, 이 두 동작이 1/8 초안에 발생하면 MOUSE_LEFT_BUTTON_CLICK 메시지를 운영체제게 던지게 된다. 어떻게해서 운영체제는 이렇게 다양한 메시지에 대한 다양한 동작을 하나의 형식으로 마무리할 수 있을까?

이미 알 수 있는 것처럼 마우스 동작에 대한 기본 원형은 delegate 키워드를 사용하여 정의되어 있으며, 개발자는 해당하는 메시지 유형에 따라 코드를 구현하기만 하면 되는 것이다. 물론 독자가 위와 같은 다양한 메시지에 대해서 아무것도 작성하지 않는다면 마우스로 버튼을 클릭해도 아무일도 발생하지 않는다. 왜냐면 delegate로 정의된 메서드의 원형만 있을 뿐 실제 구현은 아직 없기 때문이다.

delegate를 사용하지 않아도 이벤트를 정의할 수 있는 방법은 많다. 그러나 대부분의 경우에 이벤트를 정의하기 위해 delegate를 사용하는 것은 매우 보편적인 일이다.

ASP.NET이나 윈도우와 같은 UI 프로그램을 살펴보면 이벤트를 등록하고 해지하는 과정을 보게 된다. 그리고 이벤트와 함께 등록할 메서드 이름을 인자로 전달하는 것을 알 수 있다. 혹시라도 기억나지 않을 독자를 위해 옮긴다면 다음과 같을 것이다.

this.button1.Click += new System.EventHandler(this.button1_Click);

button1_Click의 원형이 delegate를 사용해서 닷넷 프레임워크안에 정의되어 있는 것이다. 그리고 여러분은 다양한 함수를 delegate로 닷넷 프레임워크안에 선언된 원형대로 정의하고 위와 같이 이벤트를 등록하는 것만으로 다양한 동작을 정의할 수 있게 된다.

"+="과 같은 기호를 통해서 이벤트를 등록하는 것을 알 수 있는데 이는 디자인 패턴 중에 Observer 패턴의 Register(), UnRegister() 동작에 해당한다.(GoF 책에는 Attach()와 Detach()로 설명되어 있는 듯 하다.) Observer 패턴에 대해서는 나중에 해당 패턴을 설명할 때 다시 살펴보겠지만 C#을 비롯한 닷넷 환경에서는 대리자를 사용하기 때문에 Observer에 대한 효용빈도가 떨어지는 것은 사실이다.

위와 같은 이벤트 코드는 VS.NET에서는 개발자를 위해 자동으로 생성해주지만, .NET Framework Essential과 같은 책을 통해서 직접 순수 텍스트 편집기로 작성함으로써 프레임워크의 전반적인 내용을 이해하는 것도 좋을 것이다.

고급 대리자(Advanced delegate)

대리자를 이용한 코딩 중에 흥미로운 것들이 많다. 위에서 소개한 적분 코드를 delegate를 사용하여 컴파일된 코드로 사용자에게 배포하고, 사용자는 원하는 함수를 정의하여 직접 적분을 수행할 수 있게 되었다. 이와 같이 사용하는 delegate는 C/C++ 언어등에서 소개하는 함수 포인터로 이해하면 좋을 듯 하다. 그런가하면 C/C++ 등에서 빈번하게 쓰이던 콜백(callback)을 대신하여 닷넷에서는 대리자를 사용한다. - 콜백은 GUI 환경에서 이벤트를 정의하기 위해서 쓰였으며, 앞에서 본 것처럼 대리자를 사용하여 이벤트를 정의하는 방법에 대해서도 살펴보았다. 여기서 잠시 함수 포인터와 콜백에 대해 언급한 것은 이전 언어 사용자를 위한 참고사항이며 굳이 머리에 담아두려 할 필요는 없다.

대리자를 사용하는 흥미로운 경우는 delegate를 static으로 선언하여 사용하는 경우, delegate 배열을 선언하여 연쇄적인 이벤트 사슬로 이용하는 경우가 있다.

delegate 배열을 사용하는 방법은 delegate로 선언되는 몇가지 원형을 정의하고 이들 원형을 배열 요소로 추가하는 방법이다. 이와 같은 방법의 장점은 재고 단계, 재난 단계에 따라 적절한 동작들을 손쉽게 커스터마이징할 수 있다는 장점이 있다.

예를 들어, 침입 탐지 시스템을 만든다고 하자. 침입의 정도에 따라서 경고 로그를 시스템에 남길 수 있고, 심각한 침입이 발생하면 로그와 함께 관리자에게 메일을 남기게 하며, 최고 경고 수준이면 로그 기록, 관리자 메일, SMS 메시지를 남기고 싶다고 하자. 이런 경우에 DoLog(), DoMail(), DoSms()에 대한 메서드 원형을 delegate로 선언하고, 클라이언트에서 세 가지 메서드에 대한 코드를 실제로 구현하고, 각각의 경고 단계에 따라 delegate 배열에 각 대리자를 원하는 조합으로 추가하면 된다. 여기서는 벌써 3가지 방법이 있으므로 대응에 따라 2의 3승 = 8가지에 해당하는 대응 수준을 융통성있게 정의할 수 있게 된다. 이러한 사용법은 디자인 패턴의 Chain of Responsibility과 유사하다. 마찬가지로 이에 대해서도 해당 패턴을 설명할 때 자세히 살펴보도록 하자.

벌써부터 이러한 사용법에 관심이 간다면 관련 자료를 인터넷과 서적을 통해서 학습하기 바라며, 아직 이러한 이야기가 어려운 분들은 적분법에서 소개한 내용부터 익숙해지도록 하자. 익숙해지는 것만으로도 여러분은 훨씬 많은 융통성을 발휘하게 될 것이다. 다음 정의만 기억하도록 하자.

"delegate는 메서드 원형을 정의하기 위해 사용하며,
메서드 원형에 대한 구현은 클라이언트에 맡긴다."

마치며

이번에는 많은 사람들이 어렵게 생각하는 대리자(delegate)에 대해서 살펴보았다. 대리자에 대한 정의나 코드 구현을 보여주기 보다는 실제로 대리자를 쓰는 경우를 자연스럽게 발생시키려 하다보니 본의아니게 이번회가 지나치게 길어지고 복잡해진 것에 대해서 미안하게 생각한다. 위에서는 간단하게나마 3가지 수치해석 알고리즘을 살펴보았다. 방정식을 풀이하기 위한 뉴튼-랩슨법, 미분을 해결하기 위해 동원된 테일러 전개법, 적분법에 널리 쓰이는 심슨법에 대해서 살펴보았다.(Newton-Cotes, Romburg, Adaptive와 같은 다양한 해법이 있지만 알고리즘 강좌가 아닌탓에 대표적인 것들에 대해서만 소개하였다. 관심있는 독자는 Newton-Cotes 부터 시작하여 다양한 알고리즘들을 탐험해보기 바란다)

대리자(delegate) 용어 선정에 대하여 - delegate는 대부분의 국내 서적에 "위임, 델러게이트, delegate, 가장"으로 번역소개되고 있으나 닷넷 프레임워크 한글판에 포함된 도움말이나 VS.NET 한글판의 도움말, MSDN Online등에서는 모두 "대리자"라는 용어로 통일되어있다. 독자가 도움말에서 보다 자세한 정보를 찾으려할 때 "위임, 델러게이트" 등을 입력해서는 원하는 자료를 찾을 수 없다. 때문에 가급적 한국 마이크로소프트가 사용하는 용어로 통일하였다. 2001년에는 다양한 용어로 혼재사용되었으며 MS도 그러한 모습을 보였으나 2001년 후반기 이후 MS의 모든 문서에서 대리자(delegate)로 통일되었으며 개발자간에는 위임, 대리자, 델러게이트와 같이 쓰인다. 다른 누군가가 "데러게잇"이라해도 당황하지는 말자. "데러게잇"이라 발음할 수 있는 사람은 코쟁이뿐이다. 백업(backup)도 누군가가 "배컵"이라 말해도 당황하지 말자. 그는 진짜 "코쟁이"이자 "본토발음"이니까. 이해하고 살자. IT 기술 자체가 외래문물이기에 기술에 대한 이해가 깊어지면서 용어가 바뀌는 경우도 흔하며, 용어란 익숙해지기 나름이라 생각한다. 용어에 "혼"이 들어있다고 생각하지 않는다.

사전에 나온 뜻대로 "delegate란 대리자라는 의미를 가지고 있습니다. 뭔가를 대신해 준다는 의미입니다"라는 식의 설명보다는 저자도 완전히 이해하고, 독자에게 쉽게 풀어서 설명해 줄 수 있는 분을 기대하며 이 글을 마칩니다. - 2003. 3. 26, traxacun

참고자료

Applied Microsoft .NET Framework Programming, Ch. 17 delegate, Jefrey Ritcher, MS Press
원서와 번역서 모두 있으며 MS Press의 저자로 NT 시절에도 상당히 깊이 있는 해설과 지식이 돋보이는 저자였다. 닷넷 프레임워크의 보다 깊은 곳에 가보고 싶다면 꼭 한 번 보기 바란다.

Advanced .NET Remoting, APress
리모팅과 웹서비스 프로그래밍에 대해서는 현재까지 나온 책중에 가장 우수하지만 원서만 제공된다. 리모팅은 대리자를 네트워크 개념으로 확대시킨 것이라 과감히 뻥을 쳐도 될 듯하다. 당연히 많은 곳에서 대리자가 등장하며 좋은 참고가 될 것이다.

Programming C# 2nd ed, Ch. 12 Delegates and Events, Jesse Liberty, O"Reilly
원서와 번역서 모두 있으며 개정판의 소스 코드를 검토하면서 1판의 오류를 거의 대부분 수정하였다. 그럼에도 나의 이름을 넣어주지 않았다. 어지간히 미운털 박혔나보다. ^^;
이 책의 특징은 쉽게 설명하고 있으면서도 핵심을 놓치지 않으면서 두껍지 않다는 정도다.

Inside C#, 2nd ed. Ch. 14 Delegates and Event Handlers, MS Press
1판에 비해서 두 배이상 증편된 2판이다. 번역서는 1판만 제공된다. 이 책에서 대리자의 다양한 응용에 대해서 다루고 깊이있게 설명하고 있는 점이 돋보인다.

Applied Numerical Methods in C, Prentice Hall
각 장의 끝에 제공되는 C 소스 코드는 해석하기 어렵게 되어있어, 어떻게하면 이해하기 어려운 코드를 작성할 수 있는가에 대한 표본이라 여겨지는 소스지만, 일본인 특유의 자세한 설명과 실용적인 설명이 특징인 책으로 입문서로 택하기에 가장 무난하다. 2003. 3월로 새롭게 나온 책이니 소스 코드도 어느 정도 개선되었기를 바라며 참고자료로 인용한다.

Numerical Recipes in C++
Numerical Recipes in C, Cambridge Univ. Press
이 책은 수치해석 알고리즘과 관련해서 매우 잘 알려진 책이지만 개인적으로 느끼기에 가장 어렵게 느껴지는 책이기도 하다. 이 책에 나오는 절반이상의 수식을 이해하지 못해 절망한 기억이 있다. 그럼에도 넘으라면 넘어가야할 듯 하다. Applied Numerical Methods in C 이후에 이 책을 선택하는 것이 보다 수월한 선택이 될 것이며, 두 권 모두 필요에 따라 레퍼런스로 활용하면 좋다. 수치해석과 관련하여 닷넷 프레임워크, JDK 등의 소스에서 볼 수 있는 알고리즘들은 이 책을 참고로 작성된 것들이 많다.
두 권 모두 절판되었으나 인터넷에서 다운 받을 수 있는 형태로 제공되는 것으로 안다.

TAG :
댓글 입력

최근 본 상품0