summaryrefslogtreecommitdiff
path: root/_content/blog/normalization/index.adoc
diff options
context:
space:
mode:
authorShulhan <m.shulhan@gmail.com>2020-04-26 20:30:58 +0700
committerShulhan <m.shulhan@gmail.com>2020-04-26 20:31:05 +0700
commitae87a7a3babc5a7194d630a02f1e79a71d93a647 (patch)
treed598230e31050986c877785bb1be50aa69222a8d /_content/blog/normalization/index.adoc
parent4ff208d604f34d0579399dd289a9a60e19c9a2ab (diff)
downloadgolang-id-web-ae87a7a3babc5a7194d630a02f1e79a71d93a647.tar.xz
all: add underscore prefix to non Go directories
This is to prevent Go tools process any directories that start with underscore.
Diffstat (limited to '_content/blog/normalization/index.adoc')
-rw-r--r--_content/blog/normalization/index.adoc373
1 files changed, 373 insertions, 0 deletions
diff --git a/_content/blog/normalization/index.adoc b/_content/blog/normalization/index.adoc
new file mode 100644
index 0000000..a49566b
--- /dev/null
+++ b/_content/blog/normalization/index.adoc
@@ -0,0 +1,373 @@
+= Normalisasi teks dalam Go
+:author: Marcel van Lohuizen
+:date: 26 November 2013
+:toc:
+
+== Pendahuluan
+
+Pada
+link:/blog/strings[artikel]
+sebelumnya kita telah membahas tentang string, byte, dan karakter dalam Go.
+Saya telah bekerja dengan banyak paket-paket untuk pemrosesan teks
+multibahasa untuk repositori teks Go.
+Beberapa paket tersebut layak memiliki artikel sendiri yang terpisah, tetapi
+sekarang saya ingin fokus pada
+https://godoc.org/code.google.com/p/go.text/unicode/norm[go text/unicode/norm]
+yang menangani normalisasi, topik yang disinggung dalam
+link:/blog/strings[artikel tentang string]
+dan subjek dari artikel ini.
+Normalisasi bekerja pada tingkat paling atas dari abstraksi bukan pada byte
+mentah.
+
+Untuk belajar tentang normalisasi,
+http://unicode.org/reports/tr15/[Annex 15 dari Standar Unicode]
+adalah bacaan yang bagus.
+Artikel yang lebih awam yaitu
+http://en.wikipedia.org/wiki/Unicode_equivalence[halaman Wikipedia].
+Di sini kita fokus tentang bagaimana normalisasi bekerja dalam Go.
+
+
+== Apa itu normalisasi?
+
+Terkadang ada beberapa cara untuk merepresentasikan string yang sama.
+Misalnya, sebuah é (e-tirus) dapat direpresentasikan dalam sebuah string
+sebagai sebuah rune ("\u00e9") atau sebuah 'e' diikuti oleh aksen tirus
+("e\u0301").
+Menurut standar Unicode, kedua hal tersebut "kesetaraan kanonis" dan
+sebaiknya diperlakukan sama.
+
+Menggunakan pembandingan byte-per-byte untuk menentukan kesamaan sudah jelas
+tidak akan menghasilkan nilai yang benar untuk kedua string tersebut.
+Unicode menentukan sekumpulan bentuk-bentuk normal supaya bila dua string
+setara secara kanonis dan dinormalisasi ke bentuk normal yang sama, maka
+representasi byte mereka akan sama.
+
+Unicode juga mendefinisikan sebuah "kesetaraan kompatibilitas" untuk
+menyamakan karakter yang merepresentasikan karakter yang sama, tetapi bisa
+jadi memiliki tampilan visual yang berbeda.
+Misalnya, angka _superscript_ '⁹' dan angka '9' biasa disebut dengan
+kesetaraan kompatibilitas.
+
+Untuk setiap bentuk kesetaraan ini, Unicode menentukan bentuk komposisi dan
+dekomposisi.
+Bentuk komposisi mengganti beberapa rune yang dapat digabungkan menjadi sebuah
+rune tunggal.
+Bentuk dekomposisi memecah rune menjadi komponen tersendiri.
+Tabel berikut memperlihatkan nama-nama, semuanya dengan prefiks NF, yang
+ditentukan oleh konsorsium Unicode untuk mengidentifikasi bentuk-bentuk
+tersebut:
+
+----
++-------------------------+-----------+-------------+
+| | Komposisi | Dekomposisi |
++-------------------------+-----------+-------------+
+| Kesamaan kanonis | NFC | NFD |
++-------------------------+-----------+-------------+
+| Kesamaan kompatibilitas | NFKC | NFKD |
++-------------------------+-----------+-------------+
+----
+
+
+== Pendekatan Go terhadap normalisasi
+
+Seperti yang telah dijelaskan juga dalam artikel tentang string, Go tidak
+menjamin bahwa karakter-karakter dalam sebuah string telah dinormalisasi.
+Namun, paket go.text dapat mengompensasi hal tersebut.
+Misalnya, paket
+https://godoc.org/code.google.com/p/go.text/collate[collate],
+yang dapat mengurutkan string menurut bahasa tertentu, bekerja secara tepat
+dengan string yang tidak dinormalisasi.
+Paket-paket dalam go.text tidak selalu membutuhkan input yang telah
+dinormalisasi, tetapi pada umumnya normalisasi bisa diperlukan untuk
+mendapatkan hasil yang konsisten.
+
+Normalisasi ada biayanya namun ia cepat, terutama untuk pemeriksaan dan
+pencarian atau jika sebuah string bukanlah NFD atau NFC dan bisa dikonversi ke
+NFD dengan melakukan dekomposisi tanpa mengubah urutan byte-byte.
+Secara praktik,
+http://www.macchiato.com/unicode/nfc-faq#TOC-How-much-text-is-already-NFC-[99.98%]
+isi halaman HTML di web dalam bentuk NFC (bila mengikutkan _markup_,
+nilai persentase akan lebih besar).
+Sejauh ini umumnya NFC dapat di-dekomposisi ke NFD tanpa perlu mengubah urutan
+(yang mana membutuhkan alokasi).
+Dan juga, cukup efisien untuk memeriksa kapan pengurutan diperlukan, sehingga
+kita dapat mempercepat dengan hanya memproses segmen-segmen tertentu yang
+membutuhkan.
+
+Supaya lebih baik, paket `collate` biasanya tidak menggunakan paket `norm`
+secara langsung, tetapi menggunakan paket `norm` untuk menggabungkan informasi
+normalisasi pada tabel tersendiri.
+Penggabungan kedua masalah tersebut membolehkan pengurutan dan normalisasi
+berjalan bersamaan tanpa memengaruhi kinerja.
+Biaya dari normalisasi seperti ini dikompensasi dengan tidak harus
+menormalisasi teks sebelumnya dan memastikan bentuk normal dijaga selama
+penyuntingan.
+Masalah yang terakhir cukup pelik.
+Misalnya, hasil dari penggabungan dua string NFC yang dinormalisasi tidak
+dijamin menjadi NFC.
+
+Tentu saja, kita dapat menghindari beban ini sebelumnya jika kita mengetahui
+bahwa sebuah string telah dinormalisasi, yang mana pada kebanyakan memang
+telah dinormalisasi.
+
+
+== Kenapa peduli?
+
+Setelah semua diskusi tentang menghindari normalisasi, anda mungkin bertanya
+kenapa harus peduli dengan semua ini.
+Alasannya adalah bahwa ada beberapa kasus yang mana normalisasi dibutuhkan dan
+sangat penting untuk memahami apa saja kasus-kasus tersebut, dan bagaimana
+menangani secara benar.
+
+Sebelum mendiskusikan hal tersebut, kita harus menjelaskan konsep dari
+'karakter'.
+
+
+== Apa itu karakter?
+
+Seperti yang telah disebutkan juga dalam artikel tentang string, beberapa
+karakter dapat memakai beberapa rune.
+Contohnya, sebuah 'e' dan '◌́' (tirus "\u0301") dapat digabungkan membentuk
+'é' ("e\u0301" dalam NFD).
+Kedua rune tersebut adalah satu karakter.
+Definisi dari sebuah karakter bisa beragam bergantung pada aplikasi.
+Untuk normalisasi kita definisikan sebagai seurutan rune yang
+dimulai dengan sebuah _starter_, sebuah rune yang tidak mengubah atau
+tergabung dengan rune lainnya, diikuti oleh urutan yang bukan _starter_, yaitu
+rune-rune yang bisa mengubah atau bergabung dengan rune lainnya (biasanya
+aksen).
+Algoritme normalisasi memproses satu karakter dalam satu waktu.
+
+Secara teori, tidak ada batas dari jumlah rune yang dapat membentuk sebuah
+karakter Unicode.
+Faktanya, tidak ada batasan jumlah pengubah yang mengikuti sebuah
+karakter, dan sebuah pengubah bisa berulang, atau bertumpuk.
+Pernah lihat 'e' dengan tiga tirus?
+Ini dia: 'é́́'.
+Itu adalah karakter dengan 4-rune yang valid menurut standar.
+
+Akibatnya, bahkan pada tingkat dasar, teks perlu diproses secara berurutan
+pada ukuran potongan yang tak terbatas.
+Hal ini tampak aneh dengan pendekatan _streaming_ terhadap pemrosesan teks,
+seperti yang digunakan oleh interface standar Go Reader dan Writer, biasanya
+model tersebut biasanya berpotensi membutuhkan buffer sementara dengan ukuran
+yang tak terbatas juga.
+Implementasi langsung dari normalisasi akan membutuhkan waktu O(n²).
+
+Tidak ada interpretasi yang bermakna dari sejumlah urutan pengubah yang banyak
+tersebut pada penerapan praktis.
+Unicode menetapkan sebuah format Stream-Safe Text, yang membolehkan pemotongan
+jumlah pengubah (yang bukan _starter_) paling banyak 30, lebih dari cukup
+untuk kebutuhan pada umumnya.
+Jika lebih, sisa pengubah akan ditempatkan setelah _Combining Grapheme Joiner_
+(CGJ atau U+034F) yang baru disisipkan.
+Go mengadopsi pendekatan ini untuk semua algoritme normalisasi.
+Keputusan ini mengorbankan sedikit kesesuaian demi sedikit keamanan.
+
+
+== Menulis dalam bentuk normal
+
+Bahkan bila kita tidak perlu menormalisasi teks pada kode Go Anda, tetap saja
+hal ini perlu dilakukan saat berkomunikasi dengan dunia luar.
+Contohnya, normalisasi ke NFC memadatkan teks Anda, membuatnya lebih singkat
+saat dikirim.
+Untuk beberapa bahasa, seperti Korea, penghematan ini bisa sangat berpengaruh.
+Juga, beberapa API eksternal bisa jadi mengharapkan teks dalam bentuk normal
+tertentu.
+Atau Anda bisa mengeluarkan teks sebagai NFC seperti yang kebanyakan orang
+lakukan.
+
+Untuk menulis teks sebagai NFC, gunakan paket
+https://godoc.org/code.google.com/p/go.text/unicode/norm[unicode/norm]
+untuk membungkus `io.Writer`:
+
+----
+wc := norm.NFC.Writer(w)
+defer wc.Close()
+// write as before...
+----
+
+Jika Anda punya string yang berukuran kecil dan ingin konversi yang cepat,
+Anda bisa menggunakan bentuk sederhana berikut:
+
+----
+norm.NFC.Bytes(b)
+----
+
+Paket `norm` menyediakan beragam method lain untuk normalisasi teks.
+Pilih salah satu yang sesuai dengan kebutuhan Anda.
+
+
+== Menangkap karakter yang mirip
+
+Bisakah Anda membedakan antara 'K' ("\u004B") dan 'K' (tanda Kelvin "\u212A")
+atau 'Ω' ("\u03a9") dan 'Ω' (tanda Ohm "\u2126")?
+Sangat mudah mengabaikan perbedaan antara variasi dari karakter yang sama.
+Pada umumnya adalah ide yang bagus untuk tidak membolehkan variasi tersebut
+dalam pengidentifikasi atau apa pun yang dapat menipu pengguna karena karakter
+yang mirip tersebut bisa menimbulkan celah keamanan.
+
+Bentuk kompatibilitas normal, NFKC dan NFKD, akan memetakan bentuk-bentuk yang
+secara visual identik ke nilai tunggal.
+Perlu diingat bahwa ia tidak akan melakukan hal tersebut saat dua simbol yang
+mirip, tetapi dari alfabet karakter yang berbeda.
+Contohnya, Latin 'o', Greek 'ο', dan Cyrillic 'о' adalah karakter-karakter
+yang berbeda.
+
+
+== Perbaikan modifikasi teks
+
+Paket `norm` bisa membantu saat kita butuh mengubah teks.
+Bayangkan sebuah kasus yang mana Anda ingin mencari dan mengganti kata "cafe"
+dengan bentuk jamak "cafes".
+Sebuah potongan kode akan berbentuk seperti ini.
+
+----
+s := "We went to eat at multiple cafe"
+cafe := "cafe"
+if p := strings.Index(s, cafe); p != -1 {
+ p += len(cafe)
+ s = s[:p] + "s" + s[p:]
+}
+fmt.Println(s)
+----
+
+Ia akan mencetak "We went to eat at multiple cafes" seperti yang diharapkan.
+Sekarang anggaplah teks tersebut berisi pengejaan Prancis "café" dalam bentuk
+NFD:
+
+----
+s := "We went to eat at multiple cafe\u0301"
+----
+
+Menggunakan kode yang sama, penanda jamak "s" akan tetap disisipkan setelah
+"e", tetapi sebelum tirus, menghasilkan "We went to eat at multiple cafeś".
+Hasil ini tidak diharapkan.
+
+Masalahnya adalah kode tersebut tidak melihat batasan antara karakter
+multi-rune dan menyisipkan sebuah rune di tengah sebuah karakter.
+Dengan menggunakan paket `norm`, kita dapat menulis kode tersebut sebagai
+berikut:
+
+----
+s := "We went to eat at multiple cafe\u0301"
+cafe := "cafe"
+if p := strings.Index(s, cafe); p != -1 {
+ p += len(cafe)
+ if bp := norm.FirstBoundary(s[p:]); bp > 0 {
+ p += bp
+ }
+ s = s[:p] + "s" + s[p:]
+}
+fmt.Println(s)
+----
+
+Contoh ini memang dibuat-buat, tetapi pesannya cukup jelas.
+Ingatlah bahwa karakter dapat menggunakan beberapa rune.
+Umumnya masalah seperti ini dapat dihindari dengan menggunakan fungsionalitas
+yang menghargai batasan karakter (seperti paket `go.text/search`.)
+
+
+== Iterasi
+
+Perkakas lain yang disediakan oleh paket `norm` yang bisa membantu bekerja
+dengan batasan karakter adalah iterator,
+https://godoc.org/code.google.com/p/go.text/unicode/norm#Iter[norm.Iter].
+Ia mengiterasi karakter satu-per-satu dalam bentuk normal.
+
+
+== Transformasi
+
+Seperti yang telah disebutkan sebelumnya, kebanyakan teks dalam bentuk NFC,
+yang mana karakter dasar dan pengubah digabungkan menjadi sebuah rune bila
+memungkinkan.
+Untuk menganalisis karakter, akan lebih mudah menangani rune setelah
+di-dekomposisi menjadi komponen terkecil.
+Di sinilah bentuk NFD sangat membantu.
+Contohnya, potongan kode berikut membuat sebuah `transform.Transformer` yang
+men-dekomposisi teks menjadi bagian-bagian kecil, menghapus semua aksen, dan
+kemudian melakukan komposisi ulang teks menjadi NFC:
+
+----
+import (
+ "unicode"
+
+ "golang.org/x/text/transform"
+ "golang.org/x/text/unicode/norm"
+)
+
+isMn := func(r rune) bool {
+ return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
+}
+t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
+----
+
+Hasil dari `Transformer` dapat digunakan untuk menghapus aksen-aksen dari
+`io.Reader`:
+
+----
+r = transform.NewReader(r, t)
+// read as before ...
+----
+
+Hal ini akan mengonversi "cafés" dalam teks menjadi "cafes", tanpa
+melihat bentuk normal dari teks aslinya.
+
+
+== Info normalisasi
+
+Seperti yang telah disebutkan sebelumnya, beberapa paket melakukan
+pra-komputasi normalisasi ke dalam tabel-nya sendiri untuk mengurangi
+normalisasi saat _run-time_.
+Tipe `norm.Properties` menyediakan akses ke informasi per-rune yang dibutuhkan
+oleh paket tersebut, yang paling terkenal yaitu _Canonical Combining Class_
+dan dekomposisi informasi.
+Bacalah
+https://godoc.org/code.google.com/p/go.text/unicode/norm/#Properties[dokumentasi]
+tipe tersebut jika Anda ingin belajar lebih dalam.
+
+
+== Kinerja
+
+Untuk mengetahui kinerja dari normalisasi, kita bandingkan dengan kinerja dari
+`strings.ToLower`.
+Sampel dari baris pertama dalam bentuk huruf kecil dan NFC semua.
+Sampel yang kedua bukan dalam huruf kecil dan bukan dalam bentuk NFC, sehingga
+membutuhkan penulisan versi yang baru.
+
+----
+Input ToLower NFC Append NFC Transform NFC Iter
+nörmalization 199 ns 137 ns 133 ns 251 ns (621 ns)
+No\u0308rmalization 427 ns 836 ns 845 ns 573 ns (948 ns)
+----
+
+Kolom dari hasil menggunakan iterator memperlihatkan pengukuran dengan dan
+tanpa inisiasi dari iterator, yang berisi buffer yang tidak perlu di-inisiasi
+ulang saat digunakan kembali.
+
+Seperti yang kita lihat, mendeteksi apakah sebuah string telah dinormalisasi
+bisa cukup efisien.
+Kebanyakan biaya normalisasi pada baris kedua adalah untuk inisiasi buffer,
+biaya yang dibayar saat kita harus memproses string yang besar.
+Dan ternyata, buffer tersebut jarang digunakan, sehingga kita mungkin mengubah
+implementasi-nya suatu saat nanti untuk mempercepat kasus-kasus umum untuk
+string-string berukuran kecil.
+
+
+== Kesimpulan
+
+Jika Anda berurusan dengan teks di dalam Go, Anda tidak perlu menggunakan
+paket `unicode/norm` untuk menormalisasi teks Anda.
+Paket tersebut bisa berguna untuk memastikan bahwa string dinormalisasi
+sebelum dikirim atau untuk manipulasi teks tingkat lanjut.
+
+Artikel ini secara singkat menyinggung paket-paket go.text lainnya berikut
+dengan pemrosesan teks multibahasa dan mungkin saja menimbulkan banyak
+pertanyaan daripada jawaban.
+Diskusi tentang topik-topik ini, bagaimana pun juga, harus menunggu di lain
+waktu.
+
+
+== Artikel terkait
+
+* link:/blogs/strings[String, byte, rune, dan karakter dalam Go]