{ }codelper

배우기 › Python

더 나아가기 · 24

세기, 누적 계산, 묶기

한 번 훑으면서 조건에 맞는 것을 세고, 지금까지의 값을 들고 다니고, 열쇠별로 묶어요.

세기, 누적, 묶기는 자료를 한 번 훑으면서 답을 만들어 가는 세 가지 기본 꼴이에요.
아래 예제는 점수를 하나씩 보면서 지금까지의 합과 최고 점수를 찍고, 80점 이상이 몇 개인지 세고, 점수를 60점대·70점대처럼 십의 자리로 묶어 찍어요.

scores = list(map(int, input().split()))count = 0          # 80점 이상인 점수의 개수total = 0          # 지금까지의 합best = scores[0]   # 지금까지의 최고 점수groups = {}        # 십의 자리(60, 70, …) → 그 묶음의 점수들for s in scores:    if s >= 80:        count += 1    total += s    if s > best:        best = s    print(total, best)    key = s // 10 * 10    if key not in groups:        groups[key] = []      # 처음 보는 열쇠면 빈 목록부터    groups[key].append(s)print(count)for key in sorted(groups):    print(f"{key}:", *groups[key])
stdin
72 85 90 68 85 77 93
stdout
72 72
157 85
247 90
315 90
400 90
477 90
570 93
4
60: 68
70: 72 77
80: 85 85
90: 90 93

핵심

  • 세기는 0에서 시작해서 조건에 맞을 때만 1을 더해요.
    합계와 달리 값이 아니라 1을 더해요.
  • 누적 값은 하나를 볼 때마다 고쳐 쓰는 변수예요.
    지금까지의 합(total), 지금까지의 최고(best)처럼 '여기까지의 답'을 늘 들고 있어서, 중간 결과를 그 자리에서 찍을 수 있어요.
    반복문 단원에서 본 total += i가 바로 이 꼴이에요.
  • 누적 변수는 반복 앞에서 만들어요.
    개수와 합은 0에서, 최고·최저는 첫 값에서 시작해요.
  • 묶기는 값마다 열쇠를 정하고(예제는 68 → 60, 72 → 70), 사전에서 그 열쇠의 목록에 값을 넣어요.
    세는 사전(사전 단원)은 값이 수였고, 묶는 사전은 값이 목록이에요.
  • 처음 보는 열쇠에는 아직 목록이 없어요.
    빈 목록을 먼저 만들어 넣고, 그다음에 값을 넣어요.
  • 세 가지를 따로 돌 필요는 없어요.
    예제는 반복문 하나에서 세기·누적·묶기를 함께 해요.

언어별로 쓰는 법

Python세기 count += 1(한 줄로는 sum(1 for s in scores if s >= 80)), 누적 total += s.
묶기는 예제처럼 if key not in groups: groups[key] = [] 다음에 groups[key].append(s), 짧게는 groups.setdefault(key, []).append(s)나 collections.defaultdict(list)
JavaScript세기 count++(한 줄로는 scores.filter((s) => s >= 80).length), 누적 total += s.
묶기는 Map에 if (!groups.has(key)) groups.set(key, []); 다음에 groups.get(key).push(s)
TypeScriptJavaScript와 같고 new Map<number, number[]>()처럼 타입을 적어요.
groups.get(key)는 undefined일 수도 있는 타입이라 바로 .push하면 타입 오류(TS2532)예요.
예제처럼 꺼낸 값을 변수에 받아 undefined인지 확인해요
Java세기 count++(한 줄로는 scores.stream().filter(s -> s >= 80).count()), 누적 total += s.
묶기는 Map<Integer, List<Integer>>에 containsKey로 확인하고 put(key, new ArrayList<>()), 짧게는 groups.computeIfAbsent(key, k -> new ArrayList<>()).add(s).
TreeMap이면 열쇠 순으로 돌아요
C#세기 count++(LINQ로는 scores.Count(s => s >= 80)), 누적 total += s.
묶기는 Dictionary<int, List<int>>에 if (!groups.ContainsKey(key)) groups[key] = new List<int>(); 다음에 groups[key].Add(s).
SortedDictionary면 열쇠 순으로 돌아요
C++세기 count++(<algorithm>의 std::count_if도 있어요), 누적 total += s.
묶기는 std::map<int, std::vector<int>>에 groups[key].push_back(s) 한 줄이에요.
없는 열쇠면 []가 빈 vector를 만들어 줘요
C세기와 누적은 다른 언어와 같아요(count++, total += s).
사전이 없어서 묶기는 손으로 해요.
열쇠가 작은 정수면 예제처럼 2차원 배열에 groups[key][sizes[key]++] = s로 넣고, 묶음마다 개수(sizes)를 따로 세요
Craykon세기 count++, 누적 total += s.
묶기는 사전()에 만약 (!groups.있나(key)) { groups[key] = []; } 다음에 groups[key].넣기(s).
열쇠 순으로 찍으려면 groups.열쇠목록을 정렬()해서 돌아요

자주 하는 실수

+=를 =+로 썼어요

int count = 0;
int scores[] = {72, 85, 90};
for (int i = 0; i < 3; i++)
    if (scores[i] >= 80) count =+ 1;
printf("%d\n", count);

→ 1 — 오류 없이 돌지만 2가 아니에요. count =+ 1은 count = +1, 곧 "1을 넣어라"예요

count += 1로 써요.
개수가 늘 1로 나오면 이 글자 순서부터 확인해요.

누적 변수를 반복 안에서 0으로 만들었어요

const scores = [72, 85, 90];
for (const s of scores) {
  let total = 0;
  total += s;
  console.log(total);
}

→ 72, 85, 90 — 바퀴마다 0으로 돌아가서 합이 쌓이지 않아요

total의 시작 값(0)은 반복 앞에서 한 번만 주고, 반복 안에서는 더하기만 해요.
let·int 같은 만드는 낱말이 없는 Python과 크레이콘도 total = 0이 반복 안에 있으면 똑같이 72, 85, 90이 나와요.

빈 목록을 만들지 않고 묶음에 넣었어요Java

Map<Integer, List<Integer>> groups = new TreeMap<>();
int s = 68;
int key = s / 10 * 10;
groups.get(key).add(s);

→ NullPointerException — 없는 열쇠라서 get이 null을 돌려줬어요

먼저 if (!groups.containsKey(key)) groups.put(key, new ArrayList<>());를 하거나, groups.computeIfAbsent(key, k -> new ArrayList<>()).add(s)로 써요.

언어마다 다른 점
묶음마다 목록을 따로 만들어야 해요.
Python에서 groups = dict.fromkeys([60, 70], [])로 열쇠를 미리 만들어 두면 모든 열쇠가 같은 목록 하나를 함께 써서, 68과 72를 따로 넣어도 {60: [68, 72], 70: [68, 72]}가 돼요.
예제처럼 처음 보는 열쇠마다 새 []를 만들어요.

연습 문제