Function Prof. Jung Guk Kim HUFS,
RTDCS Lab. 2 목표 함수와 프로시저를 프로그램하기 인자 전달 개념에 친숙하기 값 인자와 참조 인자를 언제 사용하는지를 알기 함수 주석의 중요성을 알기 변수 범위를 결정 부대효과와 전역 변수의 사용을 최소화 복잡한 작업을 보다 단순한 작업들로 나누는 전략을 개발 함수와 사전 조건이 있는 함수 호출자의 책임을 문서화하기
RTDCS Lab. 3 블랙박스로서의 함수 (1) 함수 (function) 는 이름을 가진 코드이다. sqrt() – 부동소수점 수의 제곱근 계산 getline() – 스트림으로부터 한 줄 읽기 함수는 블랙박스 – 함수 사용 시 함수 내부 코드를 알 필 요가 없다. 다른 함수가 실행 중일 때 main() 함수의 실행은 잠시 중 단된다.
RTDCS Lab. 4 블랙박스로서의 함수 (2) 함수는 함수로 전달될 입력 값을 인자로 받는다. x 는 인자 : y = sqrt(x); x 와 y 는 인자 : z = pow(x, y); 수식은 인자 : sqrt(b * b - 4 * a * c); 함수는 출력 즉 반환 값을 가진다. y 는 반환 값을 저장 : y = sqrt(x); 인자와 반환 값을 데이터 타입을 가진다. 오류 : sqrt("Harry"); 오류 : string s = sqrt(5.5);
RTDCS Lab. 함수 작성 (1) 5
RTDCS Lab. 함수 작성 (2) 함수 이름, 반환 값 타입, 인자 double future_value(double p) 함수 실행 동안 인자는 인자 변수 ( parameter variable) 에 저장된다.( 예, p). 함수 몸체는 (body) 는 중괄호로 구분된다 : double future_value(double p) {... } 반환 값은 호출한 함수로 반환된다. double future_value(double p) { double b = 1000 * pow(1 + p / 100, 10); return b; } 6
RTDCS Lab. 함수 작성 (futval.cpp) 7
RTDCS Lab. 함수 작성 (Hard Wired Values : 함수 내부에 주어진 값 ) 위의 future_value 함수의 단점 : 초기 투자액과 년수가 hard-wired 되어있다 Hard-wiring 는 함수의 재사용을 어렵게 한다 : 20 년 후의 투자액은 ?. 이자율이 달라진다면 ?. hard-wired values 를 함수에 인자로 전달하면 함수가 재사용 가능해진다. 재사용 가능하도록 함수를 설계하는 것이 좋다. double future_value(double initial_balance, double p, int n) { double b = initial_balance * pow(1 + p / 100, n); return b; } 8
RTDCS Lab. 함수 주석 (1) 모든 함수에 주석을 붙여야 한다. 주석은 프로그램을 읽는 사람에게 도움을 준다. 주석을 쓰는데 표준적인 방법은 없지만, 이 책은 javadoc 스타일 사용 은 각 인자 설명 은 반환 값 설명 코드를 작성하기 전에 먼저 주석을 달 것 ! 9
RTDCS Lab. 10 함수 주석 (2) /** Computes the value of an investment with compound intial_balance the initial value of the p the interest rate pre period in n the number of periods the investment is the balance after n periods */ double future_value(double initial_balance, double p, int n) { double b = initial_balance * pow(1 + p / 100, n); return b; }
RTDCS Lab. 11 반환 값 (1) Syntax 5.2 : return Statement
RTDCS Lab. 12 반환 값 (2) return 문이 실행되면 함수를 빠져 나간다. double future_value(double initial_balance, double p, int n) { if (n < 0) return 0; if (p < 0) return 0; double b = initial_balance * pow(1 + p / 100, n); return b; }
RTDCS Lab. 13 반환 값 (3) 함수의 실행이 끝날 때에는 언제나 반환 값을 가진다. double future_value(double initial_balance, double p, int n) { if (p >= 0) return = initial_balance * pow(1 + p / 100, n); /* Error */ }
RTDCS Lab. 14 반환 값 (4) true/false 값을 반환하는 함수를 술어 (predicate) 함수라 한다. bool approx_equal(double x, double y) { const double EPSILON = 1E-14; if (x == 0) return fabs(y) <= EPSILON; if (y == 0) return fabs(x) <= EPSILON; return fabs(x - y) / max(fabs(x), fabs(y)) <= EPSILON; }
RTDCS Lab. 15 반환 값 approx.cpp(5)
RTDCS Lab. 16 반환 값 approx.cpp (6)
RTDCS Lab. 17 인자 (1) 함수 호출에 수식을 사용 가능 b = future_value(total / 2, rate, year2 - year1); 인자 변수는 변수처럼 수정 가능하다. double future_value(double initial_balance, double p, int n) { p = 1 + p / 100; double b = initial_balance * pow(p, n); return b; } 인자 변수의 수정은 나쁜 스타일이다
RTDCS Lab. 18 인자 (2)
RTDCS Lab. 19 부대효과 (Side Effects) 함수 실행으로 외부에서 관찰 가능한 효과 (side effect) 스크린 디스플레이 ( 오류 메세지를 포함해서 ). 함수 밖에 있는 변수의 수정. 프로그램의 실행을 종료. 값을 반환하는 것을 제외하고는 함수 외부에 영향을 주어 서는 안된다. 함수에서 값 출력의 문제점. 그래픽스 프로그램은 출력 스트림이 없다. 이상적인 함수는 부대작용없이 하나의 값을 계산하는 함 수
RTDCS Lab. 20 프로시저 (1) 반환 값이 없는 함수. print_time(now); 반환 값이 없음을 나타내는 키워드 : void. void print_time(Time t) { cout << t.get_hours() << ":"; if (t.get_minutes() < 10) cout << "0"; cout << t.get_minutes() << ":“; if (t.get_seconds() < 10) cout << "0"; cout << t.get_seconds(); } 프로시저는 반환 값이 없으므로 부대작용이 있어야 한다. 이상적인 프로시저 : 하나의 부대작용만 포함
RTDCS Lab. 21 프로시저 (2)
RTDCS Lab. 22 프로시저 (3)
RTDCS Lab. 23 참조인자 (Reference Parameters(1)) 지금까지의 인자를 모두 값 인자 (value parameters) 라 부른다. 값 인자의 수정은 원래 값에 영향을 주지 않는다. 참조 인자는 새로운 변수를 만들지 않고, 기존 변수를 참 조한다. 참조 인자를 변환하면 참조인자가 참조하는 변수의 값이 변경된다.
RTDCS Lab. 24 참조인자 (2) 참조 인자는 ‘&’ 를 사용하여 표시. void raise_salary(Employee& e, double by) { double new_salary = e.get_salary() * ( 1 + by / 100); e.set_salary(new_salary); }
RTDCS Lab. 25 참조인자 (3)
RTDCS Lab. 26 참조인자 (4)
RTDCS Lab. 27 참조인자 (5)
RTDCS Lab. 28 변수범위 (Variable Scope(1)) 다른 함수에 같은 이름을 가진 변수를 사용할 수 있다. 프로그램에서 변수가 보이는 부분을 변수 범위라 부른다. 변수 범위는 변수가 정의된 부분부터 변수가 정의된 블록 끝까지이다.
RTDCS Lab. 29 An Example : Variable Scope(2) double future_value(double initial_balance, double p, int n) { double r = intial_balance * pow(1 + p / 100, n); return r; } int main() { cout << "Please enter the interest rate in percent: “; double r; cin >> r; double balance = future_value(10000, r, 10); cout << "After 10 years the balance is" << balance << "\n"; return 0; }
RTDCS Lab. 30 전역 변수 (1) 함수 밖에 정의된 변수 전역 변수는 모든 함수에서 볼 수 있다. 전역 변수의 사용을 최소화해야 한다. 전역 변수의 사용이 불가피한 경우 (cin, cout, and cwin)
RTDCS Lab. 31 An Example : Global Variables(2)
RTDCS Lab. 32 단계별 세분화 (Stepwise Refinement) 문제 해결을 위한 좋은 방법 복잡한 문제를 풀 경우, 더 단순한 작업으로 나누어서 생 각하라. How do you get coffee? Ask someone else to do it. Make coffee. OK, how do you make coffee? Make instant coffee. Brew Coffee. How do you make instant coffee? Start by boiling water. Etc.
RTDCS Lab. 33 유사코드 (Pseudocode) 로부터 코딩 (1) 프로그램 코딩 전에 계획을 세우라. 한 곳 이상에서 필요한 코드라면 함수를 작성한다. 전체 함수를 작성하기 전에 먼저 주석을 단다. Example: Writing the function /** Turns a number into its English n a positive integer < the name of n (e.g. "two hundred seventy four"· */ string int_name(int n);
RTDCS Lab. 34 유사코드 (Pseudocode) 로부터 코딩 (2) 문제를 더 작은 단위로 분리하라 /** Turns a digit into its English n an integer between 1 and the name of n ("one"... "nine") */ string digit_name(int n); /** Turns a number between 10 and 19 into its English n an integer between 10 and the name of n ("ten"..."nineteen") */ string teen_name(int n); /** Gives the English name of a multiple of n an integer between 2 and the name of 10 * n ("twenty"..."ninety") */ string tens_name(int n); 빠진 부분은 ? 백단위에 대해서는 ? 천 단위에 대해서는 ?
RTDCS Lab. 35 유사코드 (Pseudocode) 로부터 코딩 (3) 알고리즘이 복잡하면 유사코드 (pseudocode) 를 작성해 본다. 유사 코드는 C++ 와 자연어의 중간 정도의 표현이다. string int_name(int n) { int c = n; /* the part that needs to be converted */ string r; /* the return value */ if (c >= 1000) { r = name of thousands in c + "thousand" remove thousands from c } if (c >= 100) { r = r + name of hundreds in c + "hundreds" remove hundreds from c }
RTDCS Lab. 36 유사코드 (Pseudocode) 로부터 코딩 (4) if (c >= 20) { r = r + name of tens in c remove tens from c } if (c >= 10) { r = r + name of c c = 0 } if (c > 0) r = r + name of c; return r; }
RTDCS Lab. 37 유사코드 (Pseudocode) 로부터 코딩 (5) #include using namespace std; /** Turn a digit into its English n an integer between 1 and the name of n ("one"... "nine") */ string digit_name(int n) { if (n == 1) return "one"; if (n == 2) return "two"; if (n == 3) return "three"; if (n == 4) return "four"; if (n == 5) return "five"; if (n == 6) return "six"; if (n == 7) return "seven"; if (n == 8) return "eight"; if (n == 9) return "nine"; return ""; }
RTDCS Lab. 38 유사코드 (Pseudocode) 로부터 코딩 (6) /** Turn a number between 10 and 19 into its English n an integer between 10 and the name of n ("ten"... "nineteen") */ string teen_name(int n) { if (n == 10) return "ten"; if (n == 11) return "eleven"; if (n == 12) return "twelve"; if (n == 13) return "thirteen"; if (n == 14) return "fourteen"; if (n == 15) return "fifteen"; if (n == 16) return "sixteen"; if (n == 17) return "seventeen"; if (n == 18) return "eighteen"; if (n == 19) return "nineteen"; return ""; }
RTDCS Lab. 39 유사코드 (Pseudocode) 로부터 코딩 (7) /** Give the English name of a multiple of n an integer between 2 and the name of 10 * n ("twenty"... "ninety") */ string tens_name(int n) { if (n == 2) return "twenty"; if (n == 3) return "thirty"; if (n == 4) return "forty"; if (n == 5) return "fifty"; if (n == 6) return "sixty"; if (n == 7) return "seventy"; if (n == 8) return "eighty"; if (n == 9) return "ninety"; return ""; }
RTDCS Lab. 40 유사코드 (Pseudocode) 로부터 코딩 (8) /** Turn a number into its English n a positive integer < the name of n (e.g. "two hundred seventy four") */ string int_name(int n) { int c = n; /* the part that still needs to be converted */ string r; /* the return value */ if (c >= 1000) { r = int_name(c / 1000) + " thousand"; c = c % 1000; } if (c >= 100) { r = r + " " + digit_name(c / 100) + " hundred"; c = c % 100; } if (c >= 20) { r = r + " " + tens_name(c / 10); c = c % 10; }
RTDCS Lab. 41 유사코드 (Pseudocode) 로부터 코딩 (9) if (c >= 10) { r = r + " " + teen_name(c); c = 0; } if (c > 0) r = r + " " + digit_name(c); return r; } int main() { int n; cout << "Please enter a positive integer: "; cin >> n; cout << int_name(n); return 0; }
RTDCS Lab. 42 유사코드 (Pseudocode) 로부터 코딩 (10) 유사 코드는 자연어 설명보다 읽기 쉽다. 상세한 부분은 생략할 수 있다. int_name 함수는 자신을 호출한다 ( 재귀 함수 ): if (c >= 1000) { r = int_name(c / 1000) + " thousand"; c = c % 1000; }
RTDCS Lab. 43 검토 Walkthroughs(1) 손으로 따라가며 확인하기 함수 이름과 변수를 가진 테이블을 이용하여 값을 추적한 다. 함수가 다른 함수를 호출하는 경우 테이블 추가 int_name (n = 416) cr 416“” int_name (n = 416) cr 416“” digit_name (n=4) Return “four”
RTDCS Lab. 44 검토 Walkthroughs(2)
RTDCS Lab. 45 사전 조건 Preconditions(1) 부적절한 값으로 함수가 호출된다면 (e.g. sqrt(-1) )? 함수의 실패. (digit_name 는 부적절한 값으로 호출되 면 빈 스트링을 반환한다 ). 함수의 종료 (cmath library 의 함수들 ) 오류 메세지를 보내고 프로그램을 종료하는 것은 좋은 방 법이 아니다. C++ 에는 예외 (exception) 라는 좋은 방법이 있다.
RTDCS Lab. 46 사전 조건 Preconditions(2)
RTDCS Lab. 47 사전 조건 Preconditions(3) assert() 사용시 #include 포함할 것 double future_value(double initial_balance, double p, int n) { assert(p >= 0); assert(n >= 0); return initial_balance * pow(1 + p / 100, n); }
RTDCS Lab. 48 Preconditions(4) 프로그래머는 사전 조건에 대한 주석을 달아야 한다. /** Computes the value of an investment with compound initial_balance the initial value of the p the interest rate in percent; must be >= n the number of periods the investment is held; must be >= the balance after n periods */