כיצד ליצור עותקים עמוקים ברובי

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

חפצים והפניות

כדי להבין מה קורה, בואו נסתכל על איזה קוד פשוט. ראשית, מפעיל ההקצאה משתמש בסוג POD (Plain Old Data) רובי.

a = 1
b = א
a + = 1
מציב ב

כאן מפעיל ההקצאה מכין עותק של הערך של א ולהקצות אותו ב באמצעות מפעיל ההקצאה. כל שינוי ב- א לא יבוא לידי ביטוי ב ב. אבל מה עם משהו מורכב יותר? שקול זאת.

a = [1,2]
b = א
a << 3
מציב b.inspect

לפני שתפעיל את התוכנית לעיל, נסה לנחש מה יהיה הפלט ומדוע. זה לא כמו הדוגמה הקודמת, שינויים שנעשו ב- א באים לידי ביטוי ב, אבל למה? הסיבה לכך היא מערך האובייקט אינו מסוג POD. מפעיל ההקצאה אינו מבצע עותק של הערך, אלא פשוט מעתיק את ה- התייחסות לאובייקט המערך. ה א ו ב משתנים הם כעת הפניות לאותו אובייקט מערך, כל שינוי באחד המשתנים ייראה באחר.

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

instagram viewer

מה שרובי מספק: דופ שיבוט

רובי אכן מספק שתי שיטות להכנת עותקים של חפצים, כולל אחת שניתן לעשות עותקים עמוקים. ה אובייקט # דופ השיטה תיצור עותק רדוד של אובייקט. כדי להשיג זאת, ה- דופ השיטה תקרא לאתחל עותק שיטה של ​​אותה כיתה. מה שזה עושה בדיוק תלוי בכיתה. בכיתות מסוימות, כגון מערך, זה יאתחל מערך חדש עם אותם חברים כמו המערך המקורי. עם זאת, אין מדובר בעותק עמוק. שקול את הדברים הבאים.

a = [1,2]
b = a.dup
a << 3
מציב b.inspect
a = [[1,2]]
b = a.dup
א [0] << 3
מציב b.inspect

מה קרה כאן? ה מערך # initialize_copy השיטה אכן תיצור עותק של מערך, אך העותק עצמו הוא העתק רדוד. אם יש לך סוגים אחרים שאינם POD במערך שלך, השתמש ב דופ יהיה רק ​​עותק עמוק באופן חלקי. זה יהיה רק ​​עמוק כמו המערך הראשון, עמוק יותר מערכים, חשיש או אובייקטים אחרים יועתקו רק באופן רדוד.

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

אז בפועל מה המשמעות של זה? זה אומר שכל אחת מהכיתות שלך יכולה להגדיר שיטת שיבוט שתעשה העתק עמוק של אותו אובייקט. זה גם אומר שאתה צריך לכתוב שיטת שיבוט עבור כל כיתה שאתה עושה.

טריק: מרשיעה

"Marshalling" של אובייקט הוא דרך נוספת לומר "סדרת סדרות" של אובייקט. במילים אחרות, הפכו את האובייקט לזרם תווים שניתן לכתוב לקובץ שתוכלו "לבטל" או "לבטל את ההתרחשות" מאוחר יותר כדי לקבל אותו אובייקט. ניתן לנצל זאת כדי לקבל עותק עמוק של כל אובייקט.

a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
א [0] << 3
מציב b.inspect

מה קרה כאן? מרשל יוצר "dump" של המערך המקונן המאוחסן ב- א. Dump זה הוא מחרוזת תווים בינארית המיועדת לאחסון בקובץ. הוא מכיל את כל תוכנו של המערך, עותק עמוק שלם. הבא, מרשל עושה את ההפך. זה מנתח מערך תווים בינארי זה ויוצר מערך חדש לחלוטין, עם אלמנטים של מערך חדש לחלוטין.

אבל זה טריק. זה לא יעיל, זה לא יעבוד על כל האובייקטים (מה קורה אם תנסו לשבט חיבור רשת בצורה כזו?) וכנראה שזה לא נורא מהיר. עם זאת, זו הדרך הקלה ביותר להעתיק עותקים עמוקים בהזמנה אישית לאתחל עותק או שיבוט שיטות. כמו כן, ניתן לעשות את אותו הדבר בשיטות כמו to_yaml או to_xml אם טעונות ספריות כדי לתמוך בהן.

instagram story viewer