הצהרה על מערך
טיפוס מערך נכתב [N]T: אורך ואחריו טיפוס האיבר. האורך נקבע בזמן קומפילציה והוא חלק מהטיפוס.
פלט:
[90 0 0 0 75] 5
Tue
[5]int [2 3 5 7 11]
["" "b" "" "d" ""]
מערך תמיד מאותחל במלואו. איברים שלא הצבתם בהם ערך מחזיקים את ערך האפס של טיפוס האיבר: 0, "", false, nil.
אינדקסים וגבולות
האינדקסים נעים מ-0 עד len(a)-1. אינדקס קבוע מחוץ לטווח הוא שגיאת קומפילציה. אינדקס משתנה מחוץ לטווח גורם ל-panic בזמן ריצה.
var a [3]int
a[5] = 1 // compile error: invalid argument: index 5 out of bounds [0:3]
i := 5
a[i] = 1 // panic: runtime error: index out of range [5] with length 3
מעבר על מערך
for i, v := range a עובר על כל איבר לפי הסדר. v הוא עותק של האיבר, ולכן השמה ל-v לא משנה את המערך. כתבו דרך האינדקס במקום זאת.
יש עוד נקודה עדינה אחת שייחודית למערכים: range על ערך מערך מחשב מראש עותק של המערך. אם גוף הלולאה משנה איברים מאוחרים יותר, v עדיין מציג את הערכים הישנים. מעבר על &a או על a[:] חוסך את ההעתקה.
מערכים הם ערכים
זה ההבדל העיקרי ממערכים ב-C, ב-Java או ב-JavaScript. השמה של מערך מעתיקה כל איבר. גם העברת מערך לפונקציה מעתיקה אותו.
פלט:
[1 2 3] [99 2 3]
[1 2 3]
[0 2 3]
ההעתקה זולה במערכים קטנים ויקרה בגדולים: העברת [1_000_000]int לפי ערך מעתיקה שמונה מגה-בייט. העבירו מצביע (*[N]T) או slice (a[:]) כשהמערך גדול או כשהפונקציה צריכה לשנות אותו. גישה לאינדקס דרך מצביע למערך לא דורשת * מפורש: a[0] עובד על *[3]int.
השוואת מערכים ושימוש בהם כמפתחות של map
מערכים של איברים הניתנים להשוואה תומכים ב-== וב-!=. שני מערכים שווים כשכל איבר שווה. זה גם הופך אותם למפתחות map תקינים, מה שאין כן slices.
מערכים באורכים שונים הם טיפוסים שונים ואי אפשר להשוות ביניהם בכלל: [3]int{} == [4]int{} לא מתקמפל (mismatched types [3]int and [4]int).
מערכים רב-ממדיים
מערך של מערכים נותן לכם רשת קבועה. הרשת כולה היא בלוק זיכרון רציף אחד.
row[:] הופך את השורה מטיפוס [3]rune ל-slice מטיפוס []rune, ש-string() מקבלת.
מערכים מול slices
מערך [N]T | Slice []T | |
|---|---|---|
| אורך | קבוע, חלק מהטיפוס | משתנה עם append |
| השמה והעברה | מעתיקה את כל האיברים | מעתיקה כותרת קטנה, האיברים משותפים |
| ערך אפס | N איברי אפס | nil, אורך 0 |
== | כן, איבר מול איבר | רק מול nil |
| מפתח של map | כן | לא |
| שימוש נפוץ | נתונים בגודל קבוע: hashes, קואורדינטות, buffers | כמעט כל השאר |
בפועל תפגשו מערכים בעיקר בכמה מקומות: sha256.Sum256 מחזירה [32]byte, טבלאות חיפוש שגודלן לא משתנה, וכאחסון הבסיס של slice. חיתוך מערך עם a[:] או a[1:3] יוצר slice שחולק איתו את הזיכרון; הדף על slices מסביר מה המשמעות של השיתוף הזה.
המרה בין מערכים ל-slices
המרה של slice למערך (Go 1.20) או למצביע למערך (Go 1.17) גורמת ל-panic אם ה-slice קצר מאורך המערך: panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 3.
טעויות נפוצות
- לצפות שפונקציה תשנה מערך שהועבר אליה כארגומנט. היא מקבלת עותק. העבירו
*[N]Tאו slice. - שימוש במערך במקום שבו נדרש slice.
[]int{1, 2}הוא slice,[2]int{1, 2}הוא מערך. פונקציה שמקבלת[]intלא תקבל[2]int; העבירוa[:]. - מערכים גדולים בלולאת range.
for _, v := range bigArrayמעתיקה קודם את כל המערך. עברו על&bigArrayאו על slice.
שאלות נפוצות
איך מצהירים על מערך ב-Go?
כותבים את האורך בסוגריים מרובעים לפני טיפוס האיבר: var a [5]int יוצר חמישה אפסים. עם ערכים: a := [3]string{"x", "y", "z"}. השתמשו ב-[...] כדי שהקומפיילר יספור בעצמו: ל-a := [...]int{1, 2, 3} יש הטיפוס [3]int.
מה ההבדל בין מערך ל-slice ב-Go?
למערך יש אורך קבוע שהוא חלק מהטיפוס שלו ([3]int ו-[4]int הם טיפוסים שונים), והוא מועתק כשמשימים אותו או מעבירים אותו. slice ([]int) הוא תצוגה על מערך בסיס, עם אורך שיכול להשתנות באמצעות append, והעתקת slice מעתיקה רק את התצוגה ולא את האיברים. רוב קוד ה-Go משתמש ב-slices.
איך מקבלים את האורך של מערך ב-Go?
השתמשו בפונקציה המובנית len(a). עבור מערך זה קבוע בזמן קומפילציה, כך שאפשר להשתמש בו בביטויים קבועים. cap(a) מחזירה את אותו המספר.
האם אפשר להשוות מערכים ב-Go?
כן, עם == ו-!=, אם טיפוס האיבר ניתן להשוואה. שני מערכים שווים כשכל האיברים שלהם שווים. מערכים יכולים לשמש גם כמפתחות של map. אי אפשר להשוות slices עם == (חוץ מהשוואה ל-nil); עבורם השתמשו ב-slices.Equal.