22. Slice
Slice menampung deretan nilai yang berurutan, dan
bisa bertambah panjang atau memendek. Kamu sudah bertemu dengannya di
Bab 14, ketika strings.Fields dan
strings.Split memberimu daftar kata. Bab ini menunjukkan
cara membuat, mengubah, dan menelusurinya.
Membuat slice
Literal slice adalah tipenya, []string, diikuti
nilai-nilainya di dalam kurung kurawal:
fruits := []string{"apple", "banana", "cherry"}
fmt.Println(fruits[0]) // apple
fmt.Println(fruits[1]) // banana
fmt.Println(fruits[2]) // cherryBaca []string sebagai "slice berisi string".
[] ditulis lebih dulu, lalu tipe setiap item. Semua item
dalam slice punya satu tipe itu, jadi mencampurnya tidak bisa
dikompilasi:
mixed := []int{1, "two", 3}
fmt.Println(mixed)cannot use "two" (untyped string constant) as int value in array or slice literal
Penting: slice dihitung dari 0. Item pertama ada di
posisi 0, bukan 1, seperti yang sudah kamu
ketahui dari string di Bab 12. Hampir semua pemula pernah tersandung
sekali karena hal ini.
fruits[0]adalah"apple", item pertama.fruits[1]adalah"banana", item kedua.fruits[2]adalah"cherry", item ketiga.
Pakai penulisan yang sama untuk mengubah item, dan
fmt.Println untuk melihat seluruh slice:
fruits := []string{"apple", "banana", "cherry"}
fruits[1] = "blueberry"
fmt.Println(fruits) // [apple blueberry cherry]Slice dengan tiga item punya posisi yang sah 0,
1, dan 2. Posisi 3 tidak ada, dan
memintanya membuat program crash saat berjalan:
fruits := []string{"apple", "banana", "cherry"}
position := 3
fmt.Println(fruits[position])panic: runtime error: index out of range [3] with length 3
Ini jenis crash yang sama dengan di Bab 12 dan 17: program berhasil dikompilasi, lalu berhenti.
Panjang dengan len
len memberikan jumlah item, seperti pada string:
fruits := []string{"apple", "banana", "cherry"}
fmt.Println(len(fruits)) // 3
fmt.Println(fruits[len(fruits)-1]) // cherryPosisi sah terakhir selalu len(fruits)-1. Go tidak punya
posisi negatif, jadi begitulah cara mencapai item terakhir.
fruits[-1] tidak bisa dikompilasi:
invalid argument: index -1 (constant of type int) must not be negative
Slice juga bisa kosong. Menulis []string{} membuatnya,
dan kamu akan sering memulai dari sini:
empty := []string{}
fmt.Println(len(empty)) // 0
fmt.Println(empty) // []Perulangan atas slice
Bab 18 memakai range untuk menghitung.
range juga menelusuri slice, satu item setiap kali.
Nilainya saja
range memberimu dua hal di setiap putaran, posisi dan
nilai. Kalau kamu hanya ingin nilainya, tulis _ di tempat
posisi:
for _, fruit := range fruits {
fmt.Println(fruit)
}Output:
apple
banana
cherry
Perulangan berhenti sendiri setelah item terakhir, dan pada slice kosong isinya tidak pernah berjalan. Kamu tidak bisa membiarkan posisi tidak terpakai begitu saja, karena setiap variabel yang kamu deklarasikan harus dipakai:
i declared and not used
Posisi dan nilai bersama-sama
Kalau kamu juga butuh posisinya, beri nama. Posisi dimulai dari
0:
for i, fruit := range fruits {
fmt.Println(i, fruit)
}Output:
0 apple
1 banana
2 cherry
Untuk nomor yang dimulai dari 1, misalnya daftar bernomor untuk pengguna, tambahkan 1 saat mencetak:
for i, fruit := range fruits {
fmt.Printf("%d. %s\n", i+1, fruit)
}Output:
1. apple
2. banana
3. cherry
Kalau kamu hanya ingin posisinya, tulis
for i := range fruits.
Variabel nilai menyimpan salinan item. Mengubahnya tidak mengubah slice. Untuk mengubah item, pakai posisinya:
scores := []int{1, 2, 3}
for _, score := range scores {
score = score * 10
fmt.Println(score)
}
fmt.Println(scores)
for i := range scores {
scores[i] = scores[i] * 10
}
fmt.Println(scores)Output:
10
20
30
[1 2 3]
[10 20 30]
Buka exercises/22/01-slice-loop.go. Tambahkan buah
keempat dan jalankan lagi. len(fruits) dan perulangannya
menyesuaikan tanpa perubahan lain.
Slice dari teks
Sekarang slice dari Bab 14 berguna. strings.Fields
memisahkan pada spasi sepanjang apa pun, dan strings.Split
memisahkan pada teks yang kamu berikan:
words := strings.Fields("go north now")
fmt.Println(len(words)) // 3
for _, word := range words {
fmt.Println(word)
}
parts := strings.Split("red,green,blue", ",")
for i, part := range parts {
fmt.Println(i, part)
}Ini adalah exercises/22/05-from-text.go. Program ini
mencetak:
3
go
north
now
0 red
1 green
2 blue
Menambah dan menghapus item
Slice tidak bertambah panjang kalau kamu menugaskan nilai melewati
ujungnya. Coba di exercises/22/04-past-the-end.go, yang
menugaskan ke fruits[3] pada slice berisi tiga item.
Program ini berhasil dikompilasi, lalu crash:
3
panic: runtime error: index out of range [3] with length 3
Untuk menambah di akhir, pakai append. Fungsi ini
menerima slice dan nilai baru, lalu mengembalikan slice yang lebih
panjang. Kamu harus menyimpan hasilnya, biasanya dengan
menugaskannya kembali ke variabel yang sama:
fruits := []string{"apple", "banana"}
fruits = append(fruits, "cherry")
fmt.Println(fruits[len(fruits)-1]) // cherry
fmt.Println(len(fruits)) // 3Tanpa penugasan, program tidak bisa dikompilasi:
fruits := []string{"apple", "banana"}
append(fruits, "cherry")
fmt.Println(fruits)append(fruits, "cherry") (value of type []string) is not used
Menugaskan hasilnya ke variabel lain bisa dikompilasi, dan slice aslinya tetap seperti semula:
fruits := []string{"apple", "banana"}
more := append(fruits, "cherry")
fmt.Println(fruits) // [apple banana]
fmt.Println(more) // [apple banana cherry]Menambah di depan butuh satu langkah lagi. Buat slice baru berisi
satu item, lalu tambahkan slice lama kepadanya. ... setelah
slice memberikan item-itemnya satu per satu, seolah-olah kamu
mengetiknya:
fruits := []string{"apple", "banana", "cherry"}
fruits = append([]string{"apricot"}, fruits...)
fmt.Println(fruits[0]) // apricot
fmt.Println(fruits[1]) // appleUntuk menghapus item di ujung, pakai pemotongan dari Bab 12. Cara kerjanya pada slice sama dengan pada string, dan sekali lagi kamu menugaskan hasilnya kembali:
fruits := []string{"apricot", "apple", "banana", "cherry"}
fruits = fruits[:len(fruits)-1]
fmt.Println(fruits) // [apricot apple banana]
fruits = fruits[1:]
fmt.Println(fruits) // [apple banana]Untuk menghapus dari tengah, simpan bagian sebelum item dan tambahkan bagian sesudahnya:
fruits := []string{"apple", "banana", "cherry", "date"}
i := 1
fruits = append(fruits[:i], fruits[i+1:]...)
fmt.Println(fruits) // [apple cherry date]Awalnya ini terlihat aneh, jadi baca per bagian.
fruits[:i] adalah semua yang sebelum posisi i.
fruits[i+1:] adalah semua yang sesudahnya.
append menggabungkan keduanya, tanpa posisi i.
Contoh-contoh ini ada di
exercises/22/02-insert-remove.go.
Jangan mengisi item dengan "" untuk "menghapusnya",
seperti fruits[1] = "". Itu mengganti nilainya tetapi
mempertahankan posisinya, sehingga ada lubang di slice-mu.
len tetap menghitungnya.
Slice berbagi item
Slice berperilaku berbeda dari angka dan string dalam satu hal penting: menugaskan slice ke variabel lain tidak menyalin item-itemnya.
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a[0]) // 99a dan b menamai item yang sama. Untuk
salinan sungguhan, buat slice baru dengan panjang yang tepat memakai
make, lalu isi dengan copy:
a := []int{99, 2, 3}
c := make([]int, len(a))
copy(c, a)
c[0] = 0
fmt.Println(a[0]) // 99
fmt.Println(c[0]) // 0make([]int, len(a)) membuat slice berisi nol sebanyak
itu. copy lalu menyalin item dari argumen keduanya ke
argumen pertamanya.
Berbagi yang sama terjadi kalau kamu memberikan slice ke fungsi. Fungsi yang mengubah item juga mengubahnya untuk pemanggil. Parameter int tidak berperilaku seperti ini, seperti kata Bab 21:
func doubleAll(numbers []int) {
for i := range numbers {
numbers[i] = numbers[i] * 2
}
}
func main() {
scores := []int{10, 20}
doubleAll(scores)
fmt.Println(scores) // [20 40]
}Menambah ke slice berbeda. append di dalam fungsi
memberikan slice yang lebih panjang ke variabel milik fungsi itu, dan
pemanggil tidak pernah melihatnya. Jadi fungsi yang memanjangkan slice
harus mengembalikan hasilnya, dan pemanggil harus menyimpannya, persis
seperti append sendiri:
func addBonusWrong(numbers []int) {
numbers = append(numbers, 100)
}
func addBonus(numbers []int) []int {
return append(numbers, 100)
}
func main() {
scores := []int{20, 40}
addBonusWrong(scores)
fmt.Println(scores) // [20 40]
scores = addBonus(scores)
fmt.Println(scores) // [20 40 100]
}Ada satu efek lagi yang perlu diketahui. Menghapus dari tengah memindahkan item di dalam penyimpanan yang dibagi, jadi variabel lain yang berbagi item itu melihat itemnya bergeser:
fruits := []string{"apple", "banana", "cherry"}
other := fruits
fruits = append(fruits[:1], fruits[2:]...)
fmt.Println(fruits) // [apple cherry]
fmt.Println(other) // [apple cherry cherry]Kalau kamu masih butuh versi lamanya, buat salinan lebih dulu.
Contoh-contoh ini ada di exercises/22/03-sharing.go.
PR
Starter PR ada di exercises/22/homework/. Setiap starter
bisa langsung dijalankan dan mencetak satu baris pengganti.
Soal 1 — Game favorit
Buka exercises/22/homework/01-favourite-games.go. Buat
slice berisi lima nama game. Dengan perulangan range, cetak
masing-masing sebagai 1. Minecraft,
2. Terraria, dan seterusnya.
Soal 2 — Jumlahkan slice
Buka exercises/22/homework/02-sum-list.go. Slice berisi
angka sudah dideklarasikan di atas. Hitung dan cetak jumlahnya.
Soal 3 — Sisip dan hapus
Buka exercises/22/homework/03-insert-remove.go. Starter
ini punya slice pendek. Lakukan empat hal, dan cetak seluruh slice
setelah setiap langkah:
- Tambahkan item baru di akhir.
- Sisipkan item lain di posisi 0.
- Hapus item terakhir.
- Hapus item di posisi 1.
Tantangan — Balik slice
Buka exercises/22/homework/04-reverse.go. Definisikan
fungsi reverse(items []string) []string yang mengembalikan
slice baru dengan item terbalik, tanpa mengubah aslinya. Uji
pada slice berisi string, dan cetak sebelum dan sesudahnya.
Stuck atau sudah selesai? Buka halaman solusi PR.