Manipulasi Tabel

25.1 Manipulasi Tabel

Mari kita meniru sikap tersebut: Sekarang, kita juga ingin mengatur suatu latar belakang warna untuk jendela (window). Kita akan berasumsi bahwa spesifikasi / pengelompokkan warna yang terakhir terdiri atas tiga bilangan, dimana masing-masing bilangan merupakan komponen warna pada RGB. Pada umumnya, di dalam C, bilangan- bilangan tersebut merupakan bilangan bulat (integer) dalam beberapa cakupan seperti [0,255]. Di dalam Lua, karena semua bilangan merupakan bilangan riil, kita dapat menggunakan cakupan yang semakin natural yaitu [0,1].

Suatu pendekatan sederhana di sini akan meminta user untuk menetapkan masing-masing komponen di dalam suatu variabel global yang berbeda :

-- configuration file for program `pp' width = 200 height = 300 b ackground_red = 0.30 background_green = 0.10 background_blue = 0

Pendekatan ini mempunyai dua kelemahan, yaitu terlalu bertele-tele (program riil memerlukan sejumlah warna yang berbeda-beda untuk latar belakang window, latar depan window, latar belakang menu, dan lain-lain). Dan tidak ada pendefinisian ulang untuk warna-warna yang umum, sedemikian sehingga, user dengan sederhana dapat menulis pendefinisian warna, misalnya seperti background = WHITE. Untuk menghindari kelemahan ini, kita akan menggunakan suatu table untuk menghadirkan suatu warna :

background = {r=0.30, g=0.10, b=0}

Penggunaan suatu tabel memberi struktur yang lebih pada script. Saat ini user (atau aplikasi) sangat mudah mendefinisikan ulang warna-warna yang kemudian digunakan untuk file konfigurasi :

BLUE = {r=0, g=0, b=1} ... background = BLUE

Untuk mendapatkan nilai-nilai tersebut, kita dapat melakukan hal sebagai berikut :

lua_getglobal(L,"background"); if (!lua_istable(L, -1))

error(L, "`background' is not a valid color table");

red = getfield("r"); green = getfield("g"); blue = getfield("b");

Seperti biasanya, pertama kita mendapatkan nilai dari latar belakang variabel global dan memastikan bahwa ini merupakan suatu tabel. Berikutnya, kita menggunakan getfield untuk mendapatkan masing-masing komponen warna. Fungsi ini bukan merupakan bagian dari API, kita harus mendefinisikannya sebagai berikut :

#define MAX_COLOR 255

/* assume that table is on the stack top */ int getfield (const char *key) {

int result; lua_pushstring(L, key); lua_gettable(L, -2); /* get background[key] */ if (!lua_isnumber(L, -1))

error(L, "invalid component in background color"); result = (int)lua_tonumber(L, -1) * MAX_COLOR; lua_pop(L, 1); /* remove number */ return result;

Lagi-lagi kita menghadapi masalah polymorphism. Terdapat banyak kemungkinan munculnya banyak versi dari fungsi getfield, bermacam-macam jenis kunci (key), jenis nilai, penanganan kesalahan (error handling), dan lain-lain. Lua API menawarkan fungsi tunggal, yaitu lua_gettable. Lua_gettable menerima posisi dari tabel di dalam stack, mengeluarkan (pop) key dari stack, dan memasukkan (push) nilai-nilai yang sesuai. Private Getfield milik kita, berasumsi bahwa tabel berada di atas stack, maka setelah memasukkan (push) key ( lua_pushstring), tabel akan berada pada indeks -2. Sebelum kembali, getfield mengeluarkan (pop) nilai yang didapat dari stack, untuk meninggalkan stack tersebut pada tingkat yang sama yaitu pada saat sebelum adanya panggilan.

Kita akan melanjutkan contoh menjadi sedikit lebih jauh dan memperkenalkan nama-nama warna pada user. User masih dapat menggunakan table warna, tapi user juga dapat menggunakan nama-nama warna yang telah dikenal secara umum. Untuk menerapkan hal ini, kita memerlukan table warna pada aplikasi C milik kita :

struct ColorTable { char *name; unsigned char red, green, blue; } colortable[] = { {"WHITE", MAX_COLOR, MAX_COLOR, MAX_COLOR}, {"RED", MAX_COLOR, 0, 0}, {"GREEN", 0, MAX_COLOR, 0}, {"BLUE", 0, 0, MAX_COLOR}, {"BLACK", 0, 0, 0}, ... {NULL, 0, 0, 0} /* sentinel */ };

Implementasi kita akan menciptakan variabel global dengan nama-nama warna tersebut dan menginisialisai variabel-variabel ini menggunakan tabel warna. Hasilnya sama dengan jika user mempunyai bentuk berikut pada script :

WHITE = {r=1, g=1, b=1} RED = {r=1, g=0, b=0} ...

Satu-satunya perbedaan dari warna yang telah didefinisikan user adalah bahwa aplikasi pada

C mendefinisikan warna-warna ini sebelum menjalankan script milik user.

Untuk menentukan isi tabel, kita harus mendefinisikan sebuah fungsi yang berguna sebagai alat bantu, yaitu setfield. Setfield mendorong indeks dan nilai pada stack dan kemudian memanggil lua_settable :

/* assume that table is at the top */ void setfield (const char *index, int value) { lua_pushstring(L, index); lua_pushnumber(L, (double)value/MAX_COLOR); lua_settable(L, -3); }

Seperti fungsi API yang lain, lua_settable bekerja untuk banyak tipe yang berbeda, maka lua_settable mendapatkan semua operand-nya dari stack. Lua_settable menerima table indeks sebagai suatu argumen dan mengeluarkan key dan juga nilainya. Fungsi setfield berasumsi bahwa sebelum adanya panggilan, table berada pada puncak stack (indeks -1). Setelah memasukkan (push) indeks dan nilainya, table akan berada pada indeks -3.

Fungsi setcolor mendefinisikan suatu warna yang tunggal. Fungsi ini harus membuat sebuah table, menetapkan bidang yang sesuai, dan menugaskan table tersebut ke variable global yang sesuai.

void setcolor (struct ColorTable *ct) { lua_newtable(L); /* creates a table */ setfield("r", ct->red); /* table.r = ct->r */ setfield("g", ct->green); /* table.g = ct->g */ setfield("b", ct->blue); /* table.b = ct->b */ lua_setglobal(ct->name); /* `name' = table */ }

Fungsi lua_newtable menciptakan sebuah table kosong dan memasukkannya (push) ke dalam stack. setfield melakukan panggilan untuk menetapkan isi table. Dan akhirnya lua_setglobal mengeluarkan (pop) table dan menetapkannya sebagai nilai dari global dengan nama yang telah diberikan.

Dengan fungsi sebelumnya itu, pengulangan berikut akan mendaftarkan semua warna di dalam lingkungan aplikasi global :

int i = 0; while (colortable[i].name != NULL) setcolor(&colortable[i++]);

Perlu diingat bahwa aplikasi tersebut harus dapat melaksanakan pengulangan sebelum menjalankan script milik user. Terdapat pilihan lain untuk menerapkan penamaan warna. Sebagai pengganti variable global, user dapat menunjuk nama-nama warna tersebut dengan menggunakan string, penulisannya ditentukan seperti background = "BLUE". Oleh karena itu, background bisa menjadi table ataupun string. Dengan implementasi ini, suatu aplikasi tidak harus melakukan apa pun sebelum menjalankan script milik user. Sebagai gantinya, aplikasi tersebut memerlukan kerja yang lebih untuk mendapatkan warna. Saat mendapatkan nilai dari variabel background, aplikasi harus menguji apakah nilai mempunyai tipe string dan setelah itu melihat tipe string pada tabel warna : Perlu diingat bahwa aplikasi tersebut harus dapat melaksanakan pengulangan sebelum menjalankan script milik user. Terdapat pilihan lain untuk menerapkan penamaan warna. Sebagai pengganti variable global, user dapat menunjuk nama-nama warna tersebut dengan menggunakan string, penulisannya ditentukan seperti background = "BLUE". Oleh karena itu, background bisa menjadi table ataupun string. Dengan implementasi ini, suatu aplikasi tidak harus melakukan apa pun sebelum menjalankan script milik user. Sebagai gantinya, aplikasi tersebut memerlukan kerja yang lebih untuk mendapatkan warna. Saat mendapatkan nilai dari variabel background, aplikasi harus menguji apakah nilai mempunyai tipe string dan setelah itu melihat tipe string pada tabel warna :

const char *name = lua_tostring(L, -1); int i = 0; while (colortable[i].name != NULL &&

strcmp(colorname, colortable[i].name) != 0) i++;

if (colortable[i].name == NULL) /* string not found? */ error(L, "invalid color name (%s)", colorname); else { /* use colortable[i] */

red = colortable[i].red; green = colortable[i].green; blue = colortable[i].blue;

} }else if (lua_istable(L, -1)) {

red = getfield("r"); green = getfield("g"); blue = getfield("b"); }else

error(L, "invalid value for `background'");

Apakah sebenarnya yang merupakan pilihan terbaik? Pada program C, penggunaan string untuk menandakan pilihan bukan merupakan suatu latihan yang baik, sebab compiler tidak bisa mendeteksi kesalahan eja atau kesalahan penulisan. Pada Lua, bagaimanapun juga, variable global tidak harus dideklarasikan, maka Lua tidak memberi isyarat terhadap kesalahan saat user salah mengeja atau menulis nama warna. Jika user menulis WITE sebagai ganti WHITE (PUTIH), maka variable background menerima nil (nilai WITE merupakan suatu variabel yang tidak didefinisikan), dan itu adalah semua yang diketahui oleh aplikasi : nilai background adalah nil. Tidak akan ada informasi lain mengenai apa sebenarnya yang salah. Pada sisi lain, jika kita menggunakan string, nilai background bisa salah meng-eja stringnya. Maka aplikasi dapat menambah informasi tersebut pada error message. Aplikasi tersebut juga dapat membandingkan string dengan mengabaikan kasus, sedemikian sehingga seorang user dapat menulis “WHITE”, “white” ataupun “White”. Lebih dari itu, jika script milik user sedikit dan terdapat banyak warna, mungkin saja aneh untuk mendaftarkan beratus-ratus warna (dan untuk menciptakan beratus-ratus tabel dan variabel global) hanya untuk user untuk memilih beberapa saja. Dengan string, anda telah menghindari kejadian ini.