"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > فهم Go terators

فهم Go terators

تم النشر بتاريخ 2024-08-21
تصفح:206

Understanding Go terators

يبدو أن العديد من الأشخاص في حيرة من أمرهم بسبب التكرارات المضافة حديثًا في Go، ولهذا السبب قررت أن أكتب مقالًا آخر أحاول شرحها بطريقة بسيطة قدر الإمكان.

كيف يتم استدعاؤهم بواسطة Go؟

أولاً، أعتقد أنه من المهم أن نفهم كيف يتم استدعاء التكرارات واستخدامها بواسطة Go، وهو في الواقع بسيط جدًا، فلنستخدم Slices.All iterator كمثال. إليك كيفية استخدام هذا المكرر عادةً:

package main

import (
    "fmt"
    "slices"
)

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range slices.All(slice) {
        if index >= 2 {
            break
        }
        fmt.Println(index, element)
    }

    // Output:
    // 0 Element 1
    // 1 Element 2
}

وهكذا يبدو الأمر في الواقع:

package main

import (
    "fmt"
    "slices"
)

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    slices.All(slice)(func (index int, element string) bool {
        if index >= 2 {
            return false // break
        }
        fmt.Println(index, element)

        return true // continue loop as normal
    })

    // Output:
    // 0 Element 1
    // 1 Element 2
}

ما يحدث هو "نقل" نص الحلقة لإنتاج دالة يتم تمريرها إلى المكرر، بينما يتم تحويل المتابعة والكسر لإرجاع صحيح وإرجاع خطأ على التوالي. تتم إضافة return true أيضًا إلى نهاية الحلقة للإشارة إلى أننا نرغب في الحصول على العنصر التالي، إذا لم يتم اتخاذ قرار آخر من قبل.

هذا ليس كشفًا دقيقًا لما يفعله المترجم ولم أتحقق من تنفيذ Go للتحقق من ذلك، لكنهم ينتجون نتائج مماثلة من ملاحظاتي.

كيفية إنشاء المكرر الخاص بك وتنفيذه

الآن، بعد أن فهمت كيف يتم استدعاؤها وأدركت مدى بساطتها في الواقع، سيكون من الأسهل بكثير فهم كيفية إنشاء المكرر الخاص بك وتنفيذه.

لنقم بإنشاء مكرر تصحيح الأخطاء الذي سيطبع رسائل تصحيح الأخطاء لكل خطوة من خطوات تنفيذ المكرر والتي ستنتقل عبر جميع العناصر في الشريحة (وظيفة الشرائح.جميعها).

أولاً، سأقوم بإنشاء وظيفة مساعدة صغيرة لتسجيل الخروج من الرسالة بوقت التنفيذ الحالي.

import (
    "fmt"
    "time"
)

var START time.Time = time.Now()

func logt(message string) {
    fmt.Println(time.Since(START), message)
}

العودة إلى المكرر:

import (
    "iter"
)

func DebugIter[E any](slice []E) iter.Seq2[int, E] {
    logt("DebugIter called")

    // the same way iter.All returned function
    // we called in order to iterate over slice
    // here we are returning a function to
    // iterate over all slice elements too
    return func(yield func(int, E) bool) {
        logt("Seq2 return function called, starting loop")
        for index, element := range slice {
            logt("in loop, calling yield")
            shouldContinue := yield(index, element)
            if !shouldContinue {
                logt("in loop, yield returned false")
                return
            }
            logt("in loop, yield returned true")
        }
    }
}

لقد أضفت عددًا قليلًا من عبارات طباعة تصحيح الأخطاء حتى نتمكن من رؤية ترتيب تنفيذ المكرِّر بشكل أفضل وكيف سيتفاعل مع كلمات رئيسية مختلفة مثل فاصل ومتابعة.

أخيرًا، دعونا نستخدم المُكرِّر المنفَّذ:

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range DebugIter(slice) {
        message := "got element in range of iter: "   element
        logt(message)
        if index >= 2 {
            break
        }
        if index > 0 {
            continue
        }
        time.Sleep(2 * time.Second)
        logt("ended sleep in range of iter")
    }
}

سوف يعطينا النتيجة:

11.125µs DebugIter called
39.292µs Seq2 return function called, starting loop
42.459µs in loop, calling yield
44.292µs got element in range of iter: Element 1
2.001194292s ended sleep in range of iter
2.001280459s in loop, yield returned true
2.001283917s in loop, calling yield
2.001287042s got element in range of iter: Element 2
2.001291084s in loop, yield returned true
2.001293125s in loop, calling yield
2.0012955s got element in range of iter: Element 3
2.001297542s in loop, yield returned false

يوضح هذا المثال بشكل جيد كيفية عمل التكرارات وتنفيذها. عند استخدام المكرر في حلقة النطاق، يتم "نقل" جميع التعليمات الموجودة في كتلة الحلقة إلى وظيفة تسمى العائد. عندما نستدعي العائد، فإننا نطلب بشكل أساسي من go runtime تنفيذ أي شيء موجود في كتلة الحلقة بالقيمة التالية لهذا التكرار، ولهذا السبب أيضًا سيتم حظر العائد، إذا تم حظر جسم الحلقة. في حالة تحديد وقت التشغيل أن تكرار الحلقة هذا من المفترض أن يتوقف، فإن العائد سيعود خطأ، ويمكن أن يحدث ذلك عند استيفاء الكلمة الأساسية Break أثناء تنفيذ كتلة الحلقة، ويجب ألا نستدعي العائد بعد الآن إذا حدث ذلك. بخلاف ذلك، يجب أن نستمر في استدعاء العائد.

الرمز الكامل:

package main

import (
    "fmt"
    "time"
    "iter"
)

var START time.Time = time.Now()

func logt(message string) {
    fmt.Println(time.Since(START), message)
}

func DebugIter[E any](slice []E) iter.Seq2[int, E] {
    logt("DebugIter called")

    // the same way iter.All returned function
    // we called in order to iterate over slice
    // here we are returning a function to
    // iterate over all slice elements too
    return func(yield func(int, E) bool) {
        logt("Seq2 return function called, starting loop")
        for index, element := range slice {
            logt("in loop, calling yield for")
            shouldContinue := yield(index, element)
            if !shouldContinue {
                logt("in loop, yield returned false")
                return
            }
            logt("in loop, yield returned true")
        }
    }
}

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range DebugIter(slice) {
        message := "got element in range of iter: "   element
        logt(message)
        if index >= 2 {
            break
        }
        if index > 0 {
            continue
        }
        time.Sleep(2 * time.Second)
        logt("ended sleep in range of iter")
    }

    // unfold compiler magic
    //  DebugIter(slice)(func (index int, element string) bool {
    //    message := "got element in range of iter: "   element
    //    logt(message)
    //    if index >= 2 {
    //      return false
    //    }
    //    if index > 0 {
    //      return true
    //    }
    //    time.Sleep(2 * time.Second)
    //    logt("ended sleep in range of iter")
    //
    //    return true
    //  })
}
بيان الافراج تم نشر هذه المقالة على: https://dev.to/wmdanor/understanding-go-123-iterators-3ai1?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3