트랙
/
Python
Python
/
연습 문제
/
성적 매기기
성적 매기기

성적 매기기

학습 연습 문제

소개

Python에는 두 가지 반복 구조가 있어요. while문은 무한(횟수를 세지 않는) 반복에, for문은 유한(횟수를 세는) 반복에 사용해요. break, continue, else 키워드를 사용하면 루프의 동작을 세밀하게 조정할 수 있어요. range()와 enumerate()는 반복 횟수 세기와 인덱스 다루기를 도와줘요.

while문

while문은 loop expression, 즉 "테스트"가 boolean context에서 True로 평가되는 동안 계속 실행되고, False로 평가되면 종료돼요:


# Lists are considered "truthy" in a boolean context if they
# contain one or more values, and "falsy" if they are empty.

>>> placeholders = ["spam", "ham", "eggs", "green_spam", "green_ham", "green_eggs"]

>>> while placeholders:
...     print(placeholders.pop(0))
...
'spam'
'ham'
'eggs'
'green_spam'
'green_ham'
'green_eggs'

for문

Python의 기본 for loop는 _for each_라고 부르는 편이 더 정확해요. 이터러블 객체의 값들을 차례로 순회하다가, next()를 호출해도 더 이상 값이 반환되지 않으면 종료되지요:


>>> word_list = ["bird", "chicken", "barrel", "bongo"]

>>> for word in word_list:
...    if word.startswith("b"):
...        print(f"{word.title()} starts with a B.")
...    else:
...        print(f"{word.title()} doesn't start with a B.")
...
'Bird starts with a B.'
'Chicken doesn\'t start with a B.'
'Barrel starts with a B.'
'Bongo starts with a B.'

시퀀스 객체 range()

특정한 iterable이 주어지지 않으면, 특별한 range() 시퀀스가 루프 카운터로 사용돼요. range()는 시퀀스를 stop할 int를 하나 받아야 하고, 선택적으로 start와 step 매개변수를 받을 수 있어요. start 숫자를 주지 않으면 시퀀스는 0부터 시작해요. range() 객체는 지연 평가 방식이라 (값은 요청할 때 생성돼요) 일반 시퀀스 연산을 모두 지원하고, 지정한 시퀀스가 아무리 길어도 메모리를 일정량만 차지해요.

# Here we use range to produce some numbers, rather than creating a list of them in memory.
# The values will start with 1 and stop *before* 7

>>> for number in range(1, 7):
...    if number % 2 == 0:
...       print(f"{number} is even.")
...    else:
...       print(f"{number} is odd.")
'1 is odd.'
'2 is even.'
'3 is odd.'
'4 is even.'
'5 is odd.'
'6 is even.'

# range() can also take a *step* parameter.
# Here we use range to produce only the "odd" numbers, starting with 3 and stopping *before* 15.

>>> for number in range(3, 15, 2):
...    if number % 2 == 0:
...       print(f"{number} is even.")
...    else:
...       print(f"{number} is odd.")
...
'3 is odd.'
'5 is odd.'
'7 is odd.'
'9 is odd.'
'11 is odd.'
'13 is odd.'

enumerate()로 값과 인덱스 얻기

값과 그 인덱스가 모두 필요하면, 내장 함수 enumerate(<iterable>)가 (index, value) 쌍을 반환해요:


>>> word_list = ["bird", "chicken", "barrel", "apple"]

# *index* and *word* are the loop variables.
# Loop variables can be any valid python name.

>>> for index, word in enumerate(word_list):
...    if word.startswith("b"):
...        print(f"{word.title()} (at index {index}) starts with a B.")
...    else:
...        print(f"{word.title()} (at index {index}) doesn't start with a B.")
...
'Bird (at index 0) starts with a B.'
'Chicken (at index 1) doesn\'t start with a B.'
'Barrel (at index 2) starts with a B.'
'Apple (at index 3) doesn\'t start with a B.'


# The same method can be used as a "lookup" for pairing items between two lists.
# Of course, if the lengths or indexes don't line up, this doesn't work.

>>> word_list = ["cat", "chicken", "barrel", "apple", "spinach"]
>>> category_list = ["mammal", "bird", "thing", "fruit", "vegetable"]

>>> for index, word in enumerate(word_list):
...    print(f"{word.title()} is in category: {category_list[index]}.")
...
'Cat is in category: mammal.'
'Chicken is in category: bird.'
'Barrel is in category: thing.'
'Apple is in category: fruit.'
'Spinach is in category: vegetable.'

루프 동작 바꾸기

continue 키워드를 사용하면 다음 반복 주기로 건너뛸 수 있어요:

word_list = ["bird", "chicken", "barrel", "bongo", "sliver", "apple", "bear"]

# This will skip *bird*, at index 0
for index, word in enumerate(word_list):
    if index == 0:
        continue
    if word.startswith("b"):
        print(f"{word.title()} (at index {index}) starts with a b.")

'Barrel (at index 2) starts with a b.'
'Bongo (at index 3) starts with a b.'
'Bear (at index 6) starts with a b.'

break 키워드는 반복을 중단하고 가장 안쪽 loop에서 "빠져나오는" 데 사용할 수 있어요 (C 계열 언어에서처럼요):

>>>  word_list = ["bird", "chicken", "barrel", "bongo", "sliver", "apple"]

>>> for index, word in enumerate(word_list):
...    if word.startswith("b"):
...        print(f"{word.title()} (at index {index}) starts with a B.")
...    elif word == "sliver":
...       break
...    else:
...       print(f"{word.title()} doesn't start with a B.")
... print("loop broken.")
...
'Bird (at index 0) starts with a B.'
'Chicken doesn\'t start with a B.'
'Barrel (at index 2) starts with a B.'
'Bongo (at index 3) starts with a B.'
'loop broken.'

지침

학생들의 시험을 채점하는 조교로 일하고 있어요. 결과를 손으로 기록하는 건 점점 지루해지고 실수하기도 쉬워져요. 그래서 반 전체의 결과를 세고 계산하는 함수 몇 개를 만들어서, 일을 조금 더 재미있게 해보기로 해요.

1. 점수 반올림

시험 문제에는 "부분 점수"를 줄 수 있지만, 전체 시험 점수는 int여야 해요. 그래서 반 점수로 다른 작업을 하기 전에, 먼저 성적을 훑으면서 float 점수를 모두 int로 바꿔야 해요. 다행히 Python에는 사용할 수 있는 내장 round() 함수가 있어요.

student_scores라는 list를 매개변수로 받는 round_scores(student_scores) 함수를 만들어요. 이 함수는 입력 list를 _소비_하고, 모든 점수를 int로 변환한 새 배열을 return해야 해요. 결과 list에서 점수의 순서는 중요하지 않아요.

>>> student_scores = [90.33, 40.5, 55.44, 70.05, 30.55, 25.45, 80.45, 95.3, 38.7, 40.3]
>>> round_scores(student_scores)
...
[40, 39, 95, 80, 25, 31, 70, 55, 40, 90]

2. 불합격한 학생

시험을 채점하다 보니, 기대만큼 성적이 좋지 않은 학생들이 눈에 띄었어요. 그런데 다른 데 정신이 팔려서, 학생이 _몇 명_인지 정확히 적어 두는 걸 깜빡했어요.

student_scores라는 list를 매개변수로 받는 count_failed_students(student_scores) 함수를 만들어요. 이 함수는 합격 점수를 받지 못한 학생 수를 세고, 그 수를 정수로 반환해야 해요. 시험에서 합격하려면 40보다 높은 점수를 받아야 해요.

>>> count_failed_students(student_scores=[90,40,55,70,30,25,80,95,38,40])
5

3. "최고"

함께 일하는 선생님은 이번 시험에서 "가장 잘한" 학생들을 찾고 싶어 해요. "가장 잘한"의 기준은 매번 달라지기 때문에, 현재 기준값 이상인 점수를 찾아야 해요.

student_scores(성적의 list)와 threshold("최고 점수" 기준)를 매개변수로 받는 above_threshold(student_scores, threshold) 함수를 만들어요. 이 함수는 threshold보다 크거나 같은(>=) 모든 점수의 list를 반환해야 해요.

>>> above_threshold(student_scores=[90,40,55,70,30,68,70,75,83,96], threshold=75)
[90,75,83,96]

4. 알파벳 등급 계산

함께 일하는 선생님은 숫자 점수뿐만 아니라 알파벳 등급도 매기고 싶어 해요. 학생들이 시험에서 100점을 받는 일은 드물기 때문에, "알파벳 등급"의 하한 기준은 최고 점수를 바탕으로 계산하고, 최고 점수와 불합격 기준인 <= 40 사이를 균등하게 나눠요.

시험의 "최고" 점수를 인자로 받고, "미국식" 등급 구간 ["D", "C", "B", "A"] 각각의 하한 점수 list를 반환하는 letter_grades(highest) 함수를 만들어요.

"""Where the highest score is 100, and failing is <= 40.
       "F" <= 40
 41 <= "D" <= 55
 56 <= "C" <= 70
 71 <= "B" <= 85
 86 <= "A" <= 100
"""

>>> letter_grades(highest=100)
[41, 56, 71, 86]


"""Where the highest score is 88, and failing is <= 40.
       "F" <= 40
 41 <= "D" <= 52
 53 <= "C" <= 64
 65 <= "B" <= 76
 77 <= "A" <= 88
"""

>>> letter_grades(highest=88)
[41, 53, 65, 77]

5. 이름과 점수 연결하기

내림차순으로 정렬된 시험 점수 배열과, 시험 점수를 기준으로 역시 내림차순으로 정렬된 학생 이름 배열이 있어요. 각 학생 이름을 시험 점수와 짝지어서, 반 전체 순위를 출력하고 싶어요.

student_scores와 student_names를 매개변수로 받는 student_ranking(student_scores, student_names) 함수를 만들어요. student_names list의 각 학생 이름을 student_scores list의 점수와 짝지어요. 각 인자 list는 가장 높은 점수(를 받은 사람)부터 가장 낮은 점수(를 받은 사람) 순으로 정렬되어 있다고 가정해도 돼요. 이 함수는 <rank>. <student name>: <student score> 형식의 문자열 list를 반환해야 해요.

>>> student_scores = [100, 99, 90, 84, 66, 53, 47]
>>> student_names =  ['Joci', 'Sara','Kora','Jan','John','Bern', 'Fred']
>>> student_ranking(student_scores, student_names)
...
['1. Joci: 100', '2. Sara: 99', '3. Kora: 90', '4. Jan: 84', '5. John: 66', '6. Bern: 53', '7. Fred: 47']

6. "완벽한" 점수

시험에서 100점이라는 "완벽한" 점수를 받는 일은 드물지만, 적어도 한 명의 학생이 그것을 달성했는지 아는 건 흥미로워요.

student_info를 매개변수로 받는 perfect_score(student_info) 함수를 만들어요. student_info는 각 학생의 이름과 점수를 담은 list의 list예요: [["Charles", 90], ["Tony", 80]]. 이 함수는 시험에서 100점을 받은 학생의 첫 번째 [<name>, <score>] 쌍을 return해야 해요.

student_info에서 100점이 없으면, 빈 list []를 반환해야 해요.

>>> perfect_score(student_info=[["Charles", 90], ["Tony", 80], ["Alex", 100]])
["Alex", 100]

>>> perfect_score(student_info=[["Charles", 90], ["Tony", 80]])
[]
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Python Exercism

성적 매기기 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Python 트랙을 개념 17개연습 문제 146개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.