완료·실패·도움 요청 집계
150분 안팎
학습 목표
빈 자료와 완료 누락을 처리합니다.
개념
숫자보다 분류의 의미가 먼저입니다
사용성 결과표에 성공 2건이라고 쓰면 도움을 받고 끝낸 P03까지 포함했는지 독립 완료만 센 것인지 알 수 없습니다. 이번 연습은 전체 과업 기록 수와 독립 성공·실패·도움 후 완료·판정 불가를 분리합니다. 각 건수는 참여자 전체의 만족도나 실제 사용자 모집단의 성공률을 뜻하지 않습니다. 같은 과업 T01과 초안 v1의 기록을 먼저 모아 분류 기준을 적용하고 요약 옆에 관찰 조건과 자료 한계를 적습니다.
입력 한 줄은 한 과업 시도입니다
브라우저 실습의 표준 입력은 JSON 배열입니다. 각 객체는 outcome과 helped를 가지며 한 참여자의 한 과업 시도를 나타냅니다. outcome은 success, failure, assisted 또는 null을 사용하고 helped는 true 또는 false입니다. 빈 입력과 빈 배열은 기록 0건으로 처리합니다. 목록이 아닌 JSON, JSON 문법 오류, 객체가 아닌 항목 또는 helped의 잘못된 형식은 ERROR를 출력합니다. 데이터 오류를 0건처럼 숨기지 않습니다.
완료 누락은 unknown으로 남깁니다
outcome 키가 없거나 값이 null이면 unknown에 한 건을 더합니다. 실패 기록이 없다고 성공으로 간주하지 않습니다. 반대로 failed라는 잘못된 철자는 누락으로 처리하지 않고 ERROR로 거절합니다. 알 수 없는 상태를 허용하면 입력 작성자의 오타가 판정 불가 자료로 섞일 수 있습니다. unknown은 관찰의 결과가 기록되지 않았다는 명시적 상태이고 오류는 약속한 입력 형식을 어겼다는 신호라는 차이를 기억합니다.
도움은 결과에 따라 해석합니다
success라도 helped가 true이면 assisted로 집계합니다. 도움 뒤 목적을 달성한 결과를 독립 성공에서 제외하기 위해서입니다. failure에 helped가 true라면 여전히 failure입니다. 도움을 주었지만 완료하지 못한 사례를 도움 후 완료로 바꾸지 않습니다. assisted라는 입력은 helped가 true일 때만 유효합니다. helped가 false인데 assisted라면 서로 모순되므로 ERROR입니다. 도움 요청 횟수 자체는 이 네 분류의 집계 대상이 아닙니다.
출력 순서를 고정합니다
출력은 TOTAL SUCCESS FAILURE ASSISTED UNKNOWN 순서의 정수 다섯 개를 공백으로 나눈 한 줄입니다. 항목 이름은 출력하지 않고 숫자만 출력합니다. 예를 들어 독립 완료 한 건·실패 한 건·도움 후 완료 한 건·누락 한 건이면 전체는 네 건입니다. 비율과 소수점 계산은 이번 실습에서 하지 않습니다. 자료가 없을 때 성공률 0이라고 해석하는 대신 건수 전부가 0이라는 사실만 보고하고 판단 가능한 사례가 없다는 한계를 씁니다.
반복 전에 입력을 해석합니다
sys.stdin.read()는 전체 입력을 문자열로 읽습니다. strip()으로 공백만 있는지 확인하고 비어 있으면 빈 목록으로 둡니다. 값이 있으면 json.loads로 JSON을 해석합니다. JSON의 true와 null은 해석 뒤 Python의 True와 None이 됩니다. Python 코드에서 true를 직접 쓰면 이름을 찾지 못하는 오류가 생깁니다. JSON 입력과 Python 표현은 비슷해 보여도 다른 문법이므로 입력을 해석하는 단계를 생략하지 않습니다.
초기값과 반복을 연결합니다
전체 수는 목록 길이로 구하고 분류별 수는 0에서 시작합니다. for 반복은 객체를 하나씩 읽습니다. 먼저 객체 형식과 helped의 자료형을 확인한 뒤 outcome에 맞는 분류를 결정합니다. 선택한 분류의 값에 1을 더하며 누적합니다. 모든 분기를 읽고 한 건이 두 분류에 들어가지 않는지 확인합니다. 서로 독립적인 if를 여러 개 쓰면 같은 기록을 성공과 도움 후 완료에 중복 추가할 수 있어 분류를 하나로 결정하는 방식이 편합니다.
도움 뒤 완료의 조건을 먼저 읽습니다
outcome이 success라면 helped를 확인해 success 또는 assisted를 고릅니다. outcome이 assisted이면 helped가 true인지 검증합니다. failure는 도움 여부와 관계없이 failure이고 누락은 unknown입니다. 이 순서를 글로 먼저 설명하고 코드로 옮기면 “도움이 있으면 모두 assisted” 같은 실수를 줄일 수 있습니다. 판정 규칙을 새로 바꾸는 문제가 아니라 이미 합의한 결과 구분을 코드로 같은 방식으로 적용하는 문제입니다.
전체와 분류 합계를 대조합니다
유효한 입력에서는 네 분류의 합이 TOTAL과 같아야 합니다. 합이 작으면 어떤 분기가 기록을 빠뜨렸을 수 있고 합이 크면 중복 집계 가능성이 있습니다. 합계가 맞더라도 분류가 옳다는 보장은 없습니다. 성공과 실패를 바꾸어 세도 전체 수는 같습니다. 그래서 경계 사례를 따로 넣어 성공 도움 있음, 도움 있지만 실패, 완료 누락, 빈 배열을 각각 확인합니다. 총합 검사와 의미별 사례 검사를 함께 사용하는 이유입니다.
오류 메시지와 계산 결과를 나눕니다
ERROR는 사용자가 과업을 실패했다는 뜻이 아니라 집계 입력의 형식이 잘못되었다는 뜻입니다. JSONDecodeError는 쉼표·따옴표 같은 JSON 문법부터 점검합니다. TypeError나 KeyError가 화면에 그대로 보이면 형식 확인보다 필드 사용이 앞섰는지 살펴봅니다. 브라우저 제출 코드는 예상된 입력 오류를 잡아 ERROR 한 줄로 끝냅니다. 예상하지 못한 오류를 전부 숨기는 대신 무엇을 거절할지 명세에 적은 종류만 처리합니다.
도움 기록은 단순 불리언으로 축약합니다
미션의 관찰에는 none·repeat·direction·meaning이 있지만 이 집계 연습에서는 방향·의미 도움 여부만 helped로 옮깁니다. 질문을 했거나 과업 문장을 반복한 사실만으로 true를 쓰지 않습니다. 어떤 사건이 실제 도움 제공인지 원본 관찰을 읽고 변환합니다. 변환 규칙을 기록하지 않으면 같은 P03을 서로 다르게 집계할 수 있습니다. 자동 계산은 사람이 원자료를 정확히 분류하는 책임을 대신하지 않습니다.
여러 과업과 여러 버전을 섞지 않습니다
한 참여자가 T01과 T02를 수행했다면 기록은 두 건이지만 사람은 한 명입니다. 이번 TOTAL은 시도 수이므로 결과표에 참여자 수라고 쓰면 안 됩니다. v2 재확인도 v1 집계에 더하지 않습니다. 수정 전후 조건이 달라지므로 버전별 표를 만들어야 합니다. 제공 미션에서는 v1 세션이 세 건이고 v2 재확인은 별도 목록입니다. 비교 해석과 지표 대상 정의는 다음 모듈에서 더 다루며 여기서는 섞이지 않는 원자료를 준비합니다.
숫자 요약 옆에 근거를 연결합니다
v1 요약은 전체 3건, 독립 성공 1건, 실패 1건, 도움 후 완료 1건입니다. 실패 이유는 O01·O02의 상태 의미 오해, 도움 이유는 O04·O05의 경로 안내입니다. 같은 숫자가 나오는 다른 문제를 모두 같은 수정으로 해결할 수는 없습니다. summary.limits에 가상 자료라는 출처와 작은 범위를 쓰고 수정에는 숫자보다 사건 ID를 연결합니다. 빈 집계도 오류 없이 계산되지만 문제 없음을 증명한 것은 아닙니다.
검산 가능한 보고로 마칩니다
코드 출력과 사람이 센 표를 대조하고 불일치가 있다면 먼저 원자료의 outcome과 helped를 읽습니다. 원하는 결론에 맞추어 기대 출력을 바꾸지 않습니다. 누락 한 건을 추가하면 전체와 unknown만 증가하는지, 도움 없는 성공을 추가하면 전체와 success만 증가하는지 확인합니다. 실습을 끝냈다는 것은 코드를 실행했다는 뜻을 넘어 입력 계약·분류 기준·숫자의 의미·자료 한계를 동료에게 설명할 수 있다는 뜻입니다.
따라하기
한 번씩 분류합니다
다음 독립 예제를 Python 3으로 실행합니다. 마지막 None은 결과 누락을 뜻합니다.
records = ["success", "failure", "assisted", None]
print(len(records))
print(records.count("success"), records.count("failure"), records.count("assisted"), records.count(None))
실행 결과
4 1 1 1 1
도움 뒤 완료를 나눕니다
완료 기록에 실제 도움이 있으면 독립 성공 대신 도움 후 완료로 분류합니다.
outcome = "success"
helped = True
category = "assisted" if outcome == "success" and helped else outcome
print(category)
실행 결과
assisted
합계와 빈 자료를 검산합니다
합계 검산은 분류 의미까지 보장하지 않습니다. 빈 자료는 비율을 추정하지 않고 다섯 건수를 0으로 씁니다.
counts = [1, 1, 1, 1]
total = 4
print(sum(counts) == total)
print(0, 0, 0, 0, 0)
실행 결과
True 0 0 0 0 0
입력 계약을 구현합니다
브라우저에 테스트 첫 JSON 입력을 넣고 TOTAL SUCCESS FAILURE ASSISTED UNKNOWN의 기대 건수를 종이에 먼저 셉니다. starter의 TODO를 완성하고 빈 입력·도움 후 실패·누락·오류를 검사합니다. 미션 summary에는 v1 세션만 세고 rechecks는 분리합니다.
확인 문제
실습
표준 입력 JSON 배열의 시도들을 집계하여 TOTAL SUCCESS FAILURE ASSISTED UNKNOWN 순서로 정수 다섯 개를 출력합니다. 각 객체의 helped는 불리언이며 outcome은 success/failure/assisted/null 또는 키 누락입니다. success에 helped=true는 assisted, failure는 도움 여부와 무관하게 failure, 누락/null은 unknown입니다. assisted에 helped=false는 ERROR입니다. 빈 입력·빈 배열은 0 0 0 0 0입니다. JSON 문법 오류·목록 아닌 값·객체 아닌 항목·도움 형식 오류·알 수 없는 outcome은 ERROR 한 줄을 출력합니다. 한 시도는 한 분류에만 포함합니다.
모범 답안
import json
import sys
def summarize(raw):
rows = json.loads(raw) if raw.strip() else []
if not isinstance(rows, list):
raise ValueError("array required")
counts = {key: 0 for key in ("success", "failure", "assisted", "unknown")}
for row in rows:
if not isinstance(row, dict) or type(row.get("helped")) is not bool:
raise ValueError("invalid record")
outcome = row.get("outcome")
if outcome is None:
key = "unknown"
elif outcome == "success":
key = "assisted" if row["helped"] else "success"
elif outcome == "failure":
key = "failure"
elif outcome == "assisted" and row["helped"]:
key = "assisted"
else:
raise ValueError("invalid outcome")
counts[key] += 1
return [len(rows), counts["success"], counts["failure"], counts["assisted"], counts["unknown"]]
try:
print(*summarize(sys.stdin.read()))
except (json.JSONDecodeError, ValueError):
print("ERROR")
더 읽기
면접 질문
- 개발자와 같은 결과를 떠올릴 수 있는 명세를 설명해 주시면 됩니다.