Menu

מערכים ב-Golang (Array): אורך קבוע, סמנטיקת ערך ו-slices

למערך ב-Go יש אורך קבוע שהוא חלק מהטיפוס שלו, והוא מועתק בכל השמה. למדו איך מצהירים על מערכים, עוברים עליהם, משווים ומעבירים אותם, ולמה רוב קוד ה-Go משתמש ב-slices במקומם.

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

הצהרה על מערך

טיפוס מערך נכתב [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]TSlice []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.

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

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

להתחיל