- Kompiler Zig yang menyediakan kompilasi kode C dan cross-compilation secara bawaan adalah bahasa paling menakjubkan yang pernah ditemui penulis dalam 45 tahun pengalamannya
- Dengan fitur unik seperti eksekusi saat waktu kompilasi, variabel dengan ukuran bit arbitrer, dan lingkungan blok test, Zig melampaui sekadar pengganti C/C++ dan menawarkan cara pemrograman yang benar-benar baru
- Dengan sintaks yang ringkas dan jelas seperti deklarasi variabel melalui inferensi tipe, struct anonim, dan label break, Zig dapat dipelajari dengan cepat
- Dengan pengujian modul secara independen melalui blok test dan fungsi bawaan @breakpoint Zig mendukung debugging kode teroptimasi
- Dengan dukungan pemrograman tingkat rendah melalui bit field dan operasi bit, Zig mencapai efisiensi dan ketangguhan sekaligus, sambil mengintegrasikan keunggulan bahasa interpreter ke dalam bahasa terkompilasi
Pendahuluan
- Dalam 45 tahun pengalaman, tidak ada bahasa yang semenarik Zig
- Zig bukan sekadar bahasa baru, melainkan alat yang secara mendasar mengubah cara pemrograman
- Melihatnya hanya sebagai pengganti C atau C++ adalah peremehan besar
- Tujuan tulisan ini adalah memperkenalkan fitur-fitur Zig yang sederhana namun menarik, dan membantu programmer agar bisa cepat mulai menggunakannya
- Masih ada lebih banyak fitur yang memengaruhi tingkat penerimaan Zig di industri
Kompiler Zig
- Menyediakan kompilasi kode C dan cross-compilation secara default tanpa konfigurasi tambahan, yang memberi dampak besar di industri
- Instalasi dilakukan dengan mengunduh kompiler sesuai prosesor/OS dari halaman unduhan Zinglang, lalu mengekstraknya dan menyalinnya ke direktori yang diinginkan
- Di Windows 10, file zip x86_64 dapat disalin ke "Program Files", lalu nama direktori root diubah menjadi "zig-windows-x86_64" agar tidak perlu mengubah variabel lingkungan Path saat versi diperbarui
- Setelah menambahkan jalur direktori root ke variabel lingkungan Path, kompiler dapat digunakan dalam mode CLI
- Untuk membangun program "Hello World!", disarankan merujuk ke bagian "Getting Started" di situs resmi
Konsep dan perintah utama
Deklarasi variabel
- Deklarasi variabel terdiri dari bagian pertama berupa aksesibilitas (
pub atau dihilangkan), var/const, dan nama variabel, bagian kedua berupa deklarasi tipe, serta bagian ketiga berupa inisialisasi
- Hanya bagian pertama dan ketiga yang wajib, dan tipe dapat diinferensikan dari nilai inisialisasi
- Contoh:
var sum : usize = 0;
- Variabel yang dideklarasikan tanpa
pub hanya dapat diakses di dalam modul (mirip variabel static di C)
- Deklarasi variabel
pub tidak dianjurkan, dan fungsi pub sebaiknya diminimalkan untuk menurunkan coupling dan meningkatkan cohesion
Struct, struct anonim, dan blok test
- Literal struct anonim yang dibungkus oleh
.{ dan } digunakan untuk menginisialisasi elemen struct lain atau membuat struct baru dengan elemen yang sudah diinisialisasi
.{ } adalah literal struct anonim kosong
- Bentuk
struct { } adalah deklarasi struct
- Blok test memungkinkan kompilasi dan eksekusi pengujian tanpa file executable
Bit field
- Bit field dideklarasikan sebagai field bertipe ukuran tertentu di dalam packed struct
- Pointer dapat menunjuk ke bit field tertentu
Loop for
- Sintaks Zig lebih jelas daripada C, tetapi menggunakan interval terbuka
[0..9) alih-alih [0..8]
- Deklarasi tipe, inisialisasi, pengujian, dan kenaikan variabel loop
i ditangani secara otomatis
Array
[_] mendefinisikan array dengan ukuran yang tidak diketahui, diikuti oleh tipe elemen dan inisialisasi
- Contoh:
var grid = [_]u8{0} ** 81; menginisialisasi 81 elemen u8 dengan 0
- Ukuran array diinferensikan dari argumen pengulangan inisialisasi
- Dalam lingkungan test, elemen array dapat dijumlahkan sambil diiterasi
- Variabel yang dideklarasikan di antara
| pada loop for otomatis diasumsikan bertipe sama dengan elemen array
usize adalah bilangan bulat tak bertanda alami platform (u64 di 64-bit, u32 di 32-bit)
Pointer multi-item
- Agar pointer array dapat menggunakan operasi aritmetika pointer, pointer itu harus secara eksplisit dideklarasikan sebagai pointer multi-item seperti
[*]const i32
- Meski array bersifat const, pointer tetap dapat dideklarasikan sebagai var
Dereferensi pointer
- Pointer yang diberi alamat posisi array individual tidak dapat diperbarui dengan aritmetika pointer
- Dereferensi pointer menggunakan
ptr.*
Label break
- Berbagai tugas seperti inisialisasi array dapat dilakukan pada waktu kompilasi
- Label break menambahkan
: setelah nama blok, lalu mengembalikan nilai dari blok dengan break
0.. adalah rentang tak terbatas yang dimulai dari 0
- Dalam loop for, variabel diinisialisasi dan dinaikkan secara otomatis, dan loop berhenti setelah posisi terakhir array diproses
- Array tidak harus diinisialisasi secara eksplisit dengan
undefined
Fungsi di Zig
- Fungsi dideklarasikan dengan
fn dan secara default bersifat static (hanya dipakai di dalam file)
- Jika dideklarasikan dengan
pub fn, fungsi dapat di-import dari file lain
- Fungsi dapat di-"inlined"
- Pointer fungsi menempatkan
const di depan dan prototipe fungsi di belakang
Pemrograman berorientasi objek di Zig
- Struct dapat memiliki fungsi
- Dalam contoh stack, maksimal 81 elemen bertipe
StkNode dapat disimpan
- Operator
++ dan -- tidak ada di Zig, dan sebagai gantinya digunakan += serta -=
- Pointer stack adalah bilangan bulat yang digunakan sebagai indeks array
stk
- Pointer
self tidak diteruskan secara eksplisit sebagai parameter, melainkan diasumsikan secara tidak langsung sebagai pointer ke instance stack tempat fungsi dipanggil
- Saat dipanggil seperti
stack.pop(), self adalah pointer ke stack (mirip this di Java/C++)
- Fungsi
init() adalah konstruktor stack
- Fungsi
pop dan push di-"inlined"
Membangun dan menjalankan program Zig
Membangun executable
- Untuk membuat executable, dibutuhkan fungsi
main yang menandai entry point program
- Program sederhana dapat menempatkan fungsi main di file yang sama
- Untuk debugging modul secara independen, fungsi main dapat ditambahkan di akhir file lalu dikomentari kembali setelah debugging selesai
- Perintah kompilasi:
zig build-exe -O ReleaseFast program.zig
Menjalankan blok test modul
- Ini adalah salah satu fitur terbaik Zig, digunakan untuk testing dan prototyping
- Blok test dimulai dengan
test "message" { dan diakhiri dengan }
- "message" adalah string yang ditampilkan saat test dijalankan
- Blok test dijalankan secara independen dari executable, dan executable final tidak menjalankan test
- Perintah test:
zig test module.zig
- Blok test di
example.zig menguji fungsi set dan print; set menerima string desimal sebagai parameter, dan print mencetak header "Input Grid" lalu menampilkan grid
Output di Zig
- Pernyataan
std.debug.print memanggil fungsi print di debug.zig dari pustaka standar Zig std
- Parameter pertama adalah string format, dan parameter kedua adalah struct anonim yang berisi daftar variabel untuk ditampilkan
- Jika tidak ada format, struct tersebut kosong
- Secara default ditampilkan ke stderr
- Tidak seperti
printf di C, Zig dapat memproses string literal dan daftar variabel pada waktu kompilasi
Debugging executable
- Penggunaan debugger tidak mudah kecuali dengan IDE yang memiliki debugger terintegrasi (Eclipse, IntelliJ IDEA) atau kit pengembangan terintegrasi (
w64devkit)
- Integrasi simbol membuat kode membengkak dan mengharuskan kompilasi mode Debug, sehingga menghasilkan kode eksekusi yang jauh kurang efisien
- Zig menyediakan solusi praktis untuk menghindari masalah ini
Fungsi bawaan @breakpoint
- Dengan menyisipkan
@breakpoint(); ke dalam source code, program akan berhenti di titik tersebut saat dijalankan di debugger
- Ini adalah fitur berguna untuk melakukan debugging kode Zig teroptimasi tanpa simbol
- Jika
std.debug.print digunakan tepat sebelum @breakpoint(); untuk mencetak variabel yang ingin dilacak, nilai variabel saat itu dapat diperiksa
- Dalam contoh
debug_example.zig, kode untuk mencetak grid dan variabel di dalam fungsi set, serta @breakpoint();, disisipkan
- Perintah build:
zig build-exe debug_example.zig
- Setelah memanggil
debug_example.exe dengan debugger seperti gdb, jalankan program dengan perintah r
- Gunakan perintah
c untuk melanjutkan sambil melacak isi grid dan variabel
- Jika terus menekan Enter untuk melanjutkan, dapat dipastikan bahwa nilai-nilai grid sesuai dengan blok test di
example.zig
Pemrograman tingkat rendah di Zig
Representasi matriks
- Digit desimal disimpan di matriks sebagai bilangan bulat standar
u8
- Grid input berbentuk string, tetapi karakter ASCII secara internal dikonversi menjadi bilangan bulat
u8
- Penyimpanan angka disusun secara linear per baris ke dalam array
grid yang memiliki 81 posisi: var grid = [_]u8{0} ** 81;
- Untuk memverifikasi kebenaran grid, elemen perlu diakses per baris dan kolom
- Array berisi 9 pointer dibuat, dan masing-masing pointer menunjuk ke awal tiap baris
- Label break digunakan untuk mengembalikan nilai dari blok kode:
break :fill9x9 m; menginisialisasi matrix dengan m
- Notasi akses elemen:
element = matrix[i][j]
Merepresentasikan digit desimal sebagai bit
- Konsep inti adalah mengganti digit desimal bulat
i dengan bilangan bulat code
i ∈ [1,9] → code = 2ⁱ⁻¹
i = 0 → code = 0
- Posisi satu-satunya bit
1 yang disetel dalam code adalah i-1 (saat i berada di antara 1 dan 9); jika tidak, semua bit bernilai 0
- Disediakan tabel nilai
code untuk tiap digit (1→1, 2→2, 3→4, ..., 9→256)
Menghitung code di Zig
- Saat
c tidak sama dengan 0, nilai code dihitung dengan operator shift kiri: code = @as(u9,1) << (c-1);
- Di Zig, konstanta harus memiliki ukuran yang sesuai agar operasi dapat dikompilasi dan hasilnya dapat ditugaskan ke variabel
code dideklarasikan sebagai tipe u9 (karena nilai maksimum 256 memerlukan minimal 9 bit)
- Zig dapat memiliki variabel dengan ukuran bit arbitrer
- Fungsi bawaan
@as digunakan untuk melakukan cast konstanta 1 ke tipe u9
Representasi grid dengan bit field
Grid bit field per baris
- Array
lines mencerminkan seluruh grid dengan merepresentasikan tiap baris sebagai bilangan bulat 9-bit: var lines = [_]u9{0} ** 9;
- Saat array diakses dengan baris
i, keberadaan angka tertentu di baris itu dapat diperiksa dengan operasi bit AND (&): lines[i] & code
- Jika hasil operasi adalah 0, angka tersebut belum ada di baris i; jika tidak, berarti ada duplikasi
Grid bit field per kolom
- Array
columns mencerminkan seluruh grid dengan merepresentasikan tiap kolom sebagai bilangan bulat 9-bit: var columns = [_]u9{0} ** 9;
- Saat array diakses dengan kolom
j, keberadaan angka tertentu di kolom itu dapat diperiksa dengan operasi bit AND: columns[j] & code
- Jika hasil operasi adalah 0, angka tersebut belum ada di kolom j; jika tidak, berarti ada duplikasi
Aturan Sudoku
- Saat memasukkan angka baru ke grid Sudoku kosong, angka itu tidak boleh sudah ada di seluruh baris, kolom, dan sel yang memuat elemen baru tersebut
- Sel adalah masing-masing dari 9 grid 3x3 yang dipisahkan garis tebal
- Setiap elemen tertentu dalam grid 9x9 memiliki baris, kolom, dan sel unik yang memuatnya
- Pada grid contoh, sel pertama berisi 3, 5, 6, 8, 9, sedangkan 1, 2, 4, dan 7 tidak ada
- Array
lines dan columns menangani pemeriksaan duplikasi untuk baris dan kolom
- Diperlukan array baru untuk memeriksa duplikasi pada sel
Grid bit field per sel
- Array
cells mencerminkan seluruh grid dengan merepresentasikan tiap sel sebagai bilangan bulat 9-bit: var cells = [_]u9{0} ** 9;
- Akses ke
cells akan lebih mudah jika diperlakukan sebagai matriks 3x3
- Array
cell diisi dengan cara yang mirip seperti pada matriks 9x9
- Dari baris dan kolom elemen pada grid 9x9 asli, perlu ditentukan baris dan kolom pada matriks
cell
- Karena pembagian bilangan bulat sangat lambat, array
cindx = [_]usize{ 0,0,0, 1,1,1, 2,2,2 }; digunakan untuk memberikan hasil pembagian
- Saat matriks diakses dengan baris
i dan kolom j dari elemen grid 9x9, keberadaan angka tertentu di sel elemen itu dapat diperiksa dengan operasi bit AND: cell[cindx[i]][cindx[j]] & code
- Jika hasil operasi adalah 0, angka tersebut belum ada di sel; jika tidak, berarti ada duplikasi
Menguji duplikasi elemen
- Dengan menggabungkan semua elemen sebelumnya pada baris, kolom, dan sel yang sama menggunakan bit OR (
|), lalu melakukan bit AND dengan code milik elemen tersebut, verifikasi duplikasi elemen selesai dilakukan
if (((lines[i]|columns[j]|cell[cindx[i]][cindx[j]])&code) != 0) {
unreachable;
}
- Jika hasilnya 0, elemen tersebut belum ada di baris, kolom, maupun sel
- Jika hasilnya bukan 0, program berhenti dengan menjalankan perintah
unreachable
- Ini adalah cara paling sederhana di Zig untuk secara eksplisit menunjukkan runtime error
- Kode sebenarnya juga mencetak detail lokasi terjadinya error
- Contoh: jika
0 tepat setelah 8 pertama dalam string input diganti menjadi 5, maka terjadi error karena di baris 3 kolom 1 sudah ada angka 5
Memperbarui struktur data
- Di fungsi
set, loop for ganda berinteraksi per baris untuk menyalin tiap elemen baru dari string input s ke grid
- Variabel
k menjaga indeks karakter input baru dalam string s
- Karakter dikonversi ke
u4 (variabel c) dengan mengurangkan '0'
- Jika elemen baru yang akan dimasukkan ke grid bukan 0 (
c != 0), code yang dihitung dengan operasi shift kiri disalin ke tiap grid cerminan
- Lalu dilakukan bit OR (
|=) dengan grid cerminan terkait:
lines[i] |= code;
columns[j] |= code;
cell[cindx[i]][cindx[j]] |= code;
- Tidak perlu menguji secara eksplisit apakah nilai
c berada di antara 1 dan 9 — karena overflow akan terjadi saat operasi shift dijalankan
- Contoh: jika
0 tepat setelah 8 pertama pada string input diganti menjadi :, maka terjadi runtime error
- Mengganti
0 yang sama dengan / juga akan menimbulkan runtime error serupa
- Program hanya bekerja saat nilainya berada di rentang 1 sampai 9, yaitu ketika grid input hanya berisi digit desimal
- Banyak grid Sudoku di web merepresentasikan
0 sebagai ., sehingga fungsi set memiliki baris if (s[k] == '.') c = 0;
- Ini memudahkan untuk melewati operasi shift karena nilai
c adalah 0
Prototyping dan ketangguhan
- Error yang dipaksakan pada dua bagian di atas mendemonstrasikan fitur penting Zig
- Salah satunya adalah ketangguhan Zig — pada operasi shift, perilaku yang salah tidak diizinkan dan akan ditangkap saat runtime
- Meski semua upaya tampak diarahkan ke efisiensi, ini adalah contoh khas ketika performa dipertukarkan dengan ketangguhan
- Di C, jika operasi shift kehilangan bit, itu menjadi masalah programmer, dan hal ini diterjemahkan menjadi performa lebih baik dari instruksi assembler tertentu
- Fitur lainnya adalah kemungkinan menggunakan blok test untuk prototyping
- Kemungkinan penerapannya tak terhitung banyaknya, dan penerapan yang ditunjukkan di sini hanyalah debugging situasi tertentu saat error terjadi
- Fitur-fitur ini saja sudah memberikan kemampuan menakjubkan yang sangat jarang ditemukan dalam bahasa pemrograman, terutama bahasa pemrograman terkompilasi
Kesimpulan
- Zig terdiri dari tiga elemen kunci: kompatibilitas dengan C, cross-compilation, dan instalasi yang sederhana
- Karakteristik ini menunjukkan potensinya untuk menjadi standar baru bahasa pemrograman sistem
- Banyak keunggulan yang sebelumnya hanya ditemukan pada bahasa interpreter secara bertahap berpindah ke bahasa terkompilasi untuk memberikan performa yang lebih baik
- Zig sangat menonjol kemiripannya dengan bahasa interpreter melalui konsep eksekusi saat waktu kompilasi
- Hal ini membuat Zig terasa sangat berbeda dan kuat, sekaligus juga lebih sulit dipahami
Belum ada komentar.