Menerjemahkan Teks Bilangan dalam Python untuk Pemula

Menerjemahkan Teks Bilangan dalam Python untuk Pemula

Bismillaah Alhamdulillah,

Selain membaca lambang bilangan yang dirangkai, materi serupa yang diajarkan untuk anak kelas 4 SD adalah menulis lambang bilangan. Dalam konteks membaca lambang bilangan (angka), kita perlu mengenali sebuah angka sebagai angka dasar atau satuan (puluhan, ratusan, ribuan, dan seterusnya). Sedangkan untuk konteks menulis lambang bilangan, kita perlu mengenali teks angka, dan menerjemahkannya ke dalam sebuah lambang bilangan.

Dari sisi pemrograman, strategi untuk memecahkan kasus membaca dan menulis lambang bilangan itu cukup berbeda. Untuk membaca lambang bilangan dapat dikatakan relatif lebih mudah dibanding menulis lambang bilangan. Hal ini disebabkan kita harus mengenali teks angka dan satuannya yang bisa bertumpuk (atau sebagai rangkaian) dimana diperlukan pemahaman terhadap prioritas dalam penyusunannya.

Misalkan terdapat teks “seratus dua puluh satu ribu”, bisa jadi dipahami komputer dengan berbagai pemahaman berbeda, seperti:

"seratus dua puluh" dan "satu ribu"
"seratus dua puluh satu" dan "ribu"
"seratus dua puluh ribu" dan "satu"
"seratus" , "dua puluh", dan "satu ribu"
"seratus ribu" dan "dua puluh satu"
... dan lain-lain

Selain itu diperlukan pemahaman terhadap sintaks penulisan yang benar (baku), makna kata (atau kalimat), dan pengelompokan yang benar (valid).

Berikut contoh teks yang juga dapat dianggap tidak lumrah, serta format seperti apa yang dianggap lumrah itu.

"sepuluh dua" -> 10+2, seharusnya "dua belas"
"sepuluh dua" -> 102, seharusnya "seratus dua"

"tiga belas puluh", "tiga belas ratus" -> tidak umum
"tiga belas ribu", "tiga belas juta" -> umum

"dua puluh satu" -> normal
"dua puluh sebelas" -> tidak normal
"dua puluh ribu sebelas" -> normal

... dan lain-lain

Untuk penentuan tata bahasa (grammar) ini saja merupakan tantangan tersendiri, dan tentunya cukup kompleks.

Namun dalam tutorial kali ini saya tidak akan fokus terlalu jauh dalam menentukan aturan yang kompleks seperti tata bahasa tersebut. Jadi harap maklum jika nantinya ditemukan bugs dalam kode yang dibuat.

Bagi yang belum membaca tutorial membaca lambang bilangan sebelumnya, dapat dicek di tautan berikut.


Langkah 1. Desain kamus data dan percobaan dasar

Kamus data yang dibutuhkan pertama kali adalah untuk menyimpan data angka dasar.

kamusAngka = {  
            'nol': '0',  
            'satu': '1',  
            'dua': '2',  
            'tiga': '3',  
            'empat': '4',  
            'lima': '5',  
            'enam': '6',  
            'tujuh': '7',  
            'delapan': '8',  
            'sembilan': '9'  
    }

Kamus ini digunakan untuk mengambil string angka berdasarkan label bilangan yang dipilih. Berikut contoh penggunaannya dalam Python.

# Contoh 1  
kamusAngka['tiga'] # output: '3'

# Contoh 2  
teks = "delapan"  
kamusAngka[teks] # output: '8'

# Contoh 3  
kalimat = "sembilan lima empat satu tiga lima"  
lstKalimat = kalimat.split()

for sat in lstKalimat:  
    if sat in kamusAngka:  
        print(kamusAngka[sat], end="")  
# output: 954135

Kamus berikutnya adalah kamus satuan yang digunakan untuk menyimpan nilai (jangkauan) dari suatu satuan (kecuali satuan bernama “belas”).

kamusSatuan = {  
           'puluh': 1,  
           'ratus': 2,  
           'belas': 2.5,  
           'ribu': 3,  
           'juta': 6,  
           'milyar': 9,  
           'triliun': 12  
       }

Angka di samping nama satuan digunakan untuk dua hal berbeda. Pertama, angka ini dapat digunakan sebagai nilai pangkat dari bilangan 10. Misal untuk satuan “puluh”, menampung nilai ≥ 10¹. Untuk satuan “juta” dapat menampung nilai ≥ 10⁶. Begitu juga satuan lainnya — selain “belas” — nanti akan dijelaskan.

Kedua, angka ini dapat digunakan untuk pembanding antar satuan, yang nantinya digunakan untuk mengelompokkan bilangan sesuai klaster (kelompok) masing-masing yang bersesuaian.

Misalnya terdapat teks “dua puluh satu ribu lima ratus”, yang jika dirunut dari ujung paling kiri, satuan paling tinggi adalah “ribu”. Maka dalam hal ini, semua satuan di sebelah kirinya (yaitu “puluh”) dianggap satu kelompok dengan satuan “ribu”. Berikutnya dibaca lagi ke kanan, ditemukan satuan “ratus” yang berdiri sendiri hanya ditemani angka dasar “lima”. Maka prosesnya secara singkat adalah:

Satuan di kelompok 1 adalah "puluh" dan "ribu", dimana kalimat lengkap yang dapat diambil adalah "dua puluh satu ribu".

Sedangkan satuan di kelompok 2 adalah "ratus", dengan kalimat lengkapnya adalah "lima ratus"

Berikutnya, hasil terjemahan (bilangan) kelompok 1 ditambah dengan kelompok 2, yaitu: 21000 + 500 = 21500.

Dalam kenyataannya, dapat ditemukan 1 atau lebih kelompok dalam sebuah kalimat. Dalam satu kelompok dapat ditemukan 1 kata (angka dasar) atau lebih (disebut: sub kalimat/frasa).

Kamus terakhir adalah kamus pendukung untuk menyimpan nama alias.

kamusAlias = {  
            'sepuluh' : 'satu puluh',  
            'sebelas' : 'satu belas',  
            'seratus' : 'satu ratus',  
            'seribu' : 'satu ribu'  
    }

Dari kamus ini kita dapat mengetahui makna asli dari angka satuan berawalan “se” yang disambung dengan satuan “puluh”, “belas”, “ratus”, dan “ribu”.

Dalam strategi pemrogramannya, kita perlu menelusuri setiap kata dalam kalimat dan dibandingkan dengan semua kamus yang ada. Jika mengandung satuan, maka jangan lupa untuk dijadikan “10 pangkat nilai satuan” terlebih dahulu, baru dikalikan dengan angka dasar di depannya.

Berikut contoh kode versi pendeknya.

Luaran bernama ekspresi dapat kita gunakan untuk memeriksa apakah ekspresi yang dihasilkan sudah sesuai atau valid. Berikutnya luaran evaluasi dengan memanfaatkan fungsi built-in Python bernama eval() digunakan untuk mengevaluasi ekspresi dan menampilkan hasil eksekusi ekspresi (jika dianggap valid).

Kode tersebut di atas hanya mampu mengevaluasi dua (bagian) kata saja, yaitu satu angka dasar dan satu satuan, seperti: “satu juta”, “lima ribu”, dst.


Langkah 2. Strategi untuk penggunaan kamus alias dan angka belasan

Untuk konversi angka/satuan sesuai dengan data di kamus alias, dapat dibuat kode sebagai berikut.

kalimat = "seratus sepuluh ribu"  
lstKalimat = kalimat.split()

lstKalimat = [kamusAlias[x] if x in kamusAlias else x for x in lstKalimat]  
kalimatBaru = " ".join(lstKalimat)  
lstKalimatBaru = kalimatBaru.split()

# isi lstKalimatBaru adalah "['satu', 'ratus', 'satu', 'puluh', 'ribu']"

Jadi, urutan pemeriksaan kata adalah pemeriksaan di kamusAngka, kemudian apakah mengandung satuan “belas”, dan terakhir pemeriksaan di kamusSatuan.

# Untuk kata di kamusAngka selalu ditambah karakter "+" di depannya  
teks += "+" + kamusAngka[sat]

# Untuk kata mengandung satuan "belas" dilakukan penukaran (atau penambahan di depan) antara angka yang ditemukan dengan angka satu.  
teks = teks[:-1] + "1" + teks[-1:]

# Untuk kata satuan  
pengali = 10**kamusSatuan[sat]  
if i == ukuran-1:  
    teks = "(" + teks[:] + ")" + "*" + str(pengali)  
else:  
    teks += "*" + str(pengali)**

Untuk kata yang mengandung satuan (sesuai kamusSatuan), jika berada di posisi terakhir, maka perlu ditambahkan tanda kurung di depan dan belakang ekspresi (teks) sebelum dilakukan perkalian. Jika ada di tengah tidak perlu dilakukan. Berikut ini contoh kodenya.

Potongan kode tersebut di atas hanya dapat digunakan untuk mengevaluasi kalimat yang memiliki satu kelompok (klaster) saja, seperti:

Kalimat: "seratus ribu" Ekspresi: (+1*100)*1000
Hasil evaluasi: 100000

Kalimat: "dua belas ribu" Ekspresi: (+12)*1000
Hasil evaluasi: 12000

Kalimat: "lima ratus tiga puluh lima juta" Ekspresi: (+5*100+3*10+5)*1000000
Hasil evaluasi: 535000000


Langkah 3. Mengevaluasi kalimat dan membuat pengelompokan sub kalimat

Pada kode sebelumnya, evaluasi kalimat berhasil digunakan jika dalam kalimat tersebut hanya terdapat satu kelompok (klaster) saja. Sedangkan jika digunakan pada kalimat yang mengandung lebih dari satu kelompok (klaster), akan menghasilkan luaran yang tidak sesuai. Berikut contoh luarannya.

Kalimat: "seratus ribu lima ratus", harusnya ditulis: 100500 Ekspresi: (+1*100*1000+5)*100
Hasil evaluasi: 10000500 # sepuluh juta lima ratus

Kalimat: "dua ratus lima puluh", harusnya ditulis: 250 Ekspresi: (+2*100+5)*10
Hasil evaluasi: 2050 # dua ribu lima puluh

Hal ini disebabkan terdapat kesalahan baca yang mengakibatkan salah peletakan tanda “kurung” dan peletakan simbol “kali” dan “tambah” yang tidak pada tempatnya. Alhasil, sintaks ekspresi juga tidak sesuai dengan data faktual.

Salah satu solusinya adalah dengan memecah kalimat utama menjadi beberapa kelompok (klaster) berdasarkan jenjang satuan. Misalnya:

Kalimat: "seratus ribu lima ratus", dipecah menjadi "seratus ribu" dan "lima ratus"

Kalimat: "dua ratus lima puluh", dipecah menjadi "dua ratus" dan "lima puluh"

Logikanya adalah nilai satuan yang lebih tinggi ada di kiri, dan lebih rendah ada di kanan, misalnya pada kalimat “dua ratus dua puluh”. Untuk kalimat model seperti ini dapat dipecah menjadi dua sub kalimat.

Sedangkan pada kalimat “dua puluh satu ribu”, posisi satuan “puluh” yang ada di kiri memiliki tingkat lebih rendah daripada satuan “ribu” yang ada di kanan. Hal ini menandakan satuan “puluh” itu bagian dari satuan “ribu” yang ada di kanannya. Sehingga pada kalimat ini tidak dapat dipecah menjadi dua bagian.

Berikut ini contoh kode untuk mengevaluasi kalimat apakah dapat dipecah menjadi lebih dari satu sub kalimat atau tidak.

Hasil eksekusi beberapa kalimat berbeda adalah sebagai berikut.

Kalimat: "dua ratus dua puluh" ['dua ratus', 'dua puluh']
2 klaster

Kalimat: "dua puluh satu ribu" ['dua puluh satu ribu']
1 klaster

Kalimat: "lima ratus tiga puluh lima juta" ['lima ratus tiga puluh lima juta']
1 klaster

Kalimat: "seratus ribu lima ratus"
['satu ratus ribu', 'lima ratus']
2 klaster

Kalimat: "seratus ribu lima ratus dua puluh satu"
['satu ratus ribu', 'lima ratus', 'dua puluh', 'satu']
4 klaster

Kode sumber lengkap dari seluruh tutorial di atas adalah sebagai berikut.

Luaran dari potongan kode di atas adalah:

Kalimat asli: dua belas juta seratus lima puluh empat ribu lima belas

Kalimat 1: dua belas juta
Ekspresi: (+12)*1000000
Nilai evaluasi: 12000000

Kalimat 2: satu ratus lima puluh empat ribu
Ekspresi: (+1*100+5*10+4)*1000
Nilai evaluasi: 154000

Kalimat 3: lima belas
Ekspresi: +15
Nilai evaluasi: 15

Total nilai: 12154015
Total nilai (lokal): 12.154.015

Demikian tutorial untuk menulis lambang bilangan secara bertahap, khususnya bagi pemula. Semoga dapat dipahami dan memberikan manfaat.

Saran dan masukan tentunya sangat saya harapkan.

Terima kasih.


Bonus tampilan versi GUI (PyQt) untuk membaca dan menulis lambang bilangan.

Cek sumber kode lengkapnya di Github.

Demo program online ada di tautan berikut.

Galih Hermawan - Mini Projects