Satu nilai table di Lua mampu berperan sebagai array, yaitu bagian urutan dengan kunci bilangan bulat berurutan, sekaligus sebagai dictionary, yaitu bagian kamus dengan kunci sembarang. Topik ini membahas kedua wajah tersebut, konsekuensi pengindeksan berbasis satu, perbedaan ipairs dan pairs, jebakan operator # pada array berlubang, serta praktik yang menjaga bagian urutan tetap dapat diandalkan. Seluruh contoh dapat dijalankan langsung dengan penerjemah Lua dan disertai keluaran aslinya.
Dua wajah table#
Table pada dasarnya adalah struktur asosiatif yang memetakan kunci ke nilai. Kunci boleh berupa nilai apa pun selain nil dan NaN (not a number), sedangkan nilai yang disimpan pun bebas tipe. Wajah array lahir dari konvensi menyimpan data pada kunci bilangan bulat berurutan yang dimulai dari 1, sedangkan wajah dictionary menggunakan kunci sembarang di luar deretan itu. Kedua wajah tersebut tidak saling meniadakan karena keduanya berada dalam satu table yang sama. Contoh berikut menyimpan daftar barang pada kunci 1 sampai 3 dan informasi pengadaan pada kunci string dalam table yang sama.
stok = {"pensil", "buku", "kertas"}
stok.pengadaan = "gudang tengah"
stok[2] = "buku tulis"
print(stok[1], stok[2], stok[3])
print(stok.pengadaan)
print(stok["pengadaan"])pensil buku tulis kertas
gudang tengah
gudang tengahBaris pertama keluaran memuat tiga nilai yang dipisahkan karakter tab karena print menerima beberapa argumen sekaligus. Posisi kedua diisi ulang menjadi “buku tulis”, sedangkan pengadaan berdampingan dengan keduanya pada table yang sama. Bentuk stok.pengadaan hanyalah singkatan untuk stok[“pengadaan”], sehingga kedua baris terakhir keluarannya identik. Penulisan dengan tanda titik hanya sah untuk kunci string yang memenuhi aturan penamaan pengenal, sedangkan kunci lain harus ditulis dalam kurung siku.
Pengindeksan berbasis satu#
Bagian urutan table dimulai dari indeks 1, berbeda dengan bahasa seperti C dan Python yang memulai dari 0. Konsekuensinya, elemen pertama selalu ditemukan pada t[1], perulangan numerik berjalan dari 1 sampai jumlah elemen, dan indeks 0 hanyalah kunci biasa yang biasanya tidak berisi. Operator # mengembalikan jumlah elemen bagian urutan sehingga batas akhir perulangan dapat ditulis #kota tanpa penghitungan manual. Contoh berikut menunjukkan ketiga perilaku tersebut pada satu table.
kota = {"Bandung", "Semarang", "Surabaya"}
print(kota[1])
print(kota[0])
print(#kota)
for i = 1, #kota do
print(i, kota[i])
endBandung
nil
3
1 Bandung
2 Semarang
3 SurabayaKeluaran menegaskan bahwa kota[1] memuat “Bandung” sedangkan kota[0] bernilai nil karena tidak pernah diisi. Operator # mengembalikan 3 sesuai banyaknya elemen, dan perulangan for menyusuri ketiga kota dengan nomor 1, 2, dan 3. Bagi pembaca yang terbiasa dengan bahasa berbasis 0, kebiasaan mengakses elemen pertama lewat indeks 0 perlu diwaspadai karena hasilnya selalu nil pada table yang baru dibuat.
ipairs dan pairs#
Perulangan for generik menyediakan dua iterator bawaan untuk table. Fungsi ipairs menelusuri bagian urutan mulai dari indeks 1 dan berhenti tepat sebelum nil pertama ditemukan, sehingga hanya cocok untuk bagian yang rapi. Fungsi pairs mengunjungi semua kunci yang tersimpan, termasuk kunci di luar bagian urutan, tetapi tidak memberikan jaminan apa pun tentang urutan kunjungannya. Contoh berikut menggunakan table yang sama untuk membedakan keduanya.
nilai = {80, 75, nil, 90}
for i, v in ipairs(nilai) do
print(i, v)
end1 80
2 75nilai = {80, 75, nil, 90}
for k, v in pairs(nilai) do
print(k, v)
end1 80
2 75
4 90ipairs berhenti setelah indeks 2 karena nilai[3] berisi nil. pairs justru tetap mengunjungi kunci 4 sebab kunci 3 memang tidak tersimpan, karena pemberian nilai nil pada sebuah kunci sama artinya dengan menghapusnya. Urutan kunjungan yang tampil pada pairs adalah pilihan implementasi, bukan janji bahasa, sehingga program tidak boleh bergantung padanya. Pada kunci string, urutan kunjungan pairs bahkan dapat berbeda dari satu proses ke proses lainnya.
Operator # dan array berlubang#
Operator # hanya dapat diandalkan ketika bagian urutan tidak memiliki lubang (hole), yaitu nilai nil di tengah deretan kunci bilangan bulat. Bila ada lubang, manual Lua menyatakan hasilnya tidak terdefinisi, dan implementasinya memang bebas mengembalikan batas mana pun yang masuk akal. Contoh berikut membandingkan satu table penuh dengan dua table berlubang pada penerjemah yang sama.
a = {10, 20, 30}
b = {10, 20, nil, 40}
c = {10, 20, nil}
print("a =", #a)
print("b =", #b)
print("c =", #c)a = 3
b = 4
c = 2Hasil #a bernilai 3 dan sesuai harapan. Untuk b, penerjemah mengembalikan 4 meskipun elemen ketiga tidak ada, dan untuk c mengembalikan 2. Nilai-nilai itu bukan kontrak bahasa, melainkan pilihan implementasi yang dapat berubah antarversi maupun antarcara pembuatan table. Program yang menyimpan data penting tidak boleh menaruh nil di bagian urutan.
Praktik aman pada bagian urutan#
Dua kebiasaan menjaga bagian urutan tetap tertib. Pertama, gunakan table.insert dan table.remove alih-alih menugaskan nil, karena keduanya menggeser elemen dan memperbarui batas secara konsisten. Kedua, bila pengelolaan indeks dilakukan sendiri, catat jumlah elemen pada kunci lain dalam table yang sama sehingga batas akhir tidak bergantung pada operator #. Contoh berikut membangun antrean piket sederhana.
antrean = {}
table.insert(antrean, "Ali")
table.insert(antrean, "Budi")
table.insert(antrean, "Citra")
table.remove(antrean, 1)
print(#antrean)
for i, nama in ipairs(antrean) do
print(i, nama)
end2
1 Budi
2 Citratable.remove menghapus Ali pada posisi pertama lalu menggeser dua nama tersisa, sehingga # mengembalikan 2 dan tidak ada lubang tersisa. Pola kedua menggunakan penghitung pada kunci lain, sebagaimana catatan sensor suhu berikut.
sensor = {jumlah = 0}
local function catat(t, suhu)
t.jumlah = t.jumlah + 1
t[t.jumlah] = suhu
end
catat(sensor, 24.5)
catat(sensor, 25.0)
catat(sensor, 23.8)
print(sensor.jumlah)
for i = 1, sensor.jumlah do
print(i, sensor[i])
end3
1 24.5
2 25.0
3 23.8Kunci jumlah berada di wajah dictionary, sedangkan pengukuran tersimpan pada wajah array table yang sama. Perulangan membaca dari 1 sampai sensor.jumlah sehingga hasilnya pasti tanpa menyentuh operator #. Perhatikan pula bahwa 25.0 dicetak dengan bagian desimal karena penulisan bertitik menghasilkan bilangan pecahan (float).
Kunci di luar string#
Kunci dictionary tidak terbatas pada string. Bilangan, boolean, dan bahkan table atau fungsi dapat menjadi kunci karena pembandingnya adalah identitas nilai, bukan isinya. Dua table yang berbeda selalu dianggap dua kunci yang berbeda sekalipun isinya sama. Contoh berikut menyimpan satu table sebagai kunci lalu mencari nilai dengan kunci table baru yang lain.
konversi = {}
konversi["cm"] = 1
konversi[100] = "satu meter"
konversi[true] = "aktif"
kunci = {nama = "petunjuk"}
konversi[kunci] = "nilai pada kunci table"
print(konversi["cm"])
print(konversi[100])
print(konversi[true])
print(konversi[kunci])
print(konversi[{}])1
satu meter
aktif
nilai pada kunci table
nilBaris terakhir bernilai nil karena penulisan {} membuat table baru dengan identitas yang berbeda dari kunci yang pernah dipasang. Nilai hanya dapat ditemukan kembali melalui kunci yang persis sama, sehingga table yang digunakan sebagai kunci perlu disimpan dalam variabel seperti pada contoh.
Kebutuhan penyajian berurut tetap dapat dipenuhi dengan mengumpulkan kunci ke bagian urutan lalu mengurutkannya dengan table.sort sebelum disusuri kembali.
berat = {Ali = 68, Budi = 75, Citra = 61}
local nama = {}
for k in pairs(berat) do
nama[#nama + 1] = k
end
table.sort(nama)
for i, v in ipairs(nama) do
print(v, berat[v])
endAli 68
Budi 75
Citra 61Ringkasan#
- Satu table menyimpan bagian urutan pada kunci bilangan bulat mulai dari 1 dan bagian kamus pada kunci sembarang secara bersamaan.
- ipairs menelusuri bagian urutan dan berhenti tepat sebelum nil pertama, sedangkan pairs mengunjungi semua kunci tanpa jaminan urutan.
- Hasil # hanya dapat diandalkan pada bagian urutan tanpa lubang, sehingga nil tidak boleh ditugaskan di bagian urutan.
- table.insert dan table.remove menjaga keutuhan bagian urutan, sedangkan penghitung pada kunci lain memberikan batas akhir yang pasti.
Referensi#
- Lua 5.4 Reference Manual — Lua.org — Menetapkan semantik operator panjang serta perilaku ipairs dan pairs pada pustaka dasar.
- Programming in Lua, edisi pertama — Lua.org — Bab 11 membangun array dan struktur data lain di atas table.