Konstruk inline adalah perulangan dan percabangan yang dibuka saat kompilasi, sehingga setiap iterasi menjadi salinan kode tersendiri di dalam biner. Zig menyediakan inline for dan inline while untuk kebutuhan ini, dan keduanya menuntut iterable yang diketahui pada waktu kompilasi. Topik ini membahas kapan konstruk inline diperlukan, pembentukan tabel pangkat pada waktu kompilasi, serta hubungan konstruk inline dengan fungsi berparameter comptime. Hasil akhirnya adalah kemampuan memilih antara perulangan biasa dan perulangan inline secara sadar.
Konsep#
Perulangan yang dibuka saat kompilasi#
Perulangan biasa menghasilkan satu rangkaian instruksi yang diulang pada waktu berjalan, sedangkan inline for meminta kompilator menuliskan tubuh perulangan berkali-kali, satu salinan untuk setiap iterasi. Proses ini disebut unrolling, yaitu membuka gulungan perulangan menjadi rangkaian blok yang berurutan. Karena setiap iterasi berdiri sendiri, nilai indeksnya menjadi konstanta tersendiri pada salinannya masing-masing.
Sifat itulah yang membuat inline for wajib pada dua situasi, yaitu pemanggilan fungsi berparameter comptime di dalam tubuh dan akses @field dengan nama yang berubah per iterasi. inline while memberikan perilaku yang sama dengan bentuk perulangan bersyarat, lengkap dengan ekspresi lanjutan sebagaimana while biasa. Kedua konstruk menuntut iterable atau kondisinya diketahui saat kompilasi, sehingga slice yang diterima fungsi pada waktu berjalan tidak dapat menjadi iterable inline.
Pembukaan berdampak pada ukuran biner, sehingga pilihan ini layak dipertimbangkan dan tidak dipakai otomatis. Perulangan biasa tetap lebih tepat untuk data berukuran besar pada waktu berjalan, sedangkan konstruk inline unggul ketika jumlah iterasi kecil dan setiap iterasi menuntut konstanta. Angka iterasi yang besar membuat salinan tubuh menumpuk dengan cepat, sehingga jumlah iterasi inline biasanya dijaga tetap kecil.
// tidak-dijalankan
inline for (0..3) |i| {
// tiga salinan blok ini dibuat saat kompilasi,
// dengan i bernilai 0, 1, dan 2 pada salinannya masing-masing
std.debug.print("iterasi {d}\n", .{i});
}Membangun tabel pangkat saat kompilasi#
Tabel konstanta adalah penggunaan konstruk waktu kompilasi yang paling terlihat hasilnya. Program berikut membentuk tabel pangkat bilangan dua, yaitu 2 pangkat 0 sampai 2 pangkat 7, sepenuhnya saat kompilasi melalui blok berlabel pada tingkat container.
const std = @import("std");
fn pangkatDua(comptime n: u32) u64 {
return @as(u64, 1) << @intCast(n);
}
// tabel pangkat dua dibangun sepenuhnya saat kompilasi
const TABEL = blk: {
var hasil: [8]u64 = undefined;
for (0..8) |i| {
hasil[i] = pangkatDua(i);
}
break :blk hasil;
};
pub fn main() void {
for (TABEL, 0..) |nilai, i| {
std.debug.print("2^{d} = {d}\n", .{ i, nilai });
}
}2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128Blok berlabel blk: adalah ekspresi yang menghitung nilai array saat kompilasi, dan break :blk hasil mengirimkan hasilnya sebagai nilai konstanta TABEL. Kata kunci inline tidak diperlukan di dalam blok tersebut karena seluruh tubuhnya sudah berada pada konteks comptime, sehingga perulangan for biasa dijalankan oleh kompilator. Perulangan di dalam main hanya membaca elemen tabel dan mencetaknya, dan tidak ada satu operasi pangkat pun dihitung pada waktu berjalan karena seluruh nilainya sudah tertanam di dalam biner.
Tabel semacam ini lazim dipakai untuk konversi satuan dengan faktor tetap, pengali statistik, atau deret konstanta yang ditentukan spesifikasi. Keuntungannya muncul dua kali, yaitu tidak ada kode inisialisasi pada waktu berjalan dan tidak ada risiko tabel tertinggal kosong. Kegagalan perhitungan tabel menghentikan kompilasi, sehingga kesalahan konstantanya tertangkap sebelum program pernah berjalan.
# jalankan program dari direktori topik
zig run tabel-pangkat.zig2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128Contoh Lengkap#
inline while dan pemanggilan yang menuntut konstanta#
Konstruk inline paling diperlukan ketika tubuh perulangan memanggil fungsi berparameter comptime. Program berikut memanggil kuadrat untuk lima nilai sekaligus, dan setiap pemanggilan merupakan instansiasi tersendiri karena parameternya harus konstanta waktu kompilasi.
const std = @import("std");
fn kuadrat(comptime n: u32) u32 {
return n * n;
}
pub fn main() void {
// perulangan dibuka saat kompilasi: lima pemanggilan kuadrat berbeda
comptime var i: u32 = 1;
inline while (i <= 5) : (i += 1) {
std.debug.print("kuadrat {d} = {d}\n", .{ i, kuadrat(i) });
}
}kuadrat 1 = 1
kuadrat 2 = 4
kuadrat 3 = 9
kuadrat 4 = 16
kuadrat 5 = 25Penghitung i dideklarasikan dengan comptime var, sehingga nilai per iterasi diketahui saat kompilasi dan setiap salinan tubuh memanggil kuadrat dengan konstanta yang berbeda. Apabila inline while diganti while biasa, kompilasi berhenti dengan pesan unable to resolve comptime value karena kuadrat(i) menuntut konstanta yang tidak tersedia pada waktu berjalan. Perbaikannya bukan mengubah fungsi, melainkan membuka perulangannya dengan konstruk inline.
Pola ini menjelaskan kehadiran inline for pada fungsi cetakSemua di topik refleksi, yaitu akses @field menuntut nama field yang menjadi konstanta pada setiap iterasi. Hubungan kedua mekanisme itu dua arah, karena fungsi berparameter comptime memperluas daftar kasus yang memerlukan perulangan inline. Perulangan biasa tetap dipertahankan untuk bagian yang tidak menuntut konstanta, sebagaimana main pada program tabel pangkat membaca nilainya saat berjalan.
# jalankan program dari direktori topik
zig run unroll.zigkuadrat 1 = 1
kuadrat 2 = 4
kuadrat 3 = 9
kuadrat 4 = 16
kuadrat 5 = 25Kesalahan umum#
Kesalahan pada topik ini berpusat pada pemilihan antara perulangan biasa dan konstruk inline. Tiga kasus berikut paling sering muncul.
- Memakai
forbiasa untuk memanggil fungsi berparameter comptime. Kompilator menolaknya dengan pesanunable to resolve comptime valuebeserta catatanargument to comptime parameter must be comptime-known. Gantifordenganinline for, atauwhiledenganinline while, agar setiap iterasi menyediakan konstanta tersendiri. - Menjadikan slice runtime sebagai iterable inline.
inline foratas parameter[]const u8yang diterima fungsi ditolak dengan pesaninline loop condition must be comptime-known, karena isinya baru diketahui saat berjalan. Gunakanforbiasa untuk slice, dan sisakan konstruk inline untuk iterable konstanta seperti rentang0..N. - Memakai konstruk inline pada semua perulangan besar. Setiap iterasi menambah salinan kode, sehingga biner membesar tanpa manfaat apabila tubuhnya tidak menuntut konstanta waktu kompilasi. Cadangkan
inline fordaninline whileuntuk kasus yang memang memerlukannya, dan biarkan perulangan biasa mengelola data pada waktu berjalan.
Ringkasan#
inline fordaninline whilemembuka perulangan saat kompilasi, sehingga setiap iterasi menjadi salinan tersendiri dengan konstanta indeksnya.- Blok berlabel seperti
blk:menghitung nilai konstanta tingkat container saat kompilasi, dan perulangan di dalamnya sudah berjalan pada konteks comptime. - Konstruk inline wajib apabila tubuh perulangan memanggil fungsi berparameter comptime atau mengakses
@fielddengan nama yang berubah. - Iterable untuk konstruk inline wajib diketahui saat kompilasi, sedangkan slice runtime tetap diperulangkan dengan
forbiasa.
Referensi#
- comptime — Zig Language Reference
— Konstruk
inline fordaninline whilebeserta syarat iterable konstantanya.