티스토리 뷰

6.13.2 탑-다운(보텀-업) 구현 (Top-Down and/or Bottom-Up Implementation)

 

이전 포스트 함수 추상화와 단계적 개선(1) 을 보려면 ●클릭하기●

 

*printCalendar 도식화

 

 

- 앞서 분할-정복으로 문제를 세분화 시켰다면, 이제는 그 문제들을 해결할 코드를 구현 할 차례다.

- 일반적으로, 서브 문제(subproblem, 분할-정복 시 생겼던 하위 문제들)들은 함수를 구현한 코드에 상응하지만, 일부는 불필요할 정도로 단순하다.

- 우리는 어떤 모듈로 함수를 구현 할 지, 그리고 어떻게 다른 함수들과 조합할 지 결정해야 한다.

- 이전 포스트 부터 배웠던 방법을 토대로 코드를 구현한다면, 코드를 해석할 때 훨씬 읽기 쉬워진다.

- 이전 포스트에서 예로 들었던 printCalender 프로그램에서의 서브문제(subproblem)였던 readInput은 메인 함수(main)에서 바로 구현이 가능하다. 

 

- 우리는 탑-다운(top-down) 이나 보텀-업(bottom-up) 방식으로 접근이 가능하다.

탑-다운 접근은 한번에 하나 씩 함수를 위에서 아래로 구현하는 것이다.

- 간단하지만 아직 완성되지 않은 함수를 의미하는 스텁(stub)은 지금 만들고 있는 함수를 위해 사용되며, 프로그램의 프레임 워크를 빠르게 구현 할 수 있게 해준다.

- 예를 들어, 먼저 main 함수를 구현할 때, printMonth 함수를 아래와 같이 만드는 것이 스텁(stub)이다.

 

* 스텁 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#printMonth 함수의 스텁
def printMonth(year, month):
    print(year, month)
 
#printMonthTitle 함수 스텁
def printMonthTitle(year, month):
    print("printMonthtitle")
 
#getMonthBody 함수 스텁
def getMonthBody(year, month):
    print("getMonthBody")
 
#getMonthName 함수 스텁
def getMonthName(month) :
    print("getMonthName")
 
#getStartDay 함수 스텁
def getStartDay(year, month):
    print("getStartDay")
 
# getTotalNumberOfDays 함수 스텁
def getTotalNumberOfDays(year, month):
    print("getTotalNumberOfdays")
 
# getNumberOfDaysInMonth함수 스텁
def getNumberOfdaysInMonth(year, month):
    print("getNumberOfDayInMonth")
 
# isLeapYear 함수 스텁
def isLeapYear(year):
    print("isLeapYear")
 
def main():
    #연, 월 입력
    year = eval(input("연도를 입력하세요.  (예, 2001): "))
    month = eval(input("월에 해당하는 숫자를 입력하세요(1-12)"))
 
    #해당 연도의 월 출력
    printMonth(year, month)
    
main()
cs

 

- 위와 같이 스텁(stub) 함수를 작성하면 main함수를 먼저 간단히 테스트해 볼 수 있고, 에러를 고쳐 main함수를 다 완성한 뒤 안정적으로 printMonth함수를 구현 할 수 있다.

 

- 보텀-업(bottom-up)아래에서 위로 한번에 하나씩 함수를 구현하는 것이다.

- 둘 중 어떤 방법을 써도 괜찮으며, 모두 점진적으로 함수를 수행하고 프로그래밍 오류를 분리하고 디버깅을 쉽게 할 수 있도록 도와준다.

 



6.13.3 구현 세부 사항(Implementation Details)

 

- 위 접근 방법을 토대로 이제 코드를 구현하면 된다.

- getTotalNumoberOfDays(year, month) 함수를 예로

■ 1, 3, 5, 7, 8, 10, 12월은 31일을 가지고 있다.

■ 4, 6, 9, 11월은 30일을 가지고 있다.

■ 2월은 평년에는 28일, 윤년에는 29일을 가진다.

 

- 이런 방식으로 각 함수마다 기능들을 특징화시켜 코드를 구현하면 된다.

 

*printCalendar 완성 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 주어진 연도와 월에 대한 달력을 출력한다.
def printMonth(year, month):
    # 달력 머리행을 출력한다.
    printMonthTitle(year, month)
 
    # 달력의 몸체를 출력한다.
    printMonthBody(year, month)
 
# 달력의 제목을 출력한다. 예, May 1999
def printMonthTitle(year, month):
    print("         ", getMonthName(month), " ", year)
    print("-----------------------------")
    print(" Sun Mon Tue Wed Thu Fri Sat")
 
# 달력의 몸체를 출력한다.
def printMonthBody(year, month):
    # 주어진 월의 첫 번째 일에 대한 시작 요일을 구한다.
    startDay = getStartDay(year, month)
 
    # 월의 일수를 구한다.
    numberOfDaysInMonth = getNumberOfDaysInMonth(year, month)
 
    # 월의 첫째 날 앞에 공백을 삽입한다.
    i = 0
    for i in range(startDay):
       print("    ", end = "")
 
    for i in range(1, numberOfDaysInMonth + 1):
        print(format(i, '4d'), end = "")
 
        if (i + startDay) % == 0:
            print() # 새로운 행으로 이동한다.
 
# 월에 대한 영문 이름을 가져온다.
def getMonthName(month):
    if month == 1:
        monthName = "January"
    elif month == 2:
        monthName = "February"
    elif month == 3:
        monthName = "March"
    elif month == 4:
        monthName = "April"
    elif month == 5:
        monthName = "May"
    elif month == 6:
        monthName = "June"
    elif month == 7:
        monthName = "July"
    elif month == 8:
        monthName = "August"
    elif month == 9:
        monthName = "September"
    elif month == 10:
        monthName = "October"
    elif month == 11:
        monthName = "November"
    else:
        monthName = "December"
 
    return monthName
 
#  주어진 년/월/일의 시작 요일을 구한다.
def getStartDay(year, month):
    START_DAY_FOR_JAN_1_1800 = 3
 
    # 1800년 1월 1일부터 주어진 년/월/1까지 총 일수를 구한다.
    totalNumberOfDays = getTotalNumberOfDays(year, month)
 
    # 주어진 년/월/1의 시작 요일을 반환한다.
    return (totalNumberOfDays + START_DAY_FOR_JAN_1_1800) % 7
 
# 1800년 1월 1일부터 총 일수를 계산한다.
def getTotalNumberOfDays(year, month):
    total = 0
 
    # 1800년부터 입력된 년도의 1월 1일까지 모든 일수를 계산한다.
    for i in range(1800, year):
        if isLeapYear(i):
            total = total + 366
        else:
            total = total + 365
 
    # 1월부터 입력된 월의 이전 월까지 모든 일수를 더한다.
    for i in range(1, month):
        total = total + getNumberOfDaysInMonth(year, i)
 
    return total
 
# 해당 월의 총 일수를 구한다.
def getNumberOfDaysInMonth(year, month):
    if (month == or month == or month == or month == or
        month == or month == 10 or month == 12):
        return 31
 
    if month == or month == or month == or month == 11:
        return 30
 
    if month == 2:
        return 29 if isLeapYear(year) else 28
 
    return # 잘못된 월 입력일 경우
 
# 입력된 연도가 윤년인지 결정한다.
def isLeapYear(year):
    return year % 400 == or (year % == and year % 100 != 0)
 
def main():
    # 사용자로부터 년도와 월을 입력받는다.
    year = eval(input("연도를 입력하세요(예, 2001): "))
    month = eval(input(("월에 해당하는 숫자를 입력하세요(1-12): ")))
 
    # 입력 연도와 월에 대한 달력을 출력한다.
    printMonth(year, month)
 
main() # main 함수를 호출한다.
cs

 

6.13.4 단계적 개선의 이점(Benefits of Stepwise Refinement)

 

- 단계적 개선 접근법은 큰 문제점(a large problem)을 작은 문제(subproblem)로 바꾸는 것이다.

- 코드 작성, 재사용, 디버그, 테스트, 유지 보수 등에 유용하다.

 

* 단계적 개선의 이점

■ 프로그램 간소화

printCalendar 프로그램처럼 길이가 긴 프로그램, 또는 대형 프로그램을 작성할 때 유용하다. 큰 문제를 작은 문제로 간소화 시킨 후 프로그램을 작성하는 것이기 때문에, 전체 프로그램을 작성하기도, 나중에 해석하기도 쉬워진다.

 

■ 함수 재사용

단계적 개선법은 코드의 재사용을 촉진시킨다. 예를 들어 isLeapYear 함수는 getTotalNumberOfDays와 getNumberOfDasInMonth 함수에서 호출해 사용하는 함수이다. 코드의 중복을 줄일 수 있다.

 

■ 개발 과정, 디버깅 과정, 테스트 과정을 줄일 수 있다.

탑-다운이나 보텀-업 접근을 이용하여 디버깅 과정과 테스트 과정 등을 줄일 수 있다. 지금 당장에는 작은 프로그램을 구현하기 때문에 효과를 잘 느끼지 못할 지라도, 나중에 대형 프로그램을 만들 때는 확실히 시간과 디버깅 횟수 낭비를 줄일 수 있다.

 

■ 더 효율적인 팀워크를 제공한다.

단계적 개선은 큰 문제를 작은 문제로 나누는 과정이기 때문에, 이 나뉘어진 작은 문제들을 프로그래머들에게 할당하기 매우 쉬워진다.

 

참고 문헌 : Introduction to Programming Using Python / Y.DANIEL LIANG



본 게시물은 개인적인 용도로 작성된 게시물입니다. 이후 포트폴리오로 사용될 정리 자료이니 불펌과 무단도용은 하지 말아주시고 개인 공부 목적으로만 이용해주시기 바랍니다.


교재 영어 원서를 직접 번역하여 정리한 게시물이므로 일부 오타, 의역이 존재할 수 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사하겠습니다. 

 

댓글