더 나아가기 · 25
자료 목록과 열거형
묶음 자료를 목록에 모으고, 정해진 몇 가지 값에는 이름을 붙여요.
이름과 점수처럼 여러 값을 하나로 묶은 묶음 자료를 목록에 차곡차곡 담으면 표 한 장 같은 자료 목록이 돼요.
그리고 "낮음·중간·높음"처럼 정해진 몇 가지 중 하나인 값에는 이름을 붙여 둬요(열거형, enum).
아래 예제는 학생의 이름과 점수를 읽어 등급을 매긴 묶음 자료를 목록에 모으고, 한 줄씩 출력한 다음 등급이 HIGH인 학생의 이름만 골라 출력해요.
from enum import Enum class Level(Enum): # 정해진 세 가지 중 하나 LOW = 0 MID = 1 HIGH = 2 def level_of(score): if score >= 80: return Level.HIGH if score >= 60: return Level.MID return Level.LOW n = int(input())students = [] # 묶음 자료(이름, 점수, 등급)의 목록for _ in range(n): name, score = input().split() score = int(score) students.append({"name": name, "score": score, "level": level_of(score)}) for s in students: print(s["name"], s["score"], s["level"].name) high = []for s in students: if s["level"] == Level.HIGH: high.append(s["name"])print("HIGH:", *high)// enum이 없어서, 바꿀 수 없게 얼린 객체에 이름을 모아 둬요const Level = Object.freeze({ LOW: "LOW", MID: "MID", HIGH: "HIGH" }); function levelOf(score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW;} const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");const n = Number(lines[0]);const students = []; // 묶음 자료(이름, 점수, 등급)의 목록for (let i = 1; i <= n; i++) { const [name, scoreText] = lines[i].trim().split(" "); const score = Number(scoreText); students.push({ name: name, score: score, level: levelOf(score) });} for (const s of students) { console.log(s.name + " " + s.score + " " + s.level);} const high = [];for (const s of students) { if (s.level === Level.HIGH) high.push(s.name);}console.log("HIGH: " + high.join(" "));import { readFileSync } from "node:fs"; enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나(속은 0, 1, 2) type Student = { name: string; score: number; level: Level }; function levelOf(score: number): Level { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW;} const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const n: number = Number(lines[0]);const students: Student[] = []; // 묶음 자료(이름, 점수, 등급)의 목록for (let i = 1; i <= n; i++) { const [name, scoreText] = lines[i].trim().split(" "); const score = Number(scoreText); students.push({ name: name, score: score, level: levelOf(score) });} for (const s of students) { console.log(s.name + " " + s.score + " " + Level[s.level]); // Level[2]는 이름 "HIGH"} const high: string[] = [];for (const s of students) { if (s.level === Level.HIGH) high.push(s.name);}console.log("HIGH: " + high.join(" "));import java.util.*; public class Main { enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나 record Student(String name, int score, Level level) {} static Level levelOf(int score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW; } public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); List<Student> students = new ArrayList<>(); // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { String name = in.next(); int score = in.nextInt(); students.add(new Student(name, score, levelOf(score))); } for (Student s : students) { System.out.println(s.name() + " " + s.score() + " " + s.level()); } List<String> high = new ArrayList<>(); for (Student s : students) { if (s.level() == Level.HIGH) high.add(s.name()); } System.out.println("HIGH: " + String.join(" ", high)); }}using System;using System.Collections.Generic; enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나 record Student(string Name, int Score, Level Level); class Program{ static Level LevelOf(int score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW; } static void Main() { int n = int.Parse(Console.ReadLine()); var students = new List<Student>(); // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { string[] parts = Console.ReadLine().Split(); int score = int.Parse(parts[1]); students.Add(new Student(parts[0], score, LevelOf(score))); } foreach (Student s in students) { Console.WriteLine(s.Name + " " + s.Score + " " + s.Level); } var high = new List<string>(); foreach (Student s in students) { if (s.Level == Level.HIGH) high.Add(s.Name); } Console.WriteLine("HIGH: " + string.Join(" ", high)); }}#include <iostream>#include <string>#include <vector> enum class Level { LOW, MID, HIGH }; // 정해진 세 가지 중 하나const char* LEVEL_NAMES[] = {"LOW", "MID", "HIGH"}; // 이름은 따로 적어 둬요 struct Student { std::string name; int score; Level level;}; Level levelOf(int score) { if (score >= 80) return Level::HIGH; if (score >= 60) return Level::MID; return Level::LOW;} int main() { int n; std::cin >> n; std::vector<Student> students; // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { std::string name; int score; std::cin >> name >> score; students.push_back({name, score, levelOf(score)}); } for (const Student& s : students) { std::cout << s.name << ' ' << s.score << ' ' << LEVEL_NAMES[static_cast<int>(s.level)] << '\n'; } std::cout << "HIGH:"; for (const Student& s : students) { if (s.level == Level::HIGH) std::cout << ' ' << s.name; } std::cout << '\n';}#include <stdio.h> typedef enum { LOW, MID, HIGH } Level; // 정해진 세 가지 중 하나(속은 0, 1, 2)const char *LEVEL_NAMES[] = {"LOW", "MID", "HIGH"}; // 이름은 따로 적어 둬요 typedef struct { char name[32]; int score; Level level;} Student; Level level_of(int score) { if (score >= 80) return HIGH; if (score >= 60) return MID; return LOW;} int main(void) { int n; scanf("%d", &n); Student students[100]; // 묶음 자료(이름, 점수, 등급)의 배열 for (int i = 0; i < n; i++) { scanf("%31s %d", students[i].name, &students[i].score); students[i].level = level_of(students[i].score); } for (int i = 0; i < n; i++) { printf("%s %d %s\n", students[i].name, students[i].score, LEVEL_NAMES[students[i].level]); } printf("HIGH:"); for (int i = 0; i < n; i++) { if (students[i].level == HIGH) printf(" %s", students[i].name); } printf("\n"); return 0;}// enum이 없어서, 이름 붙인 상수로 정해요(바꾸지 않기로 약속한 변수)LOW = 0;MID = 1;HIGH = 2;LEVEL_NAMES = ["LOW", "MID", "HIGH"]; levelOf(score){ 만약 (score >= 80) { 돌려주기 HIGH; } 만약 (score >= 60) { 돌려주기 MID; } 돌려주기 LOW;} n = 콘솔.줄읽기().수로변환();students = []; // 묶음 자료(이름, 점수, 등급)의 목록반복 (i = 0; i < n; i++){ parts = 콘솔.줄읽기().나누기(); s = 사전(); // 바퀴마다 새 묶음 자료를 만들어요 s["name"] = parts[0]; s["score"] = parts[1].수로변환(); s["level"] = levelOf(s["score"]); students.넣기(s);} 각각 (s 에서 students){ 콘솔.쓰기(s["name"] + " " + s["score"] + " " + LEVEL_NAMES[s["level"]]);} high = [];각각 (s 에서 students){ 만약 (s["level"] == HIGH) { high.넣기(s["name"]); }}콘솔.쓰기("HIGH: " + high.잇기(" "));4 mina 92 jun 58 sora 75 theo 81
mina 92 HIGH jun 58 LOW sora 75 MID theo 81 HIGH HIGH: mina theo
핵심
- 묶음 자료 하나는 한 사람(한 물건)에 대한 값들의 묶음이에요.
이것을 목록에 담으면 한 칸이 묶음 자료 하나인 표가 돼요.students에서 하나를 꺼낸s의 이름·점수·등급을 함께 다뤄요. - 묶음 자료는 반복문 안에서 바퀴마다 새로 만들어 목록에 넣어요.
이름 목록, 점수 목록을 따로 두는 것보다 한 사람의 값이 흩어지지 않아서 좋아요. - 열거형(enum)은 정해진 몇 가지 값에 이름을 붙인 타입이에요.
문자열"HIGH"나 숫자2를 직접 쓰는 대신Level.HIGH라고 써요.
열거형이 있는 언어에서는Level.HIHG처럼 없는 이름을 쓰면 오류로 알려 줘요(Python은 실행하다가, 나머지는 컴파일할 때). - 열거형의 속 값은 언어마다 달라요.
C, C++, TypeScript, C#은 속이 0, 1, 2 같은 수예요.
이름을 출력할 때 Java와 C#은 그대로 출력하면 되고, Python은.name, TypeScript는Level[값], C와 C++는 이름을 적어 둔 배열에서 꺼내요. - 열거형이 없는 JavaScript와 크레이콘은 이름 붙인 상수로 같은 일을 해요.
코드 곳곳에2를 쓰는 대신HIGH라는 이름을 한 번 정해 두고 써요. - 점수를 등급으로 바꾸는 일은 함수 하나(
levelOf)에 모아요.
기준이 바뀌어도 한 곳만 고치면 돼요.
언어별로 쓰는 법
| Python | 묶음 자료는 사전 {"name": name, "score": score}로 만들어 students.append(...)로 넣고 s["name"]으로 꺼내요.@dataclass를 붙인 클래스로 만들면 s.name으로 꺼내요.열거형은 from enum import Enum 다음 class Level(Enum): 안에 HIGH = 2처럼 적어요.이름은 level.name, 속 값은 level.value, 이름 문자열로 찾을 때는 Level["HIGH"] |
|---|---|
| JavaScript | 묶음 자료는 객체 { name: name, score: score }로 만들어 students.push(...)로 넣고 s.name으로 꺼내요.변수 이름과 키가 같으면 { name, score }로 줄여 써요.enum이 없어서 const Level = Object.freeze({ LOW: "LOW", MID: "MID", HIGH: "HIGH" });처럼 얼린 객체를 써요.얼린 객체는 Level.HIGH = "x"를 해도 바뀌지 않아요.이 사이트의 채점기처럼 보통 스크립트로 돌리면 오류 없이 무시되고, 엄격 모드( "use strict"나 ES 모듈)에서는 TypeError로 멈춰요.없는 이름 Level.HIHG는 오류가 아니라 undefined예요 |
| TypeScript | 묶음 자료의 모양은 type Student = { name: string; score: number; level: Level };로 정하고 목록은 Student[].열거형은 enum Level { LOW, MID, HIGH }이고 속은 0, 1, 2예요.이름은 Level[s.level]로 얻어요.문자열 몇 개 중 하나로 정하는 type Level = "LOW" | "MID" | "HIGH";도 많이 써요 |
| Java | 묶음 자료는 record Student(String name, int score, Level level) {}로 만들고 new Student(...), 꺼낼 때는 s.name().목록은 List<Student>.열거형은 enum Level { LOW, MID, HIGH }이고 출력하면 이름이 그대로 나와요.순서 번호는 level.ordinal(), 이름 문자열로 찾을 때는 Level.valueOf("HIGH") |
| C# | 묶음 자료는 record Student(string Name, int Score, Level Level);로 만들고 new Student(...), 꺼낼 때는 s.Name.목록은 List<Student>.열거형은 enum Level { LOW, MID, HIGH }이고 출력하면 이름이 나와요.속 수는 (int)level, 이름 문자열로 찾을 때는 Enum.Parse<Level>("HIGH") |
| C++ | 묶음 자료는 struct Student { std::string name; int score; Level level; };, 목록은 std::vector<Student>에 push_back({name, score, levelOf(score)}).열거형은 enum class Level { LOW, MID, HIGH };이고 Level::HIGH로 써요.enum class는 std::cout에 바로 넣을 수 없고 int로 저절로 바뀌지도 않아서, 이름 배열에서 LEVEL_NAMES[static_cast<int>(s.level)]로 꺼내요 |
| C | 묶음 자료는 typedef struct { char name[32]; int score; Level level; } Student;, 목록은 배열 Student students[100];에 students[i].score처럼 써요.열거형은 typedef enum { LOW, MID, HIGH } Level;이고 속은 정수 0, 1, 2예요.이름을 출력하는 기능이 없어서 LEVEL_NAMES[students[i].level]처럼 이름 배열에서 꺼내요 |
| Craykon | 구조체가 없어서 묶음 자료는 사전으로 만들어요. 바퀴마다 s = 사전();으로 새로 만들고 s["name"] = ...으로 채운 뒤 students.넣기(s).enum도 없어서 맨 위에 LOW = 0; MID = 1; HIGH = 2;처럼 이름 붙인 상수를 두고, 이름은 목록 LEVEL_NAMES[s["level"]]에서 꺼내요.상수를 바꾸지 못하게 막는 기능은 없으니 바꾸지 않기로 약속해요 |
자주 하는 실수
열거형 값을 문자열이나 수와 비교했어요Python
from enum import Enum
class Level(Enum):
LOW = 0
MID = 1
HIGH = 2
level = Level.HIGH
print(level == "HIGH", level == 2)→ False False — Level.HIGH는 문자열 "HIGH"도 수 2도 아닌 별개의 값이에요. 오류 없이 거짓이 되어서 찾기 어려워요
level == Level.HIGH처럼 열거형끼리 비교해요.
입력으로 받은 문자열은 Level["HIGH"]로 먼저 열거형으로 바꿔요.
묶음 자료 하나를 고쳐 가며 여러 번 넣었어요JavaScript · TypeScript · Python · Craykon
const s = {};
const students = [];
for (const name of ["mina", "jun"]) {
s.name = name;
students.push(s);
}
console.log(students);→ [ { name: 'jun' }, { name: 'jun' } ] — 목록의 두 칸이 같은 묶음 자료 하나를 가리켜서, 마지막에 적은 값만 남아요
묶음 자료를 반복문 안에서 만들어요(const s = { name: name };).
그러면 바퀴마다 새 묶음 자료가 생겨요.
열거형을 출력했더니 이름이 아니라 수가 나왔어요TypeScript · C · C++
enum Level { LOW, MID, HIGH }
console.log(Level.HIGH);→ 2 — 속에 든 수가 그대로 나와요. C에서 printf("%d\n", level);, C++에서 enum Level을 std::cout에 넣어도 2예요. C++의 enum class는 std::cout에 넣으면 컴파일 오류예요
TypeScript는 Level[Level.HIGH]로 이름을 얻어요.
C와 C++는 {"LOW", "MID", "HIGH"} 같은 이름 배열을 만들어 거기서 꺼내요.
열거형이라고 해서 정해진 값만 들어가는 것은 아니에요.
C에서
Level x = 7;은 그대로 컴파일되고 7이 출력돼요.C#에서
(Level)7을 출력해도 7이에요.C++의
enum class는 Level x = 7;을 컴파일 오류로 막아 줘요.밖에서 들어온 수를 열거형으로 바꿀 때는 범위를 먼저 확인해요.