מטא־מתודת __newindex
חלק מהיחידה לוגיקה וזרימת התוכנית במסלול ה-Lua של Coddy. שיעור 19 מתוך 54.
למדת איך __index קובע מה קורה כשמנסים לקרוא מפתח שלא קיים בטבלה. עכשיו תגלו את המקבילה שלו: __newindex, שקובע מה קורה כשמשייכים ערך למפתח שעדיין לא קיים בטבלה.
מטא־מתודת __newindex מופעלת בכל פעם שמנסים להקצות ערך למפתח שאינו קיים כרגע בטבלה. כך אפשר ליירט שינויים ולשלוט בהם לפני שהם מתבצעים.
הנה דוגמה פשוטה שמונעת הוספה של מפתחות חדשים לטבלה:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- פלט: Cannot add key: name
print(readOnly.name) -- פלט: nilכשאתה מנסה להגדיר readOnly.name = "Alice", Lua מזהה ש-name לא קיים בטבלה. במקום להוסיף אותו, Lua קוראת לפונקציה __newindex עם שלושה ארגומנטים: הטבלה עצמה, המפתח שמוגדר והערך שמוקצה.
בדוגמה הזאת, הפונקציה פשוט מדפיסה הודעה ולא באמת שומרת שום דבר.
אפשר גם להשתמש ב-__newindex כדי לאמת נתונים לפני שמאפשרים להכניס אותם לטבלה שלך:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- פלט: Health must be a number!
player.health = 100 -- מותר, נשמר באמצעות rawset
player.health = "high" -- אין הודעה: health קיים כעתשימו לב לשימוש ב־rawset() בדוגמה השנייה. הפונקציה הזאת עוקפת את המטא־טבלה ומגדירה את הערך ישירות בטבלה.
בלעדיו, הגדרת הערך תפעיל שוב את __newindex, וכך תיווצר לולאה אינסופית.
מכיוון ש־__newindex מופעל רק עבור מפתחות שהטבלה לא מכילה, הבדיקה הזו מגינה על ההשמה הראשונה של כל מפתח. לאחר ש־rawset שמר את health, השמות מאוחרות יותר משנות אותו ישירות. כדי לבדוק כל השמה, השאר את הטבלה עצמה ריקה ושמור את הערכים בטבלה שנייה, שממנה היא קוראת באמצעות __index:
local data = {}
local player = setmetatable({}, {
__index = data,
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
data[key] = value
end
end
})
player.health = 100
player.health = "high" -- פלט: Health must be a number!
print(player.health) -- פלט: 100ל־player אף פעם אין מפתח משלו, ולכן כל השמה עוברת דרך __newindex, וכל קריאה עוברת דרך __index אל data.
אתגר
קלכתבו פונקציה createValidatedTable שמקבלת key ו-value ומחזירה טבלה שמקבלת רק ערכי מחרוזת עבור מפתחות חדשים.
צרו טבלה ריקה והצמידו לה מטא-טבלה עם __newindex שבודקת את סוג הערך. אם מישהו מנסה להוסיף ערך שאינו מחרוזת, השתמשו ב-rawset() כדי לאחסן במקומו את המחרוזת "invalid". עבור ערכי מחרוזת, אחסנו אותם כרגיל באמצעות rawset().
לוגיקה:
- צרו טבלה ריקה
- צרו מטא-טבלה עם
__newindexשמקבלת את הטבלה, המפתח והערך - בתוך
__newindex, בדקו אם הערך הוא מחרוזת באמצעותtype(value) == "string" - אם הערך הוא מחרוזת, השתמשו ב-
rawset(t, key, value)כדי לאחסן אותו - אם הערך אינו מחרוזת, השתמשו ב-
rawset(t, key, "invalid")כדי לאחסן במקומו את"invalid" - הצמידו את המטא-טבלה לטבלה באמצעות
setmetatable() - השתמשו ב-
rawset()כדי להוסיף לטבלה את זוג המפתח-ערך ההתחלתי (תוך עקיפת המטא-טבלה) - החזירו את הטבלה
פרמטרים:
key(מחרוזת): המפתח ההתחלתי שיש להוסיף לטבלהvalue(מחרוזת): הערך ההתחלתי שיש להוסיף לטבלה
ערך מוחזר: טבלה עם בדיקה שמקבלת רק ערכי מחרוזת, המכילה את זוג המפתח-ערך ההתחלתי (טבלה)
נסו בעצמכם
function createValidatedTable(key, value)
-- כתבו כאן את הקוד
end
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה לוגיקה וזרימת התוכנית
1איטרציה מתקדמת בטבלאות
איטרציה באמצעות pairs()איטרציה באמצעות ipairs()pairs() לעומת ipairs()סיכום – גיליון דמות2פונקציות נוספות של ספריית הטבלאות
table.concat()בניית טבלה ו־unpack()table.sort()מיון מותאם אישית באמצעות פונקציותסיכום – לוח שיאיםתרגלו בעצמכם: קומפיילר Lua אונליין