Haruskah saya memperbaiki fungsi besar yang sebagian besar terdiri dari satu regex? [Tutup]

15

Saya baru saja menulis fungsi yang membentang sekitar 100 baris. Mendengar itu, Anda mungkin tergoda untuk memberi tahu saya tentang tanggung jawab tunggal dan mendesak saya untuk menolak. Ini naluri saya juga, tapi ini masalahnya: Fungsi melakukan satu hal. Ini melakukan manipulasi string yang kompleks, dan fungsi tubuh sebagian besar terdiri dari satu regex verbose, dipecah menjadi banyak baris yang didokumentasikan. Jika saya memecah regex menjadi beberapa fungsi, saya merasa seperti saya akan benar-benar kehilangan keterbacaan, karena saya secara efektif beralih bahasa, dan tidak akan dapat mengambil keuntungan dari beberapa fitur yang ditawarkan regex. Ini pertanyaan saya sekarang:

Ketika menyangkut manipulasi string dengan ekspresi reguler, apakah fungsi besar masih anti-pola? Sepertinya kelompok yang diberi nama melayani tujuan yang sangat mirip dengan fungsi. Omong-omong, saya memiliki tes untuk setiap aliran melalui regex.

DudeOnRock
sumber
3
Saya tidak berpikir ada yang salah dengan fungsi Anda, mengingat sebagian besar darinya adalah dokumentasi . Mungkin ada masalah rawatan dengan menggunakan ekspresi reguler yang besar di tempat pertama.
Joel Cornett
2
Apakah Anda yakin regex raksasa adalah solusi terbaik untuk masalah Anda? Sudahkah Anda mempertimbangkan alternatif yang lebih sederhana, seperti parser library atau mengganti format file khusus dengan yang standar (XML, JSON dll)?
lortabac
2
Apakah ada fungsi lain, menggunakan versi regex ini yang diubah / ditingkatkan / disederhanakan? Itu akan menjadi indikator penting bahwa refactoring harus dilakukan. Jika tidak, saya akan membiarkannya apa adanya. Membutuhkan manipulasi string yang rumit seperti itu adalah bendera kuning dalam dirinya sendiri (baik saya tidak tahu konteksnya, karenanya hanya kuning), dan refactoring fungsi turun bagi saya lebih seperti ritual untuk menebus dari rasa bersalah yang dirasakan orang tentang it;)
Konrad Morawski
8
Bagaimana regexp 100 baris hanya melakukan 1 hal?
Pieter B
@ lortabac: Input adalah teks yang dibuat pengguna (prosa.)
DudeOnRock

Jawaban:

36

Apa yang Anda temui adalah disonansi kognitif yang berasal dari mendengarkan orang-orang yang mendukung kepatuhan terhadap pedoman dengan kedok "praktik terbaik" atas pengambilan keputusan yang beralasan.

Anda jelas telah melakukan pekerjaan rumah Anda:

  • Tujuan fungsi dipahami.
  • Cara kerja implementasinya dipahami (yaitu, dapat dibaca).
  • Ada tes cakupan penuh dari implementasi.
  • Tes-tes itu berlalu, artinya Anda yakin implementasi itu benar.

Jika salah satu dari poin itu tidak benar, saya akan menjadi yang pertama mengatakan bahwa fungsi Anda perlu bekerja. Jadi ada satu suara untuk meninggalkan kode apa adanya.

Suara kedua berasal dari melihat opsi Anda dan apa yang Anda dapatkan (dan kehilangan) dari masing-masing:

  • Refactor. Ini membuat Anda patuh pada gagasan seseorang tentang berapa lama suatu fungsi seharusnya dan mengorbankan keterbacaan.
  • Tidak melakukan apapun. Ini mempertahankan keterbacaan yang ada dan kepatuhan pengorbanan dengan ide seseorang tentang berapa lama fungsi seharusnya.

Keputusan ini jatuh di mana Anda lebih menghargai: keterbacaan atau panjang. Saya jatuh ke dalam kemah yang percaya bahwa panjang itu bagus tapi mudah dibaca itu penting dan akan mengambil yang terakhir dari yang pertama setiap minggu.

Intinya: jika tidak rusak, jangan memperbaikinya.

Blrfl
sumber
10
+1 untuk "Jika tidak rusak, jangan perbaiki."
Giorgio
Memang. Aturan Sandy Metz ( gist.github.com/henrik/4509394 ) bagus dan semuanya, tetapi di youtube.com/watch?v=VO-NvnZfMA4#t=1379 dia berbicara tentang bagaimana mereka muncul, dan mengapa orang mengambil mereka terlalu serius.
Amadan
@ Amdan: Dengan konteks ekstra dari video, apa yang dilakukan Metz masuk akal. Rekomendasinya kepada satu pelanggan itu sengaja ekstrem di satu sisi untuk melawan perilaku yang ekstrem di ujung lainnya sebagai cara untuk menyeretnya ke tengah yang lebih masuk akal. Sisa dari diskusi itu bermuara pada dorongan jawaban saya: penalaran, bukan iman, adalah cara untuk menentukan tindakan terbaik.
Blrfl
19

Jujur, fungsi Anda mungkin "melakukan satu hal", tetapi seperti yang Anda nyatakan sendiri

Saya bisa mulai memecah regex menjadi beberapa fungsi,

yang berarti kode reg Anda melakukan banyak hal. Dan saya kira itu bisa dipecah menjadi lebih kecil, unit yang dapat diuji secara individual. Namun, jika ini adalah ide yang baik tidak mudah dijawab, (terutama tanpa melihat kode yang sebenarnya). Dan jawaban yang benar mungkin bukan "ya" atau "tidak", tapi "belum, tapi lain kali Anda harus mengubah sesuatu di reg exp itu".

tapi rasanya saya akan benar-benar kehilangan keterbacaan seperti itu, karena saya secara efektif beralih bahasa

Dan ini adalah intinya - Anda memiliki kode yang ditulis dalam bahasa reg ex . Bahasa ini tidak menyediakan sarana abstraksi yang baik (dan saya tidak menganggap "kelompok penangkap" sebagai pengganti fungsi). Jadi refactoring "dalam bahasa reg ex" tidak benar-benar mungkin, dan menjalin exp yang lebih kecil dengan bahasa host mungkin tidak benar-benar meningkatkan keterbacaan (setidaknya, Anda merasa begitu, tetapi Anda memiliki keraguan, jika tidak, Anda tidak akan memposting pertanyaan) . Jadi inilah saran saya

  • tunjukkan kode Anda ke pengembang lanjutan lain (mungkin di /codereview// ) untuk memastikan orang lain berpikir tentang keterbacaan seperti yang Anda lakukan. Bersikaplah terbuka terhadap gagasan bahwa orang lain mungkin tidak menemukan reg line 100 exp yang dapat dibaca seperti Anda. Kadang-kadang gagasan "tidak mudah pecah menjadi potongan-potongan kecil" dapat diatasi hanya dengan sepasang mata yang kedua.

  • mengamati evolvabilitas aktual - apakah reg Anda yang mengkilap masih terlihat begitu baik ketika persyaratan baru tiba dan Anda harus menerapkan dan mengujinya? Selama reg exp Anda berfungsi, saya tidak akan menyentuhnya, tetapi setiap kali sesuatu harus diubah, saya akan mempertimbangkan kembali jika itu benar-benar ide yang baik untuk menempatkan setiap orang ke dalam satu blok besar ini - dan (serius!) Memikirkan kembali jika membelah menjadi potongan yang lebih kecil tidak akan menjadi pilihan yang lebih baik.

  • amati rawatan - dapatkah Anda secara efektif men-debug reg exp dalam bentuk saat ini dengan sangat baik? Terutama setelah Anda harus mengubah sesuatu, dan sekarang tes Anda memberi tahu Anda ada sesuatu yang salah, apakah Anda memiliki debugger reg exp yang membantu Anda menemukan akar masalahnya? Jika debugging menjadi sulit, itu juga akan menjadi kesempatan untuk mempertimbangkan kembali desain Anda.

Doc Brown
sumber
Saya akan mengatakan kelompok tangkapan bernama (kelompok tangkapan secara umum, benar-benar) paling mirip dengan variabel final / tulis-sekali, atau mungkin makro. Mereka memungkinkan Anda untuk referensi bagian tertentu dari pertandingan, baik dari objek pertandingan yang dikembalikan dari prosesor regex atau nanti dalam ekspresi reguler itu sendiri.
JAB
4

Terkadang fungsi yang lebih panjang yang melakukan satu hal adalah cara yang paling tepat untuk menangani suatu unit kerja. Anda dapat dengan mudah masuk ke fungsi yang sangat panjang ketika Anda mulai berurusan dengan query database (menggunakan bahasa query favorit Anda). Untuk membuat suatu fungsi (atau metode) lebih mudah dibaca sementara membatasinya untuk tujuan yang dinyatakannya adalah apa yang saya anggap sebagai hasil yang paling diinginkan dari suatu fungsi.

Panjang adalah "standar" sewenang-wenang dalam hal ukuran kode. Di mana fungsi 100 baris dalam C # dapat dianggap gondrong, itu akan menjadi kecil di beberapa versi perakitan. Saya telah melihat beberapa query SQL yang masuk ke dalam 200 baris rentang kode yang mengembalikan satu set data yang sangat rumit untuk sebuah laporan.

Kode sepenuhnya bekerja , yang adalah yang sederhana seperti Anda dapat cukup membuatnya adalah tujuan.

Jangan mengubahnya hanya karena itu panjang.

Adam Zuckerman
sumber
3

Anda selalu dapat memecah regex menjadi sub-regex, dan secara bertahap menyusun ekspresi akhir. Ini bisa membantu pemahaman untuk pola yang sangat besar, terutama jika sub-pola yang sama diulang berkali-kali. Misalnya dalam Perl;

my $start_re = qr/(?:\w+\.\w+)/;
my $middle_re = qr/(?:DOG)|(?:CAT)/;
my $end_re = qr/ => \d+/;

my $final_re = $start_re . $middle_re . $end_re;
# or: 
# my $final_re = qr/${start_re}${middle_re}${end_re}/
Rory Hunter
sumber
Saya menggunakan bendera verbose, yang bahkan lebih nyaman daripada apa yang Anda sarankan.
DudeOnRock
1

Saya akan mengatakan istirahat jika itu bisa pecah. dari sudut pandang rawatan dan mungkin daya masuk akal untuk melanggarnya, tetapi tentu saja Anda harus mempertimbangkan secara alami fungsi Anda dan bagaimana Anda mendapatkan input dan apa yang akan dikembalikan.

Saya ingat saya sedang mengerjakan parsing streaming chunked data menjadi objek, jadi apa yang saya lakukan pada dasarnya adalah saya membaginya menjadi dua bagian utama, satu sedang membangun unit String lengkap dari teks yang disandikan dan pada bagian kedua mengurai unit-unit tersebut ke dalam kamus data dan mengatur mereka (bisa berupa properti acak untuk objek yang berbeda) dan daripada memperbarui atau membuat objek.

Saya juga dapat memecah setiap bagian utama menjadi beberapa fungsi yang lebih kecil dan lebih spesifik sehingga pada akhirnya saya memiliki 5 fungsi berbeda untuk melakukan semuanya dan saya dapat menggunakan kembali beberapa fungsi di tempat yang berbeda.

arfo
sumber
1

Satu hal yang Anda mungkin atau mungkin tidak mempertimbangkan adalah untuk menulis parser kecil dalam bahasa yang Anda gunakan alih-alih menggunakan regex dalam bahasa itu. Ini mungkin lebih mudah dibaca, diuji, dan dirawat.

Thomas Eding
sumber
Saya sudah memikirkan hal ini sendiri. Masalahnya adalah inputnya prosa dan saya mengambil isyarat dari konteks dan format. Jika mungkin untuk menulis parser untuk sesuatu seperti ini, saya ingin belajar lebih banyak tentang itu! Saya sendiri tidak dapat menemukan apa pun.
DudeOnRock
1
Jika sebuah regex dapat menguraikannya, Anda dapat menguraikannya. Tanggapan Anda membuatnya tampak bagi saya bahwa Anda mungkin tidak berpengalaman dalam parsing. Jika itu masalahnya, Anda mungkin ingin tetap menggunakan regex. Entah itu atau pelajari keterampilan baru.
Thomas Eding
Saya ingin belajar keterampilan baru. Adakah sumber daya bagus yang bisa Anda sarankan? Saya tertarik pada teori di baliknya juga.
DudeOnRock
1

Regex raksasa adalah pilihan yang buruk dalam banyak kasus. Dalam pengalaman saya, mereka sering digunakan karena pengembang tidak terbiasa dengan parsing (lihat jawaban Thomas Eding ).

Bagaimanapun, mari kita asumsikan Anda ingin tetap berpegang pada solusi berbasis regex.

Karena saya tidak tahu kode sebenarnya, saya akan memeriksa dua skenario yang mungkin:

  • Regex sederhana (banyak pencocokan literal dan beberapa alternatif)

    Dalam hal ini fitur-fitur canggih yang ditawarkan oleh satu regex tidak diperlukan. Ini berarti Anda kemungkinan akan mendapat manfaat dari membaginya.

  • Regex itu kompleks (banyak alternatif)

    Dalam hal ini Anda tidak dapat secara realistis memiliki cakupan pengujian penuh, karena Anda mungkin memiliki jutaan kemungkinan aliran. Jadi, untuk mengujinya, Anda harus membaginya.

Saya mungkin kurang imajinasi, tetapi saya tidak bisa memikirkan situasi dunia nyata di mana regex 100-line adalah solusi yang baik.

lortabac
sumber