26. Method dan self — Solusi PR
File solusi .lua ada di
exercises/26/homework/solutions/.
Soal 1 — Objek counter
Soal. Sebuah tabel counter dengan method
inc, get, dan reset.
Cara berpikir. Tabel menyimpan state di
count; setiap method membaca atau mengubah
self.count. Definisi dan pemanggilan dengan titik dua
mengurus koneksinya.
Solusi lengkap.
local counter = { count = 0 }
function counter:inc()
self.count = self.count + 1
end
function counter:get()
return self.count
end
function counter:reset()
self.count = 0
end
counter:inc()
counter:inc()
counter:inc()
print(counter:get()) -- 3
counter:reset()
print(counter:get()) -- 0Kesalahan umum.
- Menulis
function counter.inc(self)— bentuk yang diperluas ini memang bisa jalan, tapi terkesan seperti lupa memakai titik dua. Pilih satu gaya saja dan konsisten. - Memanggil
counter.inc()bukancounter:inc(). Tanpa titik dua,selfbernilainildan assignment-nya gagal.
Soal 2 — Anjing dengan bark
Soal. Dua tabel anjing dengan nama masing-masing,
masing-masing memanggil :bark()-nya sendiri.
Cara berpikir. Dua tabel yang masing-masing punya
field name bisa berbagi method yang sama, atau
masing-masing mendefinisikan bark-nya sendiri. Keduanya
bisa jalan; cara kedua boros memori dan jarang sepadan.
Solusi lengkap.
local function makeDog(name)
local dog = { name = name }
function dog:bark()
print(self.name .. ": Woof!")
end
return dog
end
local rex = makeDog("Rex")
local lassie = makeDog("Lassie")
rex:bark() -- Rex: Woof!
lassie:bark() -- Lassie: Woof!Versi yang lebih sederhana tanpa fungsi pembantu:
local rex = { name = "Rex" }
function rex:bark()
print(self.name .. ": Woof!")
end
local lassie = { name = "Lassie" }
function lassie:bark()
print(self.name .. ": Woof!")
end
rex:bark()
lassie:bark()Keduanya menghasilkan output yang sama. Bab 27 mengembangkan cara pertama menjadi class yang sesungguhnya.
Kesalahan umum.
- Berbagi satu method
barkdengan menulislassie.bark = rex.bark. Cara itu sebenarnya bisa jalan — hasilnya tetap mencetakLassie: Woof!karenaself.namedibaca dari tabel mana pun yang memulai pemanggilan — dan ini merupakan optimasi yang valid. Asalkan kamu tahu apa yang sedang dilakukan.
Soal 3 — Objek kalkulator
Soal. Sebuah tabel calc dengan method aritmetika yang memperbarui nilai internal.
Solusi lengkap.
local calc = { value = 0 }
function calc:add(n)
self.value = self.value + n
end
function calc:sub(n)
self.value = self.value - n
end
function calc:mul(n)
self.value = self.value * n
end
function calc:show()
print("Value: " .. self.value)
end
calc:add(10)
calc:show() -- Value: 10
calc:mul(3)
calc:show() -- Value: 30
calc:sub(5)
calc:show() -- Value: 25Kesalahan umum.
- Mengembalikan nilai baru dari setiap method alih-alih memperbarui
self.value. Keduanya boleh, tapi soal meminta pembaruan di tempat (in-place), jadi method-nya memodifikasiself.value.
Tantangan — Stack
Soal. Sebuah stack LIFO dengan method
push, pop, peek,
size.
Solusi lengkap.
local stack = { items = {} }
function stack:push(v)
table.insert(self.items, v)
end
function stack:pop()
return table.remove(self.items)
end
function stack:peek()
return self.items[#self.items]
end
function stack:size()
return #self.items
end
stack:push("a")
stack:push("b")
stack:push("c")
print(stack:size()) -- 3
print(stack:peek()) -- c
print(stack:pop()) -- c
print(stack:pop()) -- b
print(stack:size()) -- 1Tanpa argumen posisi, table.insert dan
table.remove bekerja di ujung list — posisi yang tepat
untuk operasi stack.
Kesalahan umum.
- Menggunakan
table.insert(self.items, 1, v)dantable.remove(self.items, 1). Cara itu menyisipkan di depan — itu queue, bukan stack — dan lebih lambat karena setiap item lain harus bergeser. - Lupa mengembalikan nilai dari
popdanpeek. Nilai kembalian default-nya adalahnil, yang akan membuat pengujian gagal tanpa pesan error.
Selesai?
Bab berikutnya mengembangkan pola ini lebih jauh. Dengan metatable, kamu tidak perlu lagi menulis boilerplate yang sama untuk setiap "class" dan bisa berbagi tabel method di banyak instance.