Menu
CoddyTech

תבנית Iterator

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Lua של Coddy. שיעור 64 מתוך 70.

כשיש לך אוסף של פריטים שמאוחסנים בתוך אובייקט, לעיתים קרובות תרצה לעבור עליהם באמצעות לולאה. לולאת for הגנרית של Lua פועלת עם כל פונקציית איטרטור: היא קוראת לפונקציה לפני כל איטרציה ועוצרת כשהפונקציה מחזירה nil.

תבנית Iterator מעניקה לאובייקט מתודה שמחזירה פונקציה כזאת. הפונקציה שומרת את המיקום שלה במשתנה מקומי, כך שהמיקום שייך ללולאה אחת ולא לאוסף:

local Collection = {}
Collection.__index = Collection

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

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

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

כל קריאה ל־bag:iter() יוצרת פונקציה חדשה עם index משלה, שמתחיל ב־0. הפונקציה היא סגירה: היא עדיין יכולה להשתמש ב־index וב־self אחרי ש־iter החזירה ערך. אחרי הפריט האחרון, self.items[index] הוא nil, והלולאה נעצרת:

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

for item in bag:iter() do
    print(item)  -- apple, ואז banana
end

for item in bag:iter() do
    print(item)  -- apple, ואז שוב banana
end

שמור את המיקום מחוץ לאוסף עצמו. אם האובייקט שמר את self.index ושימש כאיטרטור של עצמו, הלולאה הראשונה הייתה משאירה את האינדקס בסוף, ולכן לולאה שנייה לא הייתה מדפיסה דבר, ולולאה שנעצרה מוקדם בעזרת break הייתה גורמת ללולאה הבאה להתחיל באמצע.

challenge icon

אתגר

קל

בואו נבנה מחלקת Playlist שאפשר לעבור עליה בלולאת for הגנרית של Lua! תיישמו את תבנית האיטרטור באמצעות מתודת :iter() שמחזירה פונקציית איטרטור.

תארגנו את הקוד בשני קבצים:

  • Playlist.lua: צרו מחלקת Playlist ששומרת שירים בתוכה. המחלקה שלכם צריכה לכלול בנאי :new() שמאתחל רשימת שירים ריקה ומתודה :addSong(title) שמוסיפה לה שיר. והכי חשוב, הוסיפו מתודת :iter() שמחזירה פונקציה חדשה בכל פעם שקוראים לה. הפונקציה הזאת שומרת את המיקום שלה במשתנה מקומי ומחזירה את השיר הבא בכל קריאה, או nil כשלא נשארו שירים.
  • main.lua: טענו את מודול Playlist שלכם וצרו רשימת השמעה חדשה. הוסיפו לה שירים בהתאם לקלטים שאתם מקבלים, ואז השתמשו בלולאת for הגנרית על playlist:iter() כדי להדפיס את הכותרת של כל שיר בשורה נפרדת.

מכיוון שכל קריאה ל-:iter() מתחילה מהשיר הראשון, אפשר לעבור על אותה רשימת השמעה בכל מספר של פעמים.

תקבלו שלושה קלטים, שכל אחד מהם מייצג כותרת של שיר להוספה לרשימת ההשמעה שלכם (לפי הסדר).

לדוגמה, אם הקלטים הם Bohemian Rhapsody, Stairway to Heaven ו-Hotel California, הפלט צריך להיות:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

אם הקלטים הם Yesterday, Imagine ו-Let It Be, הפלט צריך להיות:

Yesterday
Imagine
Let It Be

נסו בעצמכם

-- טענו את המודול Playlist באמצעות require
local Playlist = require('Playlist')

-- קראו מהקלט שמות של שלושה שירים
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: צרו רשימת השמעה חדשה באמצעות Playlist:new()

-- TODO: הוסיפו את שלושת השירים לרשימת ההשמעה באמצעות :addSong()

-- TODO: השתמשו בלולאת for גנרית על playlist:iter()
-- והדפיסו את השם של כל שיר בשורה נפרדת
-- רמז: for song in playlist:iter() do ... end
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Lua אונליין