Menu

פונקציות variadic ב-Golang: פרמטרי ...T ופריסה עם s...

פונקציה variadic ב-Go מקבלת כל מספר של ארגומנטים אחרונים מטיפוס אחד. למדו את התחביר ...T, איך מעבירים slice עם s..., המלכודת של ה-aliasing, ואיך fmt.Println משתמשת ב-...any.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

הפרמטר ...T

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

זה מדפיס 0, 5 ו-10. בלי ארגומנטים, nums הוא slice שהוא nil באורך 0, כך שהלולאה פשוט לא רצה.

פרמטרים רגילים יכולים לבוא קודם. רק האחרון יכול להיות variadic:

func logf(level string, format string, args ...any)

העברת slice עם s...

אם כבר יש לכם slice, אי אפשר להעביר אותו ישירות: sum(nums) נכשל עם cannot use nums (variable of type []int) as int value in argument to sum. פרסו אותו עם ... אחרי הארגומנט:

גם append היא variadic, ולכן חיבור של שני slices נכתב append(a, b...).

הפריסה חייבת להיות הדבר היחיד במקום ה-variadic. sum(1, nums...) נכשל עם too many arguments in call to sum. בנו קודם את ה-slice המשולב עם append.

ה-slice משותף, לא מועתק

כשקוראים עם ארגומנטים בודדים, Go יוצרת בשבילם slice חדש. כשפורסים slice קיים עם s..., הפונקציה מקבלת את אותו slice. כתיבה לאיברים שלו משנה את הנתונים של הקורא.

פלט:

[7 8 9]
[0 8 9]

פונקציה variadic שמשנה את הפרמטר שלה צריכה לתעד את זה, או להעתיק קודם עם slices.Clone. רוב הפונקציות ה-variadic רק קוראות את הארגומנטים שלהן, כך שזה נדיר שזה פוגע, אבל זה הבדל אמיתי משפות שבהן rest parameters הם תמיד מערך חדש.

...any ואיך fmt.Println עובדת

fmt.Println מוצהרת כ-func Println(a ...any) (n int, err error). any (כינוי ל-interface{} מאז Go 1.18) מקבל כל טיפוס, כך שאפשר להעביר מחרוזות, מספרים ו-structs בקריאה אחת.

העברה הלאה היא הפרט החשוב: logf מעבירה את args... ל-Printf. בלי הנקודות, Printf מקבלת ארגומנט אחד, []any, ושורת האזהרה מדפיסה [WARN] [3 8] of %!d(MISSING) workers idle. go vet מדווח missing ... in args forwarded to printf-like function רק כשפרמטר הפורמט מועבר הלאה בלי שינוי, כמו ב-fmt.Printf(format, args). כאן הפורמט נבנה עם +, כך ש-vet שותק והנקודות באחריותכם.

אי אפשר לפרוס []string לתוך ...any. טיפוסי האיברים חייבים להתאים בדיוק, אז המירו קודם:

names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
	args[i] = n
}
fmt.Println(args...)

דרישה של לפחות ארגומנט אחד

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

עכשיו maxOf() היא שגיאת הידור במקום בדיקה בזמן ריצה. הפונקציה המובנית max עצמה עובדת באותה צורה: היא דורשת לפחות ארגומנט אחד.

פרמטרי variadic כארגומנטים אופציונליים

ל-Go אין ערכי ברירת מחדל לפרמטרים. לפעמים משתמשים בפרמטר variadic כדי לזייף ארגומנט אופציונלי אחד:

func connect(addr string, timeout ...time.Duration)

זה עובד אבל נקרא גרוע, כי קוראים יכולים להעביר שלושה timeouts והחתימה לא אומרת איזה מהם מנצח. ליותר מאפשרות אחת, העדיפו struct של הגדרות, או את דפוס ה-functional options (...Option, שבו כל Option היא פונקציה שמציבה שדה). הדפוס הזה הוא variadic, אבל לכל ארגומנט יש משמעות ברורה.

תקציר הכללים

כללדוגמה
רק הפרמטר האחרון יכול להיות variadicfunc f(a string, b ...int)
בפנים, הפרמטר הוא sliceל-b יש טיפוס []int
אפס ארגומנטים נותנים slice שהוא nilf("x") גורם ל-b == nil
פורסים slice עם ...f("x", nums...)
פריסה משתפת את המערך שמתחתכתיבה ל-b[i] משנה את nums
אין ערבוב של פריסה עם ערכים נוספיםf("x", 1, nums...) לא מתקמפל

שאלות נפוצות

מה זו פונקציה variadic ב-Go?

פונקציה שהפרמטר האחרון שלה בצורה ...T, כמו func sum(nums ...int) int. אפשר לקרוא לה עם אפס ארגומנטים או יותר מטיפוס T, ובתוך הפונקציה הפרמטר הוא slice מסוג []T.

איך מעבירים slice לפונקציה variadic ב-Go?

הוסיפו ... אחרי ה-slice: sum(nums...). ה-slice מועבר כמו שהוא, בלי העתקה, כך שהפונקציה רואה את אותו מערך שמתחת. אי אפשר לערבב פריסה עם ארגומנטים בודדים: sum(1, nums...) לא מתקמפל.

האם יש ל-Go אופרטור spread?

רק לארגומנט האחרון של קריאה variadic: f(s...). אין spread כללי לבניית מערכים או structs, או לקריאה לפונקציות שאינן variadic עם slice. append(a, b...) הוא אותו כלל שמופעל על הפונקציה המובנית append.

האם לפונקציה ב-Go יכולים להיות שני פרמטרים variadic?

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

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל