مقدمه#
همیشه یاد گرفتن یه زبون جدید چالشهای خاصی داره. یادم میاد زمانی که شروع کرده بودم شی گرایی پایتون رو یاد میگرفتم و با جاوا مقایسش میکردم، همه چیزش خیلی عجیب بود. الان هم که دارم go رو یاد میگیرم یه چیزاییش عجیبه ولی میدونم زمان بگذره همه اینها عادی و بدیهی میشه.
در این مطلب هم میخوام در مورد تابع append
بنویسم.
تصور اشتباه از این تابع#
همونطور که میدونید این تابع یک slice
به همراه دادههای جدیدی که قراره به اسلایس اضافه بشن رو ورودی میگیره و بعد یک اسلایس جدید بر میگردونه.
ولی دقیقا تصور اشتباه من همین جا بود: اسلایس جدید!
من همیشه سعی میکنم توابع و کلاسهایی که در هر زبونی استفاده میکنم رو خوب بشناسم. بدونم اون پشتش چه اتفاقی داره میافته؛ صرفا یه مصرفکننده نباشم. به خاطر همین سعی کردم اتفاقی که پشت این تابع در حال رخ دادن هست رو با زبون c++
تصور کنم. میگفتم خب اگر قرار بود این رو توی c++
پیادهسازی کنم، باید اول یه حافظه جدید با یه سایز اضافهتر میساختم (new
)، بعد تمام المنتهای آرایه قبلی رو کپی میکردم و در آخر حافظه قبلی رو delete
میکردم.
ولی اشتباه من همین جا بود که فکر میکردم capacity
همزمان با length
یه دونه یه دونه زیاد میشه. ولی در واقع capacity
جلوتر حرکت میکنه و احتمالا 1.5 برابر میشه. خب در چنین حالتی سر آدم که درد نمیکنه که اگر capacity
کافی برای اضافه کردن المنت جدید وجود داره بیاد کلا یه آرایه جدید بسازه و همهی اون المنتهای قبلی رو هم کپی کنه.
کد#
مثلا کد زیر همین اتفاق رو خیلی جالب داره نشون میده:
package main
import "fmt"
func main() {
s := make([]int, 0, 5)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Println(a)
b := append(s, 6)
fmt.Println(b)
fmt.Println(a)
}
خروجی:
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 6]
اگر همین s
سایزش 4 بود به جای 5 خروجی این میشد:
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 5]
پس همیشه از اسلایس قبلی کپی گرفته نمیشه و یه اسلایس جدید ساخته بشه.