Ketahui lebih lanjut

Bab 8: Penghapusan Stepwise


Untuk sebahagian besar, pengaturcaraan adalah ilmu menyelesaikan masalah dengan komputer. Kerana masalah sering sukar, penyelesaian-dan program-program yang melaksanakan penyelesaian-boleh menjadi sukar juga. Untuk memudahkan anda mengembangkan penyelesaian itu, anda perlu mengamalkan metodologi dan disiplin yang dapat mengurangkan tahap kerumitan itu kepada skala yang boleh diurus.

Pada tahun-tahun awal pengaturcaraan, konsep pengkomputeran sebagai sains adalah lebih kurang percubaan dalam pemikiran yang ingin tahu. Tiada siapa yang mengetahui banyak tentang pengaturcaraan pada masa itu, dan sedikit pemikirannya sebagai disiplin kejuruteraan dalam erti kata konvensional. Walau bagaimanapun pengaturcaraan matang, disiplin seperti itu mula muncul. Yang menjadi asas kepada disiplin itu ialah pemahaman bahawa pengaturcaraan dilakukan dalam persekitaran sosial di mana programmer harus bekerjasama. Jika anda masuk ke dalam industri, anda pasti akan menjadi salah satu daripada banyak pengaturcara yang bekerja untuk membangunkan program yang besar. Program itu, lebih-lebih lagi, hampir pasti untuk hidup dan memerlukan penyelenggaraan melampaui penerapannya yang asalnya. Seseorang akan mahu program itu menyertakan beberapa ciri baru atau berfungsi dengan cara yang berbeza. Apabila berlaku, pasukan pengaturcara baru mesti masuk dan membuat perubahan yang diperlukan dalam program. Sekiranya program ditulis dalam gaya individu dengan sedikit atau tiada kesamaan, semua orang untuk bekerja bersama secara produktif adalah amat sukar.

Untuk memerangi masalah ini, pengaturcara mula membangun satu set metodologi pengaturcaraan yang dipanggil secara kolektif Kejuruteraan perisian . Menggunakan kemahiran kejuruteraan perisian yang baik bukan sahaja memudahkan para pengaturcara lain untuk membaca dan memahami program anda, tetapi juga memudahkan anda menulis program-program tersebut di tempat pertama. Salah satu kemajuan metodologi yang paling penting untuk keluar dari kejuruteraan perisian ialah strategi reka bentuk atas ke bawah atau penambahbaikan langkah demi langkah , yang terdiri daripada menyelesaikan masalah dengan memulakan dengan masalah secara keseluruhan. Anda memecahkan seluruh masalah ke dalam kepingan, dan kemudian menyelesaikan setiap bahagian, memecahkan mereka ke bawah jika perlu. Strategi teratas ini dilengkapkan dengan ujian berulang di mana anda memastikan bahawa potongan penyelesaian yang lebih kecil berfungsi sebelum bergerak.

Latihan dalam perbaikan langkah demi langkah

Untuk menggambarkan konsep penghalusan berturut-turut, mari kita ajar Karel untuk menyelesaikan masalah baru. Bayangkan Karel kini hidup dalam dunia yang kelihatan seperti ini:

Di setiap lajur, terdapat menara beeper s yang tinggi tidak diketahui, walaupun beberapa lajur (seperti 7, dan 9 dalam dunia sampel) mungkin kosong. Tugas Karel adalah untuk mengumpul semua beeper s di setiap menara ini, meletakkannya kembali di sudut paling timur baris 1, dan kemudian kembali ke kedudukan permulaannya. Oleh itu, apabila Karel menyelesaikan tugasnya dalam contoh di atas, semua 25 beeper saat ini di menara harus disusun di sudut lajur ke-9 dan baris 1, seperti berikut:

Yang penting, anda boleh mengandaikan bahawa Karel bermulabermuladengan sifar beeper s dalam begnya. Setiap beeper dijemput ditambah ke begnya. Apabila meletakkan beeper s di sudut, karel boleh menggunakannya beepersInBag() ujian.

Kunci untuk menyelesaikan masalah ini ialah mengurai program dengan cara yang betul, sementara masih dapat menguji ketika anda pergi. Tugas ini lebih kompleks daripada yang lain yang anda lihat, yang menjadikan subproblem yang sesuai lebih penting untuk mendapatkan penyelesaian yang berjaya.

Prinsip reka bentuk atas ke bawah

Idea utama dalam perbaikan langkah demi langkah ialah anda harus memulakan rancangan program anda dari atas, yang merujuk kepada tahap program yang paling tinggi secara konseptual dan paling abstrak. Di peringkat ini, masalah menara beeper jelas dibahagikan kepada tiga fasa bebas. Pertama, Karel perlu mengumpul semua beeper s. Kedua, Karel perlu menyimpannya di persimpangan terakhir. Ketiga, Karel harus kembali ke kedudukan rumahnya. Penguraian konsep ini mengisyaratkan bahawa kaedah run untuk program ini akan mempunyai struktur berikut:

   public void run() {
      kumpulkanSemuaConoS();
      dropSemuaConoS();
      pulangKeRumah();
   }

Di peringkat ini, masalahnya mudah difahami. Sudah tentu, ada beberapa butiran yang tersisa dalam bentuk kaedah yang belum anda tulis. Walau bagaimanapun, adalah penting untuk melihat setiap tahap penguraian dan meyakinkan diri sendiri bahawa, selagi anda percaya bahawa kaedah yang akan anda tulis akan menyelesaikan subproblem dengan betul, maka anda akan mempunyai penyelesaian kepada masalah secara keseluruhan .

Ujian menguji semasa anda pergi

Sekarang anda telah menentukan struktur untuk program ini secara keseluruhan, tiba masanya untuk move ke move pertama, yang terdiri daripada mengumpul semua beeper s. Tugas ini sendiri lebih rumit daripada masalah mudah dari bab-bab sebelumnya. Mengumpul semua beeper s bermakna anda perlu mengambil beeper s di setiap menara sehingga anda sampai ke sudut akhir. Hakikat bahawa anda perlu mengulangi operasi untuk setiap menara menunjukkan bahawa anda memerlukan gelung sementara di sini. Gelung sementara akan mengulangi proses mengumpulSatuMenara dan kemudian bergerak.

Awas: Ia berbahaya untuk cuba menulis keseluruhan program tanpa ujian ia seperti yang anda pergi. Jika anda membuat kesilapan, sukar untuk mencari kesilapan. Kita tahu bahawa kita akan mengulangi proses mengumpul satu menara. Marilah kita menulis dan ujian mengumpul menara tunggal sebelum kami meletakkan MengumpulSatuMenara proses dalam untuk gelung. Oleh itutemporariliykita boleh mulakan dengan takrif berikut kumpulkanSemuaConoS:

   private void kumpulkanSemuaConoS() {
      /* pelaksanaan sementara untuk tujuan ujian */
      mengumpulSatuMenara();
      move();
   }

Sebagai prinsip panduan, jika anda mempunyai gelung yang kompleks, mengujibadangelung sebelum anda menulis gelung keseluruhan.

Menempah mengumpul menara

Apabila mengumpulSatuMenara dipanggil, Karel sama ada berdiri di pangkal menara beeper s atau berdiri di sudut kosong. Dalam kes pertama, anda perlu mengumpul beeper s di menara. Di dalam yang terakhir, anda boleh move . Keadaan ini terdengar seperti permohonan untuk pernyataan jika, di mana anda akan menulis sesuatu seperti ini:

   if(beepersPresent()){
      mengumpulMenaraSebenar();
   }

Sebelum anda menambah pernyataan sedemikian kepada kod, anda harus memikirkan sama ada anda perlu membuat ujian ini. Selalunya, program boleh dibuat dengan lebih mudah dengan memerhati kes-kes yang pada mulanya kelihatan istimewa boleh dirawat dengan cara yang sama seperti situasi yang lebih umum. Dalam masalah semasa, apa yang berlaku jika anda memutuskan bahawa terdapat menara beeper s di setiap jalan tetapi beberapa menara itu sifar beeper s tinggi? Penggunaan wawasan ini memudahkan program kerana anda tidak lagi perlu menguji sama ada terdapat menara di jalan tertentu.

Kaedah mengumpulSatuMenara masih cukup rumit bahawa tahap penguraian tambahan adalah teratur. Untuk mengumpul semua beeper s di sebuah menara, Karel perlu melakukan langkah-langkah berikut:

  1. Belok kiri ke muka beeper s di menara.
  2. Kumpulkan semua beeper s di menara, berhenti apabila tidak lagi beeper s ditemui.
  3. Beralih ke muka kembali ke bahagian bawah dunia.
  4. Kembali ke dinding yang mewakili tanah.
  5. Belok kiri untuk bersiap sedia ke move ke sudut seterusnya.

Sekali lagi, garis panduan ini menyediakan model untuk kaedah mengumpulSatuMenara, yang kelihatan seperti ini:

   private void mengumpulSatuMenara(){
      turnLeft();
      mengumpulBarisConoS();
      turnAround();
      moveKeDinding();
      turnLeft();
   }

Kaedah prasyarat dan postkondisi

Perintah belokKiri pada permulaan dan akhir kaedah mengumpulSatuMenara adalah penting bagi ketepatan program ini. Apabila mengumpulSatuMenara dipanggil, Karel sentiasa berada di suatu tempat di baris pertama menghadap ke timur. Apabila ia menyelesaikan operasi, program secara keseluruhan akan berfungsi dengan betul hanya jika Karel sekali lagi menghadap ke timur pada sudut yang sama. Syarat-syarat yang mesti berlaku sebelum kaedah dipanggil dirujuk sebagai prasyarat ; syarat-syarat yang mesti diguna pakai setelah selesai kaedah dikenali sebagai postkonditions .

Apabila anda mentakrifkan satu kaedah, anda akan mendapat masalah yang lebih sedikit jika anda menulis dengan tepat apa yang pra dan postkondisi. Sebaik sahaja anda berbuat demikian, anda perlu memastikan bahawa kod yang anda tulis selalu meninggalkan postkondisi yang memuaskan, dengan mengandaikan bahawa prasyarat itu berpuas hati untuk dimulakan. Sebagai contoh, fikirkan tentang apa yang berlaku jika anda memanggil MengumpulSatuMenara ketika Karel berada di baris 1 menghadap ke timur. Perintah belokKiri yang pertama meninggalkan Karel menghadap utara, yang bermaksud bahawa Karel betul-betul selari dengan lajur beeper s yang mewakili menara. Kaedah MengumpulBarisConoS - yang masih belum ditulis tetapi tetap melakukan tugas yang anda faham secara konseptual - hanya move s tanpa berpaling. Oleh itu, pada akhir panggilan untuk mengumpulBarisConoS, Karel masih akan menghadap utara. Oleh itu, panggilan pusing meninggalkan Karel menghadap ke selatan. Seperti mengumpulBarisConoS, kaedah move KeDinding tidak melibatkan apa-apa giliran tetapi sebaliknya hanya move sehingga ia mencapai dinding sempadan. Kerana Karel menghadap ke selatan, dinding sempadan ini akan menjadi satu di bahagian bawah layar, tepat di bawah baris 1. Oleh itu arahan akhir belokKiri meninggalkan Karel pada baris 1 yang menghadap ke timur, yang memuaskan postkondisi.

Mengulangi proses itu

Anda run program anda dan ia berjaya membersihkan satu menara dan meninggalkan Karel dalam postkondisi yang dijanjikan. Wahoo! Anda baru sahaja memukul tonggak penting dalam menyelesaikan tugas keras ini! Sekarang kita perlu mengulangi proses membersihkan satu menara menggunakan gelung sementara.

Tetapi apa yang kelihatan seperti gelung ini? Pertama sekali, anda harus memikirkan ujian bersyarat. Anda mahu Karel berhenti apabila ia memukul dinding pada akhir baris. Oleh itu, anda mahu Karel tetap selagi ruang di depan adalah jelas. Oleh itu, anda tahu bahawa kaedah kumpulkanSemuaConoS akan menyertakan gelung sementara yang menggunakan ujian depanAdalahJelas. Di setiap kedudukan, anda mahu Karel mengumpul semua beeper s di menara bermula di sudut itu. Sekiranya anda memberikan nama tersebut, yang mungkin seperti mengumpulSatuMenara, anda boleh meneruskan dan menulis definisi untuk kaedah kumpulkanSemuaConoS walaupun anda belum lagi mengisi butir-butirnya.

Walau bagaimanapun, anda perlu berhati-hati. Kod untuk kumpulkanSemuaConoS tidak kelihatan seperti ini:

   private void kumpulkanSemuaConoS(){
      /* gelung kereta! */
      while(frontIsClear()) {
         mengumpulSatuMenara();
         move();
      }
   }

Pelaksanaan ini adalah buggy kerana alasan yang sama bahawa versi pertama PlaceConoTalian umum dari bab 6 gagal melakukan tugasnya. Terdapat ralat fencepost dalam versi kod ini, kerana Karel perlu menguji kehadiran menara beeper di jalan terakhir. Pelaksanaan yang betul adalah:

   private void kumpulkanSemuaConoS(){
      while(frontIsClear()) {
         mengumpulSatuMenara();
         move();
      }
      mengumpulSatuMenara();
   }

Ambil perhatian bahawa kaedah ini mempunyai struktur yang sama dengan program utama dari program PlaceConoTalian yang dibentangkan dalam bab 6. Satu-satunya perbezaan adalah bahawa program ini memanggilSatuMenara di mana yang lain disebut tempatkanBeeper. Kedua-dua program ini adalah setiap contoh strategi umum yang kelihatan seperti ini:

   private void kumpulkanSemuaConoS(){
      while(frontIsClear()) {
          melakukan beberapa operasi.
         move();
      }
       melakukan operasi yang sama untuk sudut akhir.
   }

Anda boleh menggunakan strategi ini setiap kali anda perlu melakukan operasi di setiap sudut seperti anda move sepanjang jalan yang berakhir di dinding. Sekiranya anda mengingati struktur umum strategi ini, anda boleh menggunakannya apabila anda menghadapi masalah yang memerlukan operasi sedemikian. Strategi yang boleh digunakan semula seperti ini sering muncul dalam pengaturcaraan dan dirujuk sebagai idiom pengaturcaraan atau corak . Semakin banyak pola yang anda ketahui, semakin mudah bagi anda untuk mencari satu yang sesuai dengan jenis masalah tertentu.

Selesai

Walaupun kerja keras telah dilakukan, masih terdapat beberapa hujung yang longgar yang perlu diselesaikan. Program utama memanggil dua kaedah-dropSemuaConoS dan pulangKeRumah- yang masih belum ditulis. Begitu juga mengumpulSatuMenara panggilan mengumpulBarisConoS dan move KeDinding. Mujurlah, semua empat kaedah ini cukup mudah untuk kod tanpa penguraian lanjut, terutamanya jika anda menggunakan move KeDinding dalam definisi pulangKeRumah. Inilah pelaksanaan lengkap:


Bab seterusnya