어떤 프로그램이 수백 줄로 길어지면 따라가기 어렵게 된다. Fortran codes 실제의 공학적 문제를 해결하기 위한 Fortran 코드는 수만 줄 씩 되기도 한다. e only way to handle such 이런 엄청난 코드를 다루는 유리한 방법은 modular approach을 써서 프로그램을 부프로그램subprograms이라고 하는 작은 단위 여러 개로 나누는 것이다.
부 프로그램이란 확실하게 정의된 작은 문제를 해결하는 (작은) 코드 조각이다. 큰 프로그램에서는 다른 자료를 갖고 같은 문제를 풀어야 하는 경우가 종종 있다. 이러 작업은 같은 코드를 반복하는 대신에 부프로그램을 사용하여 해결하여야 한다. 같은 부프로그램을 입력 자료만 다르게 하여 여러 차례 사용할 수 있다.
Fortran에는 두 가지 형태의 부프로그램 - 함수functions 와 서브루틴 subroutines -이 있다.
함수 (Functions)
Fortran 함수는 수학의 함수와 매우 비슷하다. 둘 다 일단의 입력 변수 (parameters)를 받아들여 어떤 형태의 값을 준다. 앞에서 사용자 정의user defined 부프로그램에 대하여 살펴보았다. Fortran 77 역시 내장built-in 함수가 있다.
다음의 간단한 예는 함수를 사용하는 방법이다.
x = cos(pi/3.0)
여기서 cos은 cosine 함수이고 따라서 (pi가 올바르게 정의되어 있다면 (Fortran 77에는 내장된 상수는 없다.)) x는 0.5가 될 것이다. Fortran 77에는 내장 함수가 여러 가지 있다. 가장 많이 사용하는 것으로는:
abs absolute value
min minimum value
max maximum value
sqrt square root
sin sine
cos cosine
tan tangent
atan arctangent
exp exponential (natural)
log logarithm (natural)
보통 함수는 형태 type가 있다. 위에서 언급한 대부분의 내장 함수는 일반적generic이다. 따라서 위 예에서 pi와 x 는 real이거나 double precision이다. compiler는 형태를 조사하여 알맞는 (real 또는 double precision) cos을 사용할 것이다. 불행하게도, Fortran은 다양한 polymorphic 기능의 언어가 아니라 일반적으로는 사용자가 조심하여 변수와 함수의 형태를 맞추어야 한다!
이제 사용자가 만든 함수를 살펴보자. 다음 문제를 풀어보자: 지질학자가 Bay Area의 강수량을 조사하여 관계 수식 r(m,t) 를 구하였다. 이 식에서 r 은 비의 양, m 은 달이고, t 는 위치에 따라 다른 scalar parameter이다. r에 관한 식과 t 값을 사용하여 연간 강수량을 계산한다.
이 문제를 푸는 분명한 방법은 모든 달에 대하여 계산하는 loop를 만들어 더함으로써 r의 값을 구하는 것이다. r의 값을 구하는 것은 독립적인 작은 문제이므로 이를 함수로 하는 것이 편리하다. 다음 주 프로그램 (main program)을 사용할 수 있다:
program rain
real r, t, sum
integer m
read (*,*) t
sum = 0.0
do 10 m = 1, 12
sum = sum + r(m, t)
10 continue
write (*,*) 'Annual rainfall is ', sum, 'inches'
stop
end
덧붙여 함수 r은 함수로 정의하여야 한다. 지질학자가 얻은 공식은 다음과 같다.
r(m,t) = t/10 * (m**2 + 14*m + 46) if this is positive
r(m,t) = 0 otherwise
이를 Fortran function로 나타내면 다음과 같다.
real function r(m,t)
integer m
real t
r = 0.1*t * (m**2 + 14*m + 46)
if (r .LT. 0) r = 0.0
return
end
함수의 구조가 주 프로그램과 닮았다는 것을 알 수 있다. 그 차이는:
함수는 형태(type)가 있다. 이 형태는 부르는 프로그램에서도 선언하여야 한다.
결과 값은 함수와 같은 이름의 변수에 저장되어야 한다.
함수는 of stop 대신 return 문으로 끝나야 한다.
요약하면 Fortran 77 함수의 일반적 syntax는 다음과 같다.
type function name (list-of-variables)declarationsstatements
return
end
함수는 부르는 프로그램에서 정확한 형태로 선언되어야 한다. 그리고 그 함수 이름과 괄호 안에 매개 변수를 나열하여 부른다.
서브루틴 (Subroutines)
Fortran의 function은 일반적인 함수와 마찬가지로 오직 한가지 결과만을 갖고 돌아오는 것이 원칙이다. 때때로는 두 개나 그 이상의 변수 (때때로는 결과 변수 없이)값을 갖고 돌아 오게하고 싶을 때가 있다. 이런 경우에는 서브루틴subroutine 구조를 사용하여야 한다. syntax는 다음과 같다.
subroutine name (list-of-arguments)declarationsstatements
return
end
서브루틴은 type이 없고 따라서 이 서브루틴을 부르는 프로그램에서 종류 type 를 선언해줄 필요가 없다.
다음은 아주 간단한 서브루틴의 예이다. 이 서브루틴에서는 두 정수 변수의 값을 서로 바꾸는 것이다.
subroutine iswap (a, b)
integer a, b
c Local variables
integer tmp
tmp = a
a = b
b = tmp
return
end
여기에는 변수 선언이 두 block으로 되어있음에 주의한다. 우선 입출력 매개 변수, 즉 부르는 프로그램 caller 과 불리어지는 프로그램 callee 양쪽에 공통인 변수를 선언한다. 그리고 나서 국부 변수local variables, 즉 이 부프로그램 안에서만 사용하는 변수를 선언한다. 서로 다른 부 프로그램에서 같은 변수 이름을 사용할 수 있고 compiler는 이를 이름이 같지만 다른 변수로 취급한다.
Call-by-reference
Fortran 77은 소위 call-by-reference 방식을 취한다. 이는 함수/서브루틴 매개 변수의 값만을 넘겨주는 (call-by-value) 대신 변수의 memory 주소 (pointer)를 넘겨주는 것을 의미한다. 다음 간단한 예가 그 차이를 설명한다.
program callex
integer m, n
c
m = 1
n = 2
call iswap(m, n)
write(*,*) m, n
stop
end
이 프로그램의 결과는 예상할 수 있는대로 "2 1"이다. 그러나 Fortran 77에서 call-by-value를 사용한다면 결과는 "1 2"로 변수 m과 n이 서로 바뀌지 않는다. 이는 m과 n의 값만이 서브루틴 iswap에 전달된다면 m과 n이 서브루틴 내부에서 서로 비뀐다하더라도 새 값은 주 프로그램에 전달되지 않을 것이기 때문이다.
위 예제에서 call-by-reference가 정확하게 우리가 원하는 바이다. 하지만 이 때문에 Fortran code를 쓸 때 원하지 않던 부작용 side effects이 일어나지 않도록 주의하여야 한다. 한 예로, For example, sometimes it is tempting to use an input parameter 부 프로그램 안에서 입력 매개 변수를 국부 변수로 사용하면 편할 때가 잇는데 이 경우 그 값이 바뀌어 버린다. 그 결과 기대하지 않던 값이 이 부프로그램을 부른 프로그램으로 퍼져나가게 되기에 결코 사용하여서는 안 된다.
뒷 장에서 배열(array)을 매개 변수로 넘겨 주는 문제에서 다시 살펴 볼 것이다.
Exercises
Exercise A
정수 n을 받아들여서 n! (n factorial)을 돌려 보내는 function fac을 작성한다. 작성한 프로그램을 다음 주프로그램을 사용하여 검사하라.
program tstfac
c
c Exercise A, section 11.
c Main program to test factorial function.
c
integer n, fac
10 continue
write(*,*) 'Give n: '
read (*,*) n
if (n.gt.0) then
write(*,*) n, ' factorial is', fac(n)
goto 10
endif
c End of loop
stop
end
n = 5, 10, 20일 때의 결과를 살펴본다. 또 이 프로그램을 정지시키는 방법을 알아본다. (Hint: You have to use a loop in your function since Fortran 77에서는 자신이 자신을 부르는 재귀 recursion이 불가능하므로 fucntion에서 loop를 사용하여야만 한다.)
Exercise B
세 실수 a, b, c를 입력으로 하여 이차방정식 ax**2 + bx + c = 0의 두 실근을 구하는 서브루틴 qsolve를 작성하라. 근이 허수이면 다음과 같은 오류 정보 error message를 출력하게 한다.