if, while, dan for

Menulis percabangan dan perulangan yang terlihat atas rentang dan slice.

Daftar isi

Alur kendali menentukan kapan sebuah langkah dijalankan dan berapa kali langkah itu diulang, sedangkan Zig menyediakannya melalui tiga kata kunci utama, yaitu if, while, dan for. Topik ini membahas percabangan if sebagai pernyataan sekaligus ekspresi, perulangan while dengan klausa lanjutan, serta perulangan for atas rentang dan slice. Setelah membacanya, rekap nilai dan pencarian data dapat ditulis dengan struktur kendali yang tepat, lengkap dengan cara keluar lebih awal dari perulangan bertumpuk.

Konsep#

if, else if, dan else#

Pernyataan if mengeksekusi satu blok kode hanya apabila kondisinya bernilai benar, sedangkan else if dan else menambahkan jalur alternatif setelahnya. Kondisi wajib bertipe bool, yaitu tipe yang hanya memuat true atau false, sehingga perbandingan seperti nilai >= 70 ditulis secara eksplisit. Blok ditulis di antara kurung kurawal, dan tanda kurung pada kondisi tidak diperlukan. Pemeriksaan berhenti pada kondisi benar yang pertama, sehingga urutan penulisan menentukan hasil akhirnya.

Program berikut menentukan kategori dua nilai ujian menurut empat rentang, dari A hingga D.

zig kategori-nilai.zig
const std = @import("std");

pub fn main() void {
    const nilai: u8 = 87;
    if (nilai >= 85) {
        std.debug.print("nilai {d} memperoleh kategori A\n", .{nilai});
    } else if (nilai >= 70) {
        std.debug.print("nilai {d} memperoleh kategori B\n", .{nilai});
    } else if (nilai >= 55) {
        std.debug.print("nilai {d} memperoleh kategori C\n", .{nilai});
    } else {
        std.debug.print("nilai {d} memperoleh kategori D\n", .{nilai});
    }

    const hadir: u8 = 64;
    if (hadir >= 85) {
        std.debug.print("nilai {d} memperoleh kategori A\n", .{hadir});
    } else if (hadir >= 70) {
        std.debug.print("nilai {d} memperoleh kategori B\n", .{hadir});
    } else if (hadir >= 55) {
        std.debug.print("nilai {d} memperoleh kategori C\n", .{hadir});
    } else {
        std.debug.print("nilai {d} memperoleh kategori D\n", .{hadir});
    }
}
text Keluaran
nilai 87 memperoleh kategori A
nilai 64 memperoleh kategori C

Nilai 87 memenuhi syarat pertama sehingga pemeriksaan langsung berhenti, sedangkan nilai 64 melompati dua syarat pertama dan berhenti pada rentang C. Simpan berkas di atas di direktori kerja, lalu jalankan dengan perintah berikut.

bash
# menjalankan program kategori nilai
zig run kategori-nilai.zig
text Keluaran
nilai 87 memperoleh kategori A
nilai 64 memperoleh kategori C

Perintah zig run mengompilasi berkas dan langsung mengeksekusi hasilnya dalam satu langkah. Seluruh contoh pada jalur ini menampilkan teks melalui std.debug.print, yaitu fungsi penulisan bawaan Zig yang menulis ke standard error. Aliran keluaran itu ikut tertangkap penuh oleh terminal, sehingga isi blok Keluaran sama persis dengan teks yang tampil ketika program dijalankan.

if sebagai ekspresi#

Berbeda dari banyak bahasa lain, if pada Zig adalah ekspresi, yakni konstruksi yang menghasilkan nilai dan dapat diberikan langsung kepada variabel. Bentuk ekspresinya wajib memuat else ketika hasilnya dipakai sebagai nilai, karena kedua jalur harus menghasilkan nilai dengan tipe yang sama. Fitur ini menggantikan operator ternary yang dipakai bahasa lain, sehingga tidak ada sintaks tambahan yang perlu dihafal.

zig Contoh: if sebagai ekspresi
// tidak-dijalankan
const terendah = if (a < b) a else b;

Bentuk tanpa else tetap sah secara sintaks, tetapi hasilnya bertipe void karena jalur tanpa else tidak menghasilkan nilai. Karena itu, pemakaiannya sebagai nilai, misalnya const terendah: u8 = if (a < b) a;, ditolak dengan pesan error: expected type 'u8', found 'void'. Perbaikannya berupa penambahan else yang menyediakan nilai jalur kedua, sedangkan bentuk pernyataan yang tidak menyimpan hasil, misalnya if (n > 0) hitung += n;, tetap boleh tanpa else. Pembedaan kedua bentuk ini penting saat menyusun nilai ambang dan penjepitan rentang, sebab hasil ekspresi dapat langsung dipakai tanpa variabel sementara.

while dengan klausa lanjutan#

Perulangan while mengulang bloknya selama kondisi masih bernilai benar, dan kondisi diperiksa ulang pada awal setiap putaran. Zig menyediakan klausa lanjutan, yaitu ekspresi yang dieksekusi setiap kali satu putaran selesai, ditulis setelah titik dua pada baris yang sama. Klausa ini menjadi tempat penambahan pencacah, sehingga pola while (i < n) : (i += 1) menggantikan penulisan i += 1 di akhir blok yang mudah terlupakan.

zig jumlah-transaksi.zig
const std = @import("std");

pub fn main() void {
    const transaksi = [_]u32{ 15000, 22000, 9000, 31000, 18000 };
    var i: usize = 0;
    var total: u32 = 0;
    while (i < transaksi.len) : (i += 1) {
        total += transaksi[i];
    }
    std.debug.print("total belanja: {d}\n", .{total});
    std.debug.print("iterasi: {d}\n", .{i});
}
text Keluaran
total belanja: 95000
iterasi: 5

Program di atas menjumlahkan lima transaksi dengan pencacah i bertipe usize, yaitu tipe bilangan untuk indeks dan panjang koleksi. Klausa (i += 1) dieksekusi setelah badan blok selesai, sehingga pencacah bertambah sebelum kondisi diperiksa ulang dan jumlah putarannya pas lima kali. Di dalam blok, break menghentikan perulangan lebih awal, sedangkan continue melompat ke putaran berikutnya, dan keduanya baru benar-benar dibutuhkan pada pencarian di bagian akhir topik ini.

Contoh Lengkap#

for atas rentang dan slice#

Perulangan for menelusuri koleksi secara langsung tanpa pencacah manual, dan bentuk paling lazimnya menelusuri slice atau array. Setiap elemen diterima melalui capture di dalam pipa, misalnya |n| menampung satu elemen, sedangkan |n, i| menampung elemen beserta indeksnya. Rentang bilangan juga dapat ditelusuri dengan bentuk for (0..n) |i|, yang menghasilkan angka dari 0 hingga n tanpa memuat n itu sendiri.

zig Contoh: for dengan elemen dan indeks
// tidak-dijalankan
for (nilai, 0..) |n, i| {
    std.debug.print("urutan {d}: {d}\n", .{ i, n });
}

Dua atau lebih koleksi dapat ditelusuri sekaligus dalam satu perulangan, dan panjangnya wajib sama karena elemen-elemennya dipasangkan menurut posisi. Bentuk paralel ini menggantikan pola indeks tunggal yang mengambil dua koleksi secara terpisah.

zig Contoh: for paralel atas dua slice
// tidak-dijalankan
for (nama, nilai) |n, v| {
    std.debug.print("{s}: {d}\n", .{ n, v });
}

Kombinasi for dan if menghasilkan program pencarian yang ringkas. Program berikut mencari nilai tertinggi dari enam nilai ujian dengan satu kali penelusuran.

zig maksimum.zig
const std = @import("std");

pub fn main() void {
    const nilai = [_]u8{ 76, 92, 68, 88, 95, 81 };
    var maksimum: u8 = 0;
    for (nilai) |n| {
        if (n > maksimum) {
            maksimum = n;
        }
    }
    std.debug.print("nilai tertinggi: {d}\n", .{maksimum});
}
text Keluaran
nilai tertinggi: 95

Variabel maksimum diawali nol dan diperbarui hanya bila elemen yang sedang ditelusuri lebih besar daripadanya. Pola pembanding berjalan semacam ini cukup satu kali penelusuran, sedangkan menyalin dan mengurutkan koleksi hanya untuk mengambil nilai terbesar adalah kerja yang jauh lebih mahal.

Berhenti lebih awal dengan label#

Perulangan yang bertumpuk membutuhkan cara untuk keluar dari lebih dari satu tingkat sekaligus, dan Zig menyediakannya melalui label. Label ditulis sebelum kata kunci perulangan diikuti titik dua, misalnya cari: for (...). Pernyataan break :cari keluar dari perulangan berlabel itu, sedangkan continue :cari melompat ke putaran berikutnya pada tingkat yang sama, dan label yang tidak dituliskan selalu merujuk perulangan terdalam.

zig cari-remedial.zig
const std = @import("std");

pub fn main() void {
    const siswa = [_][]const u8{ "Ali", "Budi", "Citra" };
    const nilai_mtk = [_]u8{ 74, 58, 91 };
    const nilai_ipa = [_]u8{ 81, 66, 88 };
    var ketemu = false;

    cari: for (siswa, nilai_mtk, nilai_ipa) |nama, mtk, ipa| {
        const nilai = [_]u8{ mtk, ipa };
        for (nilai) |n| {
            if (n >= 70) {
                continue;
            }
            std.debug.print("{s} mengikuti remedial, nilai {d}\n", .{ nama, n });
            ketemu = true;
            break :cari;
        }
    }

    if (!ketemu) {
        std.debug.print("tidak ada siswa yang mengikuti remedial\n", .{});
    }
    std.debug.print("pencarian selesai\n", .{});
}
text Keluaran
Budi mengikuti remedial, nilai 58
pencarian selesai

Program di atas menelusuri tiga slice sejajar, yaitu nama siswa beserta nilai matematika dan IPA miliknya, lalu mencari siswa pertama yang nilainya di bawah 70. Nilai yang memadai dilewati oleh continue tanpa label, sedangkan break :cari menghentikan kedua perulangan sekaligus begitu kasus pertama ditemukan. Tanpa label, break hanya keluar dari perulangan dalam dan pencarian berlanjut ke siswa berikutnya, sehingga hasilnya tidak lagi berupa kasus pertama.

Kesalahan umum#

Kesalahan pertama adalah memberikan kondisi non-boolean kepada if atau while. Kompilator menolak programnya sebelum berjalan dengan pesan error: expected type 'bool', found 'u8', dan perbaikannya adalah menuliskan perbandingan secara eksplisit seperti if (jumlah > 0). Zig tidak memiliki truthiness, yakni penilaian kebenaran otomatis atas nilai non-boolean, sehingga nol tidak pernah dianggap salah dan angka lain tidak pernah dianggap benar.

Kesalahan kedua adalah mengharapkan nilai dari if tanpa else, misalnya const x: u8 = if (a > b) a;. Hasil bentuk tanpa else bertipe void, sehingga kompilator menolaknya dengan pesan error: expected type 'u8', found 'void'. Tambahkan else beserta nilainya, atau ubah bentuknya menjadi pernyataan biasa bila hasilnya tidak disimpan.

Kesalahan ketiga adalah while tanpa klausa lanjutan sekaligus tanpa break, yang menghasilkan perulangan tanpa ujung. Periksa bahwa setiap putaran mendekatkan kondisi menuju penghentian, baik melalui klausa lanjutan, pernyataan break, maupun perubahan nilai di dalam blok. Klausa lanjutan menjadi pilihan paling aman karena pencacah tidak mungkin terlupakan.

Ringkasan#

  • Pernyataan if mengeksekusi bloknya hanya untuk kondisi bertipe bool, dan pemeriksaan berhenti pada kondisi benar yang pertama.
  • if adalah ekspresi yang wajib memuat else bila hasilnya disimpan, sehingga tidak memerlukan operator ternary.
  • while memakai klausa lanjutan seperti : (i += 1) untuk pencacah, sedangkan for menelusuri slice, array, dan rentang 0..n melalui capture.
  • break dan continue dapat ditujukan kepada label, sehingga perulangan bertumpuk dapat keluar dari lebih dari satu tingkat sekaligus.

Referensi#