תבנית 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 הייתה גורמת ללולאה הבאה להתחיל באמצע.
אתגר
קלבואו נבנה מחלקת 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
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4פרויקט: בנק דיגיטלי
הגדרת הפרויקטפעולת הפקדה7פולימורפיזם ודריסת מתודות
דריסת מתודותקריאה למתודות של מחלקת האבטיפוסיות ברווזממשק משותףבדיקת טיפוססיכום – תפקידי עובדים2תבנית אב־טיפוס למחלקה
מושג אב־הטיפוסקישור באמצעות __indexהבנאי :new()אתחול מאפייניםמופעים עצמאייםסיכום – מפעל מכוניות5העמסת אופרטורים בתכנות מונחה עצמים
חיבור אובייקטיםחיסור אובייקטיםשרשור אובייקטיםהשוואת אובייקטים (<, >)חזרה – חישובים עם ארנק8כימוס
כללי מתן שמותClosures לשמירה על פרטיותגישה באמצעות Closuresטבלאות לקריאה בלבדלוגיקת אימותסיכום – כספת מאובטחת11תבניות עיצוב (בקצרה)
פונקציות Factoryטבלת Singletonתבנית IteratorObserver (מאזין)סיכום – מפעל Logger3מצב והתנהגות של אובייקטים
משתני מופעמתודות Getterמתודות Setterמאפיינים מחושביםעיצוב מחרוזותבדיקות שוויוןסיכום – ציון תלמיד6יסודות הירושה
הכנת מבנה הירושהירושת מתודותהרחבת הבנאיהוספת מתודות למחלקת הבתמשותף לעומת ייחודיסיכום – היררכיית צורותתרגלו בעצמכם: קומפיילר Lua אונליין