27. Pointer — Solusi PR
File solusi .go ada di
exercises/27/homework/solutions/.
Soal 1 — Titik dengan move
Soal. point dengan
distance dan move.
Cara berpikir. Mulai dari point di bab
tadi. move mengubah field, jadi butuh receiver pointer, dan
distance juga memakainya supaya seragam.
a.move(1, 1) berhasil karena a adalah
variabel, dan Go mengambil lokasinya untukmu.
Solusi yang dikerjakan.
package main
import (
"fmt"
"math"
)
type point struct {
x float64
y float64
}
func (p *point) distance(other point) float64 {
dx := p.x - other.x
dy := p.y - other.y
return math.Sqrt(dx*dx + dy*dy)
}
func (p *point) move(dx float64, dy float64) {
p.x = p.x + dx
p.y = p.y + dy
}
func main() {
a := point{x: 0, y: 0}
b := point{x: 3, y: 4}
fmt.Println(a.distance(b)) // 5
a.move(1, 1)
fmt.Println(a.x, a.y) // 1 1
fmt.Printf("%.3f\n", a.distance(b)) // 3.606
}Kesalahan umum.
- Menulis
func (p point) move(...)tanpa*. Kode ini bisa dikompilasi, tetapi perpindahannya mengubah salinan, danatetap di tempatnya. - Lupa menambahkan
"math"ke import. Go berhenti denganundefined: math.
Soal 2 — Karakter
Soal. Karakter dengan serangan, penyembuhan, dan baris laporan.
Cara berpikir. Ada dua aturan untuk hp:
tidak pernah di bawah 0, dan tidak pernah di atas maxHP.
Masing-masing punya satu tempat, yaitu sebuah if di akhir
metode yang mengubah hp. report memanggil
isAlive pada receiver yang sama.
Solusi yang dikerjakan.
package main
import "fmt"
type character struct {
name string
hp int
maxHP int
}
func newCharacter(name string, hp int) *character {
return &character{name: name, hp: hp, maxHP: hp}
}
func (c *character) takeDamage(amount int) {
c.hp = c.hp - amount
if c.hp < 0 {
c.hp = 0
}
}
func (c *character) heal(amount int) {
c.hp = c.hp + amount
if c.hp > c.maxHP {
c.hp = c.maxHP
}
}
func (c *character) isAlive() bool {
return c.hp > 0
}
func (c *character) report() {
fmt.Printf("%s: %d / %d HP (alive: %v)\n", c.name, c.hp, c.maxHP, c.isAlive())
}
func main() {
keiko := newCharacter("Keiko", 100)
keiko.report()
keiko.takeDamage(30)
keiko.report()
keiko.takeDamage(80)
keiko.report()
keiko.heal(20)
keiko.report()
}Output:
Keiko: 100 / 100 HP (alive: true)
Keiko: 70 / 100 HP (alive: true)
Keiko: 0 / 100 HP (alive: false)
Keiko: 20 / 100 HP (alive: true)
Kesalahan umum.
- Membiarkan
hpturun di bawah 0 atau naik di atasmaxHP. Dua pemeriksaanifditakeDamagedanhealmenjaga nilainya dalam rentang. - Mengisi
hpdinewCharacterdan melupakanmaxHP. Nilai nolnya0, jadihealpertama menjatuhkanhpke0.
Soal 3 — Persegi panjang
Soal. Persegi panjang dengan area dan
perimeter.
Cara berpikir. Tidak ada metode yang mengubah persegi panjangnya, dan field-nya hanya dua dan kecil. Aturan 3 dari bab tadi berlaku, jadi receiver nilai cocok. Receiver pointer juga bisa, dan output-nya akan sama.
Solusi yang dikerjakan.
package main
import "fmt"
type rectangle struct {
w int
h int
}
func (r rectangle) area() int {
return r.w * r.h
}
func (r rectangle) perimeter() int {
return 2 * (r.w + r.h)
}
func main() {
r1 := rectangle{w: 3, h: 4}
r2 := rectangle{w: 10, h: 2}
fmt.Println(r1.area()) // 12
fmt.Println(r1.perimeter()) // 14
fmt.Println(r2.area()) // 20
fmt.Println(r2.perimeter()) // 24
}Kesalahan umum.
- Menyimpan
wdanhdi variabel di luar fungsi. Dengan dua persegi panjang, yang kedua akan menimpa yang pertama. Field itu milik setiap nilai.
Tantangan — Anjing dan temannya
Soal. Anjing yang temannya berupa pointer ke anjing
lain, atau nil.
Cara berpikir. Field *dog berarti
"mengarah ke seekor anjing, atau tidak ke mana-mana".
newDog tidak mengisinya, jadi nilainya mulai dari
nil. describe harus memeriksa nil
sebelum membaca d.friend.name.
Solusi yang dikerjakan.
package main
import "fmt"
type dog struct {
name string
friend *dog
}
func newDog(name string) *dog {
return &dog{name: name}
}
func (d *dog) describe() {
if d.friend == nil {
fmt.Printf("%s has no friend.\n", d.name)
return
}
fmt.Printf("%s is friends with %s.\n", d.name, d.friend.name)
}
func main() {
rex := newDog("Rex")
lassie := newDog("Lassie")
rex.describe()
lassie.describe()
rex.friend = lassie
rex.describe()
lassie.describe()
rex.friend.name = "Lady"
rex.describe()
lassie.describe()
}Output:
Rex has no friend.
Lassie has no friend.
Rex is friends with Lassie.
Lassie has no friend.
Rex is friends with Lady.
Lady has no friend.
Dua baris terakhir menunjukkan pembagiannya. rex.friend
dan lassie mengarah ke anjing yang sama, jadi mengubah nama
lewat yang satu terlihat lewat yang lain. Field dog sebagai
ganti *dog bahkan tidak bisa dikompilasi, dengan pesan
invalid recursive type: dog refers to itself, karena seekor
anjing harus berisi seekor anjing utuh, yang berisi seekor anjing utuh
lagi, tanpa akhir. Pointer punya ukuran yang tetap.
Kesalahan umum.
- Membaca
d.friend.nametanpa pemeriksaannil. Pemanggilandescribepertama crash denganinvalid memory address or nil pointer dereference.
Selesai?
Sekarang kamu bisa mengubah nilai lewat pointer, dan memilih receiver untuk sebuah metode. Berikutnya, Banyak struct bersama menempatkan struct di dalam slice dan map, tempat salinan dari Bab 26 kembali sebagai jebakan.