1.8 آرایه و slice

1.8 آرایه و slice

آرایه و slice دو نوع تایپ با یک وجه اشتراک در زبان گو هستند ولی این دو نوع تایپ یکسری تفاوت‌هایی نیز با هم دارند که در ادامه به آن‌ها می‌پردازیم.

1.8.1 تعریف آرایه #

آرایه یکی از عمومی‌ترین تایپ‌ها در زبان‌های برنامه‌نویسی آرایه‌ها هستند که برای نگه داری گروهی مقادیر از یک نوع تایپ استفاده می‌شود. برای دسترسی به هر کدام از مقادیر درون آرایه باید از اندیس استفاده کرد. معمولا برای تعریف آرایه تعداد مشخص و ثابتی را برای تعداد مقادیر مشخص میکنید. یعنی این آرایه قرار است چند تا مقدار نگه داری کند.

array

در مثال زیر یک نمونه کد در خصوص چگونگی تعریف آرایه قرار داده‌ایم :

package main

import "fmt"

func main() {
	arrayInts := [5]int{1, 25, 12354, 654, 32}
	fmt.Println(arrayInts)
}
  1. یک متغیر کوتاه از نوع آرایه با نام arrayInts تعریف کردیم.
  2. ظرفیت آرایه را با عدد ۵ تعیین کردیم (یعنی این آرایه فقط ۵ تا مقدار بیشتر نگه داری نمی‌کند)
  3. سپس تایپ آرایه را از نوع int مشخص کردیم.
  4. در نهایت در همانجا آرایه را مقدار دهی کردیم. در زبان گو مقدار دهی با باز کردن {} به انگلیسی curly bracket انجام می‌شود.

1.8.2 مفهوم اندازه و ظرفیت (size, capacity) #

در آرایه ما ۲ تا مفهوم داریم: اندازه و ظرفیت که از عنوان این مفهوم مشخص است آرایه دارای یک اندازه و ظرفیت مشخصی است و اگر شما بیشتر از ظرفیت و اندازه تعیین شده مقدار دهی کنید با خطا مواجه خواهید شد.

در آرایه ظرفیت به نسبت اندازه تعیین می‌شود.
package main

import "fmt"

func main() {
	arrayString := [3]string{"a", "b", "c", "d"}
	fmt.Println(arrayString)
}

در کد فوق ما یک آرایه با اندازه ۳ تعریف کردیم و ۴ تا مقدار داخلش قرار دادیم و پس از اجرا, با خطای تعداد مقادیر بیشتر از اندازه و ظرفیت می باشد مواجه شدیم.

1.8.2.1 تابع len و cap #

برای آرایه و slice ما ۲ تا تابع داریم که می‌توانیم اندازه و ظرفیت یک آرایه یا slice را بگیریم.

  • تابع len یکی از توابع بسیار کاربردی و پراستفاده هنگام کار با آرایه یا slice است که می‌توانید اندازه آرایه یا slice را بگیرید.
  • تابع cap ظرفیت آرایه و slice را نمایش می‌دهد.
package main

import "fmt"

func main() {
	arrayString := [3]string{"a", "b", "c"}
	fmt.Printf("array %v, len %d, cap %d", arrayString, len(arrayString), cap(arrayString))
}

1.8.3 تعریف آرایه و مقدارهی #

در مثال زیر ما یک آرایه با مقدار 5 تعریف کردیم و قصد داریم در ادامه کد، آرایه رو مقداردهی کنیم.

package main

import "fmt"

func main() {
	nums := [5]int{}
	fmt.Printf("array nums values %v, len %d, cap %d", nums, len(nums), cap(nums))

	nums[0] = 1
	nums[1] = 2
	nums[2] = 10
	nums[4] = 999

	fmt.Println("")
	fmt.Printf("array nums values %v, len %d, cap %d", nums, len(nums), cap(nums))
}

array

  1. در کد فوق در ابتدا ما یک آرایه بدون مقدار تعریف کردیم.
  2. سپس با استفاده از اندیس مقدار را در خانه مشخص قرار دادیم.

1.8.3.1 تعریف آرایه با اندازه تعیین شده توسط کامپایلر (شما اندازه رو بهش نمیدین.) #

شما در زبان گو می‌توانید با استفاده از ... (Ellipsis) یک آرایه با اندازه مشخص شده توسط کامپایلر تعریف کنید.

package main

import "fmt"

func main() {
	nums := [...]int{1, 25, 45, 8797, 78, 879, 541, 11}
	fmt.Printf("array nums values %v, len %d, cap %d", nums, len(nums), cap(nums))
}
توجه کنید زمانی که ... (Ellipsis) برای تعریف آرایه استفاده می‌کنید فقط در همان لحظه تعریف می‌توانید آرایه رو مقدار دهی کنید.

1.8.3.2 تعریف آرایه دو بعدی یا چند بعدی #

در زبان گو همانند سایر زبان‌ها می‌توانید آرایه دو بعدی یا چند بعدی تعریف کنید. این نوع آرایه‌ها برای پیاده‌سازی ماتریس یا یکسری سناریوهای توسعه کاربردی مناسب هستند.

package main

import "fmt"

func main() {
	nums := [2][2][2]int{{{1, 2}, {2, 3}}, {{4, 5}, {6, 7}}}
	fmt.Printf("array nums values %v, len %d, cap %d", nums, len(nums), cap(nums))
}

array

1.8.3.3 مقایسه آرایه‌ها #

در کد زیر ما یک نمونه از مقایسه آرایه‌ها را قرار داده‌ایم که این مقایسه براساس تایپ، اندازه و مقادیر در نظر گرفته شده است.

package main

import "fmt"

func main() {
	nums := [2]int{1, 2}
	nums2 := [2]int{1, 3}
	nums3 := [2]int{1, 2}
	nums4 := [3]int{1, 2, 3}
	chars := [2]string{"a", "b"}

	fmt.Println(nums == nums2) // false
	fmt.Println(nums == nums3) // true
	fmt.Println(nums == nums4) // error: invalid operation: nums == nums4 (mismatched types [2]int and [3]int)
	fmt.Println(nums == chars) // error: invalid operation: nums == chars (mismatched types [2]int and [2]string)
}

1.8.4 برش (slice) #

همانطور که می‌دانید آرایه یکی از مهم‌ترین عناوین در زبان‌های برنامه‌نویسی است، اما در زبان گو slice نسبت به آرایه بسیار پر استفاده و کاربردی‌تر است. اگر بخواهیم خیلی ساده slice را توصیف کنیم در واقع “یک slice به عنوان یک بخش بهم پیوسته از یک آرایه تعریف می‌شود که شما می‌توانید المنت‌ها را در حال اجرا افزایش یا کاهش دهید بدون آنکه ظرفیت و اندازه آن را مشخص کنید.”

اما این سوال پیش می‌آید علت اینکه slice به نسبت آرایه کاربرد بیشتری دارد چیست؟ آرایه دارای برخی از محدودیت‌ها علی الخصوص، اندازه ثابت می‌باشد اما در slice شما این محدودیت‌ها را نخواهید داشت و خیلی ساده می‌توانید المنت‌ها را افزایش، حذف و حتی کپی کنید.

در زبان گو slice‌ها یک پارچگی آرایه را حفظ می‌کنند و کار با آرایه خیلی ساده و آسان‌تر خواهد شد.

slice

1.8.4.1 تعریف یک slice با اندازه مشخص #

شما می توانید با استفاده از تابع make یک slice با اندازه مشخص تعریف کنید.

slice

slice := make([]int, 5)

fmt.Println(len(slice)) // Print 5

fmt.Println(cap(slice)) // Print 5

1.8.4.2 تعریف یک slice با اندازه و ظرفیت مشخص #

شما می‌توانید با استفاده از تابع make یک slice با ظرفیت و اندازه مشخص تعریف کنید.

slice

slice := make([]int, 3, 5)

fmt.Println(len(slice)) // Print 3

fmt.Println(cap(slice)) // Print 5

توجه کنید مقدار ظرفیت نباید کمتر از مقدار اندازه باشد.

package main

import “fmt”

func main() { test := make([]int, 5, 4) fmt.Println(test) }

1.8.4.3 تعریف یک slice با متغیر کوتاه short variable declaration #

شما خیلی ساده می‌توانید یک slice را توسط متغیر کوتاه ایجاد کنید.

slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}

fmt.Println(len(slice)) //Print 5

fmt.Println(cap(slice)) //Print 5

intSlice:= []int{10, 20, 30}

fmt.Println(len(intSlice)) //Print 3

fmt.Println(cap(intSlice)) //Print 3

1.8.4.4 تعریف یک slice با موقعیت‌های شاخص #

شما می‌توانید یک slice را با موقعیت‌های شاخص ایجاد کنید که n تا المنت با مقدار پیش‌فرض ایجاد می‌کند و در آخر x را به آخر slice اضافه می‌کند. (در مثال زیر ۹۹ تا المنت با مقدار 0 و در اخر یک المنت با مقدار 88) درست می‌کند.

package main

import "fmt"

func main() {
	test := []int{99: 88}
	fmt.Println(len(test), cap(test))
}

slice

1.8.4.5 تعریف یک slice خالی #

شما می‌توانید خیلی ساده یک slice خالی ایجاد کنید.

sliceOne := make([]int, 0)

sliceTwo := []int{}

fmt.Println(sliceOne == nil) // print false

fmt.Println(len(sliceOne)) // print 0

fmt.Println(cap(sliceOne)) // print 0

fmt.Println(sliceTwo == nil) // print false

fmt.Println(len(sliceTwo)) // print 0

fmt.Println(cap(sliceTwo)) // print 0

slice

1.8.5 مقدار دهی مجدد یکی از المنت های slice یا آرایه #

شما خیلی راحت می‌توانید مقدار یکی از المنت‌های slice یا آرایه را مقدار دهی کنید.

slice := []int{10, 20, 30, 40}

fmt.Println(slice) //print [10 20 30 40]

slice[1] = 25

fmt.Println(slice) // print [10 25 30 40]

slice

1.8.6 ایجاد یک slice جدید بر اساس یک slice از پیش تعریف شده #

شما می‌توانید یک slice جدید را بر اساس یک slice از پیش تعریف شده ایجاد کنید.

x := []int{10, 20, 30, 40, 50}

fmt.Println(x) // Print [10 20 30 40 50]

fmt.Println(len(x)) // Print 5

fmt.Println(cap(x)) // Print 5

y := x[1:3]

fmt.Println(y) //Print [20 30]

fmt.Println(len(y)) //Print 2

fmt.Println(cap(y)) //Print 4

slice

  • ما یک متغیر با نام x با ۵ تا المنت مقدار دهی شده تعریف کردیم.
  • سپس یک متغیر کوتاه با نام y تعریف کردیم که متغیر x را داخلش قرار دادیم.
  • سپس به متغیر x گفتیم از اندیس ۱ تا ۳ را به y اختصاص بدهد.

توجه کنید اتفاقی که مثال بالا رخ داد این بود که ما اندازه و ظرفیت جدیدی برای متغیر y تعیین کردیم.

Len: 3 - 1 = 2 Cap: 5 - 1 = 4

1.8.7 خطای index out of range در slice #

یک slice فقط با توجه به اندازه و اندیس‌هایش امکان دسترسی و مقدار دهی مجدد المنت هایش را می‌دهد، اما اگر شما بخواهید خارج از اندازه تعیین شده جهت مقداری دهی و یا دسترسی به slice اقدام کنید با خطای index out of range مواجه خواهید شد.

package main

import "fmt"

func main() {
	slice := []int{10, 20, 30, 40, 50}
	newSlice := slice[1:3]
	newSlice[3] = 45
	fmt.Println(newSlice)
}

1.8.8 افرودن (append) المنت‌های یک slice #

شما خیلی ساده می‌توانید با استفاده از تابع append به المنت‌های یک slice بیفزایید.

slice := []int{10, 20, 30, 40, 50}

newSlice := slice[1:3]

fmt.Println(len(newSlice)) // Print 2

fmt.Println(cap(newSlice)) // Print 4

newSlice = append(newSlice, 60)

fmt.Println(len(newSlice)) // Print 3

fmt.Println(cap(newSlice)) // Print 4

slice

در کد زیر اتفاقی که صورت گرفته است این است که اگر شما ... Ellipsis را بعد از کلمه slice بزارید یعنی دارید می‌گید تمامی المنت‌های داخل slice به newSlice اضافه شود.

slice := []int{10, 20, 30, 40, 50}

newSlice := []int{}

newSlice = append(newSlice, slice...)

1.8.9 نحوه حذف یک المنت در slice #

برای حذف یک المنت در slice باید بصورت تکنیکی اینکار را انجام دهید چون زبان گو یک روش built-in برای این کار ندارد.

slice

  • در این روش شما باید در ابتدا آخرین المنت را به المنت مورد نظر با استفاده از اندیس کپی کنید.
  • سپس آخرین المنت را از slice حذف کنید.
package main

import "fmt"

func main() {
	slice := []int{10, 20, 30, 40, 50}
	slice[1] = slice[len(slice)-1]
	slice = slice[:len(slice)-1]
	fmt.Println(slice)
}
  • یک روش دیگر برای حذف یک المنت از slice استفاده از تابع append است.
  • به مثال زیر توجه کنید.
package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5}
	index := 2 // ایندکس المنتی که میخاییم حذفش کنیم
	slice = append(slice[:index], slice[index+1:]...)
	fmt.Println(slice) // خروجی: [1 2 4 5]
}

1.8.10 تابع copy در slice #

شما با استفاده از تابع copy می‌توانید یک slice را به slice دیگری کپی کنید.

package main

import "fmt"

func main() {
	src := []int{1, 2, 3, 4, 5}
	dst := make([]int, 5)
	numberOfElementsCopied := copy(dst, src)
	fmt.Println(numberOfElementsCopied, dst)
}

1.8.11 نحوه مرتب کردن (sort) یک slice #

برای مرتب کردن یک slice می‌توانید از توابع کتابخانه sort در زبان گو استفاده کنید.

  • sort.Ints
  • sort.Float64s
  • sort.Strings
package main

import (
	"fmt"
	"sort"
)

func main() {
	s := []int{4, 2, 3, 1}
	sort.Ints(s)
	fmt.Println(s)
}

1.8.12 فرق بین آرایه و slice #

  1. فرق نوع تعریف آرایه و slice
    • برای تعریف آرایه شما باید داخل براکت [] یک مقداری را قرار دهید.
    • برای تعریف slice هیچ مقداری را داخل براکت [] نباید قرار دهید.
array := [3]int{10, 20, 30}

slice := []int{10, 20, 30}
  1. فرق بین مقدار صفر آرایه و slice
    • مقدار خالی slice ها nil است.
    • مقدار خالی یک آرایه، همان آرایه با المنت‌های مقدار پیش‌فرض است.
var slice []int32
var array [2]int32

fmt.Println(slice == nil) // print true
fmt.Println(len(slice)) // print 0
fmt.Println(cap(slice)) // print 0

fmt.Println(array) // print [0 0]