Enum dan bit_set

Enum ordinal dan penyimpanan himpunan enum dengan bit_set.

Daftar isi

Enum menetapkan sekumpulan konstanta bernama yang disusun berurutan, sedangkan bit_set menyimpan himpunan nilai enum dalam bit. Topik ini membahas deklarasi enum dengan nilai implisit maupun eksplisit, penggunaan enum sebagai label status, iterasi enum, serta operasi himpunan pada bit_set beserta ukuran penyimpanannya. Setelah membaca, label status dan kumpulan flag dapat dinyatakan tanpa konstanta angka yang tersebar di berbagai tempat.

Deklarasi enum dan nilai ordinal#

Enum dideklarasikan dengan kata kunci :: enum diikuti daftar anggota. Tanpa nilai eksplisit, anggota pertama bernilai 0 dan anggota berikutnya naik satu demi satu. Nilai dapat ditetapkan secara eksplisit pada anggota mana pun, dan anggota setelahnya melanjutkan dari nilai tersebut. Enum dicetak oleh fmt.println sebagai nama anggotanya, sedangkan konversi int(nama_enum) menampilkan nilai ordinal di balik nama itu.

Program berikut menyatukan seluruh contoh topik ini, mulai dari enum, iterasi, hingga bit_set.

odin main.odin
package main

import "core:fmt"

Hari :: enum {
        Senin,
        Selasa,
        Rabu,
        Kamis,
        Jumat,
}

Status_Berkas :: enum {
        Baru = 10,
        Diproses,
        Selesai = 50,
        Ditolak,
}

main :: proc() {
        // enum implisit: nilai dimulai dari 0
        hari_ini := Hari.Selasa
        fmt.println(hari_ini, int(hari_ini))

        // enum dengan nilai eksplisit dan kelanjutan otomatis
        s := Status_Berkas.Diproses
        fmt.println(s, int(s))
        fmt.println(Status_Berkas.Ditolak, int(Status_Berkas.Ditolak))

        // perbandingan dan percabangan
        fmt.println("hari_ini == Hari.Selasa:", hari_ini == Hari.Selasa)
        switch hari_ini {
        case .Senin:
                fmt.println("hari pertama")
        case .Selasa, .Rabu, .Kamis:
                fmt.println("hari kerja")
        case .Jumat:
                fmt.println("hari terakhir")
        }

        // iterasi enum
        for h in Hari {
                fmt.println(h)
        }

        // bit_set: himpunan enum
        jadwal := bit_set[Hari]{}
        jadwal += {Hari.Senin, Hari.Rabu}
        fmt.println("jadwal:", jadwal)
        fmt.println("Rabu dijadwalkan?", Hari.Rabu in jadwal)
        fmt.println("Kamis dijadwalkan?", Hari.Kamis in jadwal)

        jadwal += {Hari.Jumat}
        jadwal -= {Hari.Rabu}
        fmt.println("sesudah perubahan:", jadwal)

        // operasi himpunan
        x := bit_set[Hari]{Hari.Senin, Hari.Selasa}
        y := bit_set[Hari]{Hari.Selasa, Hari.Jumat}
        fmt.println("gabungan :", x | y)
        fmt.println("irisan   :", x & y)
        fmt.println("selisih  :", x &~ y)
        fmt.println("komplemen:", ~x)

        // ukuran penyimpanan
        fmt.println("ukuran bit_set[Hari] =", size_of(bit_set[Hari]), "byte")
        fmt.println("ukuran [5]bool =", size_of([5]bool), "byte")
}
bash
odin run main.odin -file
text Keluaran
Selasa 1
Diproses 11
Ditolak 51
hari_ini == Hari.Selasa: true
hari kerja
Senin
Selasa
Rabu
Kamis
Jumat
jadwal: bit_set[Hari]{Senin, Rabu}
Rabu dijadwalkan? true
Kamis dijadwalkan? false
sesudah perubahan: bit_set[Hari]{Senin, Jumat}
gabungan : bit_set[Hari]{Senin, Selasa, Jumat}
irisan   : bit_set[Hari]{Selasa}
selisih  : bit_set[Hari]{Senin}
komplemen: bit_set[Hari]{Rabu, Kamis, Jumat}
ukuran bit_set[Hari] = 1 byte
ukuran [5]bool = 5 byte

Keluaran menunjukkan kelanjutan nilai eksplisit secara nyata. Pada Status_Berkas, anggota Diproses dinyatakan tepat setelah Baru = 10, sehingga ordinalnya 11, sedangkan Ditolak mengikuti Selesai = 50 dan bernilai 51. Nilai eksplisit semacam ini berguna ketika angka tersebut harus stabil dari versi ke versi, misalnya karena tersimpan dalam arsip berkas.

Percabangan dan iterasi enum#

Enum memperjelas label status karena setiap nilai memiliki nama dan tipe tersendiri. Pernyataan switch memilih langkah berdasarkan enum tersebut, dan penulisan case menggunakan pemilih implisit seperti .Senin yang menyebut anggota tanpa mengulang nama tipenya. switch pada enum wajib mencakup seluruh anggota, atau diberi awalan #partial switch bila hanya sebagian yang dibahas, sehingga anggota yang terlewat terdeteksi sebagai error pada waktu kompilasi. Anggota yang salah nama juga langsung ditolak compiler.

Perulangan for h in Hari mengunjungi setiap anggota enum satu per satu sesuai urutan deklarasi. Bentuk ini memungkinkan tabel atau penjadwalan dibangun dari enum itu sendiri, seperti mencetak kelima hari pada keluaran program di atas. Enum yang nilai anggotanya melompat tetap dapat diiterasi, dengan urutan kunjungan mengikuti urutan penulisannya.

Tipe penyimpanan enum sendiri juga dapat ditetapkan dengan menyebut tipe dasar pada deklarasi, misalnya Hari :: enum u8 { ... }. Tanpa penentuan, compiler memilih tipe yang cukup untuk seluruh anggota, sedangkan penentuan eksplisit mengunci ukurannya, seperti enum u8 yang menempati satu byte. Kebutuhan ini muncul ketika nilai enum berpindah melalui format berkas atau protokol yang ukuran fieldnya sudah ditetapkan.

bit_set: himpunan flag berbasis bit#

bit_set menyimpan himpunan nilai enum dalam satu bit per anggota, dengan tipe ditulis bit_set[Hari] untuk seluruh anggota enum Hari. Anggota ditambahkan dengan jadwal += {Hari.Senin, Hari.Rabu}, dilepas dengan jadwal -= {Hari.Rabu}, dan keanggotaan diuji dengan Hari.Rabu in jadwal. Literal himpunan {...} di sisi kanan operator memuat satu atau beberapa anggota sekaligus, sehingga penambahan massal tidak memerlukan perulangan.

Operasi antarhimpunan menggunakan operator bit yang lazim pada bahasa Odin. Operator | membentuk gabungan, & membentuk irisan, &~ membentuk selisih, sedangkan ~ membentuk komplemen terhadap seluruh anggota enum. Pada keluaran program, irisan x & y hanya menyisakan Selasa sebagai satu-satunya anggota yang hadir di kedua himpunan, sedangkan komplemen ~x berisi tiga hari yang tidak hadir di x. Kombinasi ini mencakup kebutuhan umum himpunan flag, misalnya menandai hari kerja lalu mengecualikan hari libur.

Pemeriksaan keanggotaan juga dapat dilakukan per anggota dengan menggabungkan perulangan enum dan operator in, misalnya for h in Hari yang setiap langkahnya menguji h in jadwal. Pola ini menelusuri himpunan sesuai urutan deklarasi enum, sehingga laporan yang dihasilkan memiliki urutan yang tetap tanpa perlu menyimpan urutan tambahan. Untuk data jadwal piket atau izin hari kerja yang jarang berubah, satu byte himpunan sudah mencakup seluruh minggu.

Ukuran penyimpanan bit_set#

Kekompakan bit_set terlihat pada ukurannya. Himpunan lima anggota Hari menempati satu byte karena satu byte memuat hingga delapan bit, sedangkan array tetap [5]bool menempati lima byte karena setiap bool disimpan penuh satu byte. Perbedaan itu membesar pada enum dengan anggota yang lebih banyak, dan aturan kasarnya tetap sama, yaitu setiap delapan anggota menambah satu byte. Bila ukuran atau pola penyimpanan perlu dikunci, misalnya untuk konversi data antarsistem, tipe penyimpanan dinyatakan eksplisit seperti bit_set[Hari; u8] yang menetapkan backing u8.

Kekompakan itu bukan tanpa biaya. bit_set hanya menjawab ya atau tidak untuk keanggotaan, sehingga jumlah anggota yang terpilih tetap perlu dihitung secara manual. Data per hari yang lebih kaya daripada boolean, misalnya jam kerja, juga tetap membutuhkan array atau struct tersendiri. Untuk kumpulan flag yang memang biner, komposisi enum dan bit_set memberikan deklarasi yang padat sekaligus mudah dibaca.

Ringkasan#

  • Enum memberi nama pada nilai berurutan, dengan nilai implisit mulai dari 0 atau nilai eksplisit yang dilanjutkan otomatis oleh anggota berikutnya.
  • Enum cocok sebagai label status dan diperiksa dengan switch yang menggunakan pemilih implisit seperti .Senin.
  • Perulangan for x in Enum mengunjungi seluruh anggota sesuai urutan deklarasi.
  • bit_set[Enum] menyimpan himpunan enum dalam bit, dengan penambahan +=, pelepasan -=, keanggotaan in, dan operasi |, &, &~, serta ~.
  • Ukuran bit_set jauh lebih kecil daripada array bool karena setiap anggota hanya menempati satu bit.

Referensi#