דילוג לתוכן
  • דף הבית
  • קטגוריות
  • פוסטים אחרונים
  • משתמשים
  • חיפוש
  • חוקי הפורום
כיווץ
תחומים

תחומים - פורום חרדי מקצועי

💡 רוצה לזכור קריאת שמע בזמן? לחץ כאן!
  1. דף הבית
  2. תכנות
  3. למה DoForEach לא קיים ב-LINQ?

למה DoForEach לא קיים ב-LINQ?

מתוזמן נעוץ נעול הועבר תכנות
24 פוסטים 4 כותבים 495 צפיות
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • קומפיונטק מנותק
    קומפיונטק מנותק
    קומפיונט
    השיב לdovid ב נערך לאחרונה על ידי
    #15

    @dovid
    אכן קראתי התשובה הוא מסביר שם נפלא למה ForEach אין מקומו ב-LINQ.
    אבל עדיין יש כמה תמיהות כדלהלן:

    בקשר לטענות הכותב:
    א. למה דווקא ForEach מיותר וכי ה-ToList לא מיותר?! אדרבה, בעידן הנוכחי שהתכנות הפונקציונלי די פופולרי, קטע הקוד הבא יכול להיות מאוד אלגנטי:

    Enumerable.Range(1, 20)
              .Select(n => n * 3)
              .Where(n => n % 2 == 0)
              .DoForEach(n => Console.WriteLine(n));
    

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

    ב. כנ"ל.

    ג. זה באמת הנקודה שהכי דגדגה לי, כי היות והערך המוחזר מ-ForEach הוא void יוצא שהיא המתודה היחידה שלא מחזירה ערך כלשהו. אבל גם על זה אפשר להתפלסף, כי הרעיון של ForEach זהה לרעיון של ToList ו-ToArray אלא שספציפית הפעולה של ForEach לא אמורה להחזיר ערך כלשהו.
    וחוץ מזה מדוע העיקרון הגורף של LINQ הוא להחזיר ערך כלשהו? למה לא להשתמש איתו כדי להשפיע על הרצף בלי להחזיר שום ערך? איזה דבר רע (או לא פילוסופי) יש בזה?!

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

    dovidD תגובה 1 תגובה אחרונה
    0
    • dovidD מנותק
      dovidD מנותק
      dovid ניהול
      השיב לקומפיונט ב נערך לאחרונה על ידי dovid
      #16

      @קומפיונט
      א. ToList מצריך לולאה + משתנה. זה הרבה יותר משמעותי מלולאה נטו.
      ב. כנ"ל
      ג. הנקודה היא לא מה חוזר. קוראים לזה תכנות פונקציונלי.
      הפילוספיה היא שזה מתודות שאפשר להשתמש בהם בבטחה בלי לחשוש שהם ישפיעו על חלקים אחרים בתכנית שלך. לעולם לא יקרה שינוי לחלק בתכנית מפעולת LINQ שלא הושמה לתוך משתנה. בתוך המתודה ToList או ToArray קורה מה שקורה, אבל זה בסקופ סגור ובלתי משפיע, וזה יוצר פלט שאתה בוחר איך להשתמש בו.
      פונקציה שנכתבה לפי כללי התכנות הפונקציונלי בהכרח תחזיר ערך כי אחרת מה היא עושה, כי מה שקורה בגוף הפונקציה חייב להיות פרטי ובלתי נוגע לשום דבר אחר בעולם מלבד פלט.

      מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

      בכל נושא אפשר ליצור קשר dovid@tchumim.com

      קומפיונטק תגובה 1 תגובה אחרונה
      4
      • קומפיונטק מנותק
        קומפיונטק מנותק
        קומפיונט
        השיב לdovid ב נערך לאחרונה על ידי
        #17

        @dovid כתב בלמה DoForEach לא קיים ב-LINQ?:

        א. ToList מצריך לולאה + משתנה. זה הרבה יותר משמעותי מלולאה נטו.

        למה ToList חוסך לולאה ומשתנה? זה לכאורה עושה return new List<TSource>(enum)

        ג. הנקודה היא לא מה חוזר. קוראים לזה תכנות פונקציונלי.

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

        אולי בגלל שלא מובן לי עד הסוף מה זה 'תכנות פונקציונלי' אז אני לא מצליח להבין מדוע חייב לחזור ערך. אין כזה דבר בתכנות פונקציונלי שחוזר ערך void?

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

        לכאורה המתודה ForEach שומרת על הכלל הזה, מכיוון שהיא לא תוכל לשנות ערכים כל שהם, (הייעוד שלה זה לעשות פעולה חיצונית עבור כל אחד מהערכים) היא אמנם תוכל לשנות מאפיינים בתוך ערכים, אבל זה גם בעיה שנמצאת ב-Select וב-Where אם כי זה לא הייעוד שלהם, אבל הם לא מספקים את ההגנה הזאת.

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

        תגובה 1 תגובה אחרונה
        2
        • חגיח מנותק
          חגיח מנותק
          חגי
          השיב לdovid ב נערך לאחרונה על ידי
          #18

          @dovid
          בכולל אחה"צ באמצע תוס' קשה פתאום עלה לי הרעיון שבשביל לבדוק את זה, אפשר לממש את הIEnumerator לבד.

          dovidD תגובה 1 תגובה אחרונה
          0
          • dovidD מנותק
            dovidD מנותק
            dovid ניהול
            השיב לחגי ב נערך לאחרונה על ידי dovid
            #19

            @חגי נכון.

            void Main()
            {
                var test = new testList();
                Console.WriteLine("ILIST ACCESS:");
                Console.WriteLine(test.Last());
            
                var test2 = test.Select(t => t * 2);
                Console.WriteLine("IENUMERABLE ACCESS:");
                Console.WriteLine(test2.Last());
            }
            
            
            public class ProxyIEnumerator :IEnumerator<int>
            {
                IEnumerator<int> targetEnum;
                public ProxyIEnumerator(IList<int> _source) => targetEnum = _source.GetEnumerator();
            
                public bool MoveNext()
                {
                    Console.WriteLine("MoveNext call");
                    return targetEnum.MoveNext();
                }
            
                public void Reset() => targetEnum.Reset();
                public int Current => targetEnum.Current;
            
                object IEnumerator.Current => targetEnum.Current;
                public void Dispose() => targetEnum.Dispose();
            
            }
            
            
            
            class testList : IList<int>
            {
                private int[] source = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            
                public int Count => source.Length;
            
                public int this[int index]
                {
                    get
                    {
                        Console.WriteLine("access " + index);
                        return source[index];
                    }
                    set => throw new NotImplementedException();
                }
            
                public IEnumerator<int> GetEnumerator() => new ProxyIEnumerator(source);
            
            
                public bool IsReadOnly => throw new NotImplementedException();
                IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
                public void Add(int item) => throw new NotImplementedException();
                public void Clear() => throw new NotImplementedException();
                public bool Contains(int item) => throw new NotImplementedException();
                public void CopyTo(int[] array, int arrayIndex) => throw new NotImplementedException();
                public int IndexOf(int item) => throw new NotImplementedException();
                public void Insert(int index, int item) => throw new NotImplementedException();
                public bool Remove(int item) => throw new NotImplementedException();
                public void RemoveAt(int index) => throw new NotImplementedException();
            }
            

            פלט:

            ILIST ACCESS:
            access 8
            9
            IENUMERABLE ACCESS:
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            MoveNext call
            18
            

            מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

            בכל נושא אפשר ליצור קשר dovid@tchumim.com

            תגובה 1 תגובה אחרונה
            3
            • קומפיונטק מנותק
              קומפיונטק מנותק
              קומפיונט
              כתב ב נערך לאחרונה על ידי קומפיונט
              #20

              @חגי + @dovid

              אם אני מבין נכון אז הדוגמא ש @dovid הביא ממחישה שכשקוראים ל-Last אז אם האובייקט של הרצף ממש את IList`1 אז ישר מתבצע קפיצה לאינדקס האחרון, ואם לא אז ה-Last עובר ו'מעיר' את כל הרצף עד שחוזר false מה-()MoveNext.
              אם הבנתי נכון אז אפשר לראות את זה ב-ILSpy:

              אגב, בדקתי, ToList לא משכפל את הרצף, הוא רק יוצר Reference חדש לערכים הקיימים.
              עריכה: אפילו המתודה CopyTo ב-List`1 משכפלת רק את ה-Reference.

              LastIlSpy.jpg

              dovidD תגובה 1 תגובה אחרונה
              0
              • dovidD מנותק
                dovidD מנותק
                dovid ניהול
                השיב לקומפיונט ב נערך לאחרונה על ידי
                #21

                @קומפיונט כתב בלמה DoForEach לא קיים ב-LINQ?:

                אגב, בדקתי, ToList לא משכפל את הרצף, הוא רק יוצר Reference חדש לערכים הקיימים.
                עריכה: אפילו המתודה CopyTo ב-List`1 משכפלת רק את ה-Reference.

                אם האלמנטים ברשימה הם לא פרימטיביים (כלומר Reference Type) ברור כשמש שתוכן הפריטים לא משוכפל. הנושא הוא הרשימה עצמה, נוצרת רשימה נוספת. כל רשימה זה מערך בזיכרון, גם שההתייחסות הם לאותם אובייקטים.
                אבל כפי שציינתי לעיל, IEnumerable לא חייב להיות בכלל רשימה גשמית ולא חייבת להיות בעלת סוף כל שהוא, ובמקרים הללו מדובר ביצירת הרשימה ולא רק בהעתקת רשימת הפניות.

                מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

                בכל נושא אפשר ליצור קשר dovid@tchumim.com

                קומפיונטק תגובה 1 תגובה אחרונה
                3
                • קומפיונטק מנותק
                  קומפיונטק מנותק
                  קומפיונט
                  השיב לdovid ב נערך לאחרונה על ידי
                  #22

                  @dovid כתב בלמה DoForEach לא קיים ב-LINQ?:

                  אם האלמנטים ברשימה הם לא פרימטיביים (כלומר Reference Type) ברור כשמש שתוכן הפריטים לא משוכפל. הנושא הוא הרשימה עצמה, נוצרת רשימה נוספת. כל רשימה זה מערך בזיכרון, גם שההתייחסות הם לאותם אובייקטים.

                  אז מה היתה ההוא אמינא ש-ToList לא משכפל?! ההו"א היתה ש-ToList מתנהג כמו Enumerator?

                  תגובה 1 תגובה אחרונה
                  0
                  • yossizY מנותק
                    yossizY מנותק
                    yossiz
                    כתב ב נערך לאחרונה על ידי
                    #23

                    האם שכחתם שדוטנט הוא בקוד פתוח היום? או שיותר קל לבדוק תיאוריות עם טסטים מאשר לקרוא את קוד המקור?

                    📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

                    dovidD תגובה 1 תגובה אחרונה
                    4
                    • dovidD מנותק
                      dovidD מנותק
                      dovid ניהול
                      השיב לyossiz ב נערך לאחרונה על ידי dovid
                      #24

                      @yossiz
                      א. לפחות עבורי זה יותר ממחיש
                      ב. יש לי טראומות לפעמים כשאני מבקר בקוד המקור.
                      (עריכה: בקשר לILSpy הGo To Definition בינתיים מוליך אליו, ולא עשו עוד קיצור כזה ישר לגיטאב).

                      מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

                      בכל נושא אפשר ליצור קשר dovid@tchumim.com

                      תגובה 1 תגובה אחרונה
                      3

                      • 1
                      • 2
                      בא תתחבר לדף היומי!
                      • התחברות

                      • אין לך חשבון עדיין? הרשמה

                      • התחברו או הירשמו כדי לחפש.
                      • פוסט ראשון
                        פוסט אחרון
                      0
                      • דף הבית
                      • קטגוריות
                      • פוסטים אחרונים
                      • משתמשים
                      • חיפוש
                      • חוקי הפורום