נתקלתי אתמול בבאג חמוד
-
בחזרה לנושא המקורי
משהו יכול להסביר מה זה שונה מ foreach או מ:for (int i = 0; i < 3; i++) { var j = i; list.Add(() => Console.WriteLine(j)); }שזה מפיק:
[2 ,1 ,0]הרי גם
iוגםjהם לוקאליים, אז למה הלמדה משתמשת פעם אחת בערך ופעם אחת ברפרנס? -
D dovid פיצל נושא זה
-
@קומפיונט
בגלל שjהוא לוקלי, אבלiלא, הוא מוצהר מחוץ ללולאה.
אז יש לך רפרנס שונה ל-jבכל ריצה. ורק אחד ל-iלכל הריצות.@חגי אתה מתכוונן שלמשתנה i יש סקופ יותר ארוך מ - j, אבל בפועל שניהם משתנים לוקאליים.
אחרי מחשבה אני משער שזה התנהגות מכוונת של הקומפיילר, ברגע שחוזרים כמה פעמים על יצירת למדה הקומפיילר מחליט האם ליצור אובייקט למדה חדש בכל פעם, או ליצור אובייקט למדה אחד בהתחלה ולעדכן אותו בכל פעם.
במקרה של
jהקומפיילר מזהה שיש שימוש במשנה מהסקופ הנוכחי אז הוא יוצר אובייקט למדה חדש, אבל במקרה שלiהוא יוצר את הלמדה פעם אחת בסקופ החיצוני ומעדכן אותו בכל שינוי.אפשר לאמת את זה על ידי בדיקת כתובות האובייקטים ברשימה.
-
@קומפיונט "לוקלי" זה בדיוק אורך הסקופ (לא מכיר משמעות אחרת למילה לוקלי בC#).
בכל איטרציה נוצר משתנה חדש j. פונקציית הלמבדה שנוצרת בכל איטרציה לוכדת את הרפרנס למשתנה שונה בכל פעם (שלכולם קוראים j אבל הם מקומות נפרדים בזיכרון).
בכל מקרה לכידה של משתנים בסקופ היא תמיד רפרנס למשתנה ואף פעם לא העתקת ערך - שינוי של הערך במשתנה ישפיע תמיד על הפונקציה הלוכדת (וגם לכן זה מעכב את איסופם באספן הזבל כל עוד יש מצביע חי לפונקציה).מלבד עצם העובדה שמדובר בהתנהגות מתוכננת ותקינה, סגנון הבדיקה שלך גם חוטא לשפת C#, זו לא שפה שנוצרה ואז תועדה אלא להיפך, ובתיעוד אין זכר למימושים חסכניים של מהדרים או מפרשים. המחקר הטכני שלך מעניין, אבל מדובר פה בהתנהגות הכרחית מעצם התיעוד של השפה בלי קשר למימוש כזה או אחר מאחורי הקלעים, זה כללי המשחק הרשמיים של השפה.
-
@קומפיונט "לוקלי" זה בדיוק אורך הסקופ (לא מכיר משמעות אחרת למילה לוקלי בC#).
בכל איטרציה נוצר משתנה חדש j. פונקציית הלמבדה שנוצרת בכל איטרציה לוכדת את הרפרנס למשתנה שונה בכל פעם (שלכולם קוראים j אבל הם מקומות נפרדים בזיכרון).
בכל מקרה לכידה של משתנים בסקופ היא תמיד רפרנס למשתנה ואף פעם לא העתקת ערך - שינוי של הערך במשתנה ישפיע תמיד על הפונקציה הלוכדת (וגם לכן זה מעכב את איסופם באספן הזבל כל עוד יש מצביע חי לפונקציה).מלבד עצם העובדה שמדובר בהתנהגות מתוכננת ותקינה, סגנון הבדיקה שלך גם חוטא לשפת C#, זו לא שפה שנוצרה ואז תועדה אלא להיפך, ובתיעוד אין זכר למימושים חסכניים של מהדרים או מפרשים. המחקר הטכני שלך מעניין, אבל מדובר פה בהתנהגות הכרחית מעצם התיעוד של השפה בלי קשר למימוש כזה או אחר מאחורי הקלעים, זה כללי המשחק הרשמיים של השפה.
-
אני משאיר את זה פה למי שבכל זאת אוהב לראות איך זה קורא בפועל.
@dovid כתב בנתקלתי אתמול בבאג חמוד:
חוטא לשפת C#
ובלבד שיכוון ליבו
-
אני משאיר את זה פה למי שבכל זאת אוהב לראות איך זה קורא בפועל.
@dovid כתב בנתקלתי אתמול בבאג חמוד:
חוטא לשפת C#
ובלבד שיכוון ליבו
-
@קומפיונט "לוקלי" זה בדיוק אורך הסקופ (לא מכיר משמעות אחרת למילה לוקלי בC#).
בכל איטרציה נוצר משתנה חדש j. פונקציית הלמבדה שנוצרת בכל איטרציה לוכדת את הרפרנס למשתנה שונה בכל פעם (שלכולם קוראים j אבל הם מקומות נפרדים בזיכרון).
בכל מקרה לכידה של משתנים בסקופ היא תמיד רפרנס למשתנה ואף פעם לא העתקת ערך - שינוי של הערך במשתנה ישפיע תמיד על הפונקציה הלוכדת (וגם לכן זה מעכב את איסופם באספן הזבל כל עוד יש מצביע חי לפונקציה).מלבד עצם העובדה שמדובר בהתנהגות מתוכננת ותקינה, סגנון הבדיקה שלך גם חוטא לשפת C#, זו לא שפה שנוצרה ואז תועדה אלא להיפך, ובתיעוד אין זכר למימושים חסכניים של מהדרים או מפרשים. המחקר הטכני שלך מעניין, אבל מדובר פה בהתנהגות הכרחית מעצם התיעוד של השפה בלי קשר למימוש כזה או אחר מאחורי הקלעים, זה כללי המשחק הרשמיים של השפה.
@dovid כתב בנתקלתי אתמול בבאג חמוד:
מלבד עצם העובדה שמדובר בהתנהגות מתוכננת ותקינה, סגנון הבדיקה שלך גם חוטא לשפת C#, זו לא שפה שנוצרה ואז תועדה אלא להיפך, ובתיעוד אין זכר למימושים חסכניים של מהדרים או מפרשים. המחקר הטכני שלך מעניין, אבל מדובר פה בהתנהגות הכרחית מעצם התיעוד של השפה בלי קשר למימוש כזה או אחר מאחורי הקלעים, זה כללי המשחק הרשמיים של השפה.
נשמע קצת חמור מה שאמרת, איך ב C# המפורשת והבדוקה זה אמור להיות תקני..
ב RUST זה לא יכל לקרות.
let mut list = Vec::new(); for i in 0..3 { list.push(move || println!("{}", i)); // move זה המשמעות של לקיחת בעלות, ומספר מממש Copy } list.iter_mut().for_each(|x| x()); // דוגמה נוספת, עם סוג ערך שלא מיישם Copy והמהדר מזהה את הבאג: let mut list_str = Vec::new(); // for s in ["a".to_string(), "b".to_string(), "c".to_string()].iter() // לא עובר קומפילציה כי זה אורך החיים של המערך הוא רק בתוך הלולאה, ואני משאיל בלבד ומייצא את זה החוצה לרשימה // for s in ["a".to_string(), "b".to_string(), "c".to_string()].iter().cloned() // כן עובר קומפילציה כי זה משכפל את המערך, ואז אפשר לייצא את זה החוצה for s in ["a".to_string(), "b".to_string(), "c".to_string()].into_iter() // עובר קומפילציה כי אני לא משאיל אלא משתמש בערך עצמו, ומייצא את זה החוצה { list_str.push(move || println!("{}", s)); // let mut s = s; // שורה זו לא אפשרית כי כבר ייצאתי החוצה את הערך } list_str.iter().for_each(|x| x());(אני יודע שבקוד כאן השתמשתי בforeach - כי אין for רגיל ב RUST. אבל הרעיון - איך נוצר כזה בלבול עם הרפרנס ב C#?)
-
@dovid כתב בנתקלתי אתמול בבאג חמוד:
מלבד עצם העובדה שמדובר בהתנהגות מתוכננת ותקינה, סגנון הבדיקה שלך גם חוטא לשפת C#, זו לא שפה שנוצרה ואז תועדה אלא להיפך, ובתיעוד אין זכר למימושים חסכניים של מהדרים או מפרשים. המחקר הטכני שלך מעניין, אבל מדובר פה בהתנהגות הכרחית מעצם התיעוד של השפה בלי קשר למימוש כזה או אחר מאחורי הקלעים, זה כללי המשחק הרשמיים של השפה.
נשמע קצת חמור מה שאמרת, איך ב C# המפורשת והבדוקה זה אמור להיות תקני..
ב RUST זה לא יכל לקרות.
let mut list = Vec::new(); for i in 0..3 { list.push(move || println!("{}", i)); // move זה המשמעות של לקיחת בעלות, ומספר מממש Copy } list.iter_mut().for_each(|x| x()); // דוגמה נוספת, עם סוג ערך שלא מיישם Copy והמהדר מזהה את הבאג: let mut list_str = Vec::new(); // for s in ["a".to_string(), "b".to_string(), "c".to_string()].iter() // לא עובר קומפילציה כי זה אורך החיים של המערך הוא רק בתוך הלולאה, ואני משאיל בלבד ומייצא את זה החוצה לרשימה // for s in ["a".to_string(), "b".to_string(), "c".to_string()].iter().cloned() // כן עובר קומפילציה כי זה משכפל את המערך, ואז אפשר לייצא את זה החוצה for s in ["a".to_string(), "b".to_string(), "c".to_string()].into_iter() // עובר קומפילציה כי אני לא משאיל אלא משתמש בערך עצמו, ומייצא את זה החוצה { list_str.push(move || println!("{}", s)); // let mut s = s; // שורה זו לא אפשרית כי כבר ייצאתי החוצה את הערך } list_str.iter().for_each(|x| x());(אני יודע שבקוד כאן השתמשתי בforeach - כי אין for רגיל ב RUST. אבל הרעיון - איך נוצר כזה בלבול עם הרפרנס ב C#?)
-
@קומפיונט כתב בנתקלתי אתמול בבאג חמוד:
@Y.Excel.Access שים לב מה ה - LLM כתב לך בהערה של שורה 4...
לא השתמשתי בשום AI כדי לכתוב את הכמה שורות האלו!
ובקשר להערה שלך - לכן הבאתי עוד דוגמה של String שלא מממש Copy.
-
@קומפיונט כתב בנתקלתי אתמול בבאג חמוד:
@Y.Excel.Access שים לב מה ה - LLM כתב לך בהערה של שורה 4...
לא השתמשתי בשום AI כדי לכתוב את הכמה שורות האלו!
ובקשר להערה שלך - לכן הבאתי עוד דוגמה של String שלא מממש Copy.
@Y.Excel.Access כתב בנתקלתי אתמול בבאג חמוד:
לא השתמשתי בשום AI כדי לכתוב את הכמה שורות האלו!
אופסס, סליחה שחשדתי...