Menu
Coddy logo textTech

이터레이터 패턴

Coddy Lua 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 70개 중 64번째.

객체 내부에 저장된 항목 컬렉션이 있을 때, 종종 이를 반복문으로 순회하고 싶을 것입니다. Lua의 범용 for 루프는 사용자 정의 반복자(iterator)와 함께 멋지게 작동하며, 이 기능을 클래스에 직접 구축할 수 있습니다.

반복자 패턴(Iterator pattern)은 객체가 내부 데이터가 순회되는 방식을 제어할 수 있게 해줍니다. 한 가지 우아한 접근 방식은 객체를 함수처럼 호출 가능하게 만드는 __call 메타메소드를 사용하는 것입니다. 각 호출은 더 이상 항목이 없을 때까지 다음 항목을 반환합니다.

local Collection = {}
Collection.__index = Collection

function Collection:new()
    local obj = { items = {}, index = 0 }
    setmetatable(obj, Collection)
    return obj
end

function Collection:add(item)
    table.insert(self.items, item)
end

function Collection:__call()
    self.index = self.index + 1
    return self.items[self.index]
end

__call이 정의되어 있으면, while 루프를 사용하여 반복하거나 객체를 일반적인 for 문에 직접 전달할 수 있습니다:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag do
    print(item)
end

루프는 bag()을 반복적으로 호출합니다. __callnil(더 이상 항목이 없음)을 반환하면 루프가 중단됩니다. 이 패턴은 반복 로직을 객체 내부에 캡슐화하여, 항목에 액세스하는 방법과 순서를 사용자가 완전히 제어할 수 있게 해줍니다.

challenge icon

챌린지

쉬움

Lua의 제네릭 for 루프를 사용하여 반복할 수 있는 Playlist 클래스를 만들어 봅시다! __call 메타메소드를 사용하여 이터레이터(Iterator) 패턴을 구현함으로써, 플레이리스트를 노래별로 순회할 수 있게 만들 것입니다.

코드는 두 개의 파일로 구성됩니다:

  • Playlist.lua: 노래를 내부적으로 저장하는 Playlist 클래스를 생성합니다. 클래스에는 빈 노래 목록을 초기화하고 반복 인덱스를 0으로 설정하는 :new() 생성자가 필요합니다. 플레이리스트에 노래를 추가하는 :addSong(title) 메소드를 추가하세요. 가장 중요한 것은, 플레이리스트가 "호출"될 때마다 목록의 다음 노래를 반환(더 이상 노래가 없으면 nil 반환)하도록 __call 메타메소드를 구현하는 것입니다.
  • main.lua: Playlist 모듈을 불러와서(require) 새 플레이리스트를 생성합니다. 입력받은 값을 기반으로 노래를 추가한 다음, 제네릭 for 루프를 사용하여 플레이리스트를 반복하고 각 노래 제목을 한 줄에 하나씩 출력하세요.

이터레이터 패턴의 마법은 Playlist 객체를 호출 가능하게 만든다는 점입니다. for 루프에서 사용될 때, Lua는 nil을 반환할 때까지 이를 반복적으로 호출하여 각 노래를 순서대로 가져옵니다.

플레이리스트에 추가할 세 개의 노래 제목을 입력으로 받게 됩니다 (순서대로).

예를 들어, 입력이 Bohemian Rhapsody, Stairway to Heaven, Hotel California라면 출력은 다음과 같아야 합니다:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

입력이 Yesterday, Imagine, Let It Be라면 출력은 다음과 같아야 합니다:

Yesterday
Imagine
Let It Be

치트 시트

반복자 패턴(Iterator pattern)은 객체가 사용자 정의 반복 로직을 구현하여 내부 데이터가 탐색되는 방식을 제어할 수 있게 해줍니다.

__call 메타메소드를 사용하여 객체를 함수처럼 호출 가능하게 만들 수 있습니다. 각 호출은 더 이상 항목이 없을 때까지(nil을 반환할 때까지) 다음 항목을 반환합니다:

local Collection = {}
Collection.__index = Collection

function Collection:new()
    local obj = { items = {}, index = 0 }
    setmetatable(obj, Collection)
    return obj
end

function Collection:add(item)
    table.insert(self.items, item)
end

function Collection:__call()
    self.index = self.index + 1
    return self.items[self.index]
end

__call이 정의되면, 일반적인 for 루프에서 객체를 직접 사용할 수 있습니다:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag do
    print(item)
end

루프는 객체를 반복적으로 호출합니다. __callnil을 반환하면 루프가 자동으로 중단됩니다.

직접 해보기

-- Playlist 모듈을 불러옵니다
local Playlist = require('Playlist')

-- 입력으로부터 세 개의 곡 제목을 읽습니다
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Playlist:new()를 사용하여 새로운 플레이리스트를 생성하세요

-- TODO: :addSong()을 사용하여 플레이리스트에 세 곡을 추가하세요

-- TODO: generic for 루프를 사용하여 플레이리스트를 반복하세요
-- 그리고 각 곡의 제목을 각각의 줄에 출력하세요
-- 힌트: for song in playlist do ... end
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Object Oriented Programming의 모든 레슨