6.1. Mengembalikan Nilai (Return)
Fungsi built-in yang telah kita gunakan, macam abs
, pow
, int
, max
, dan range
, tentunya memberikan sebuah hasil. Memanggil masing-masing fungsi tersebut menghasilkan sebuah nilai, yang mana biasanya kita berikan ke variabel atau kita gunakan sebagai bagian dari sebuah ekspresi.
biggest = max(3, 7, 2, 5) x = abs(3 - 11) + 10
Sebelumnya, kita juga pernah menulis fungsi kita sendiri yang akan mengembalikan jumlah final dari perhitungan bunga berganda.
Pada bab ini, kita akan menulis banyak fungsi lagi yang mengembalikan nilai, yang mana kita akan menamai fungsi yang me-return/mengembalikan nilai tersebut dengan sebutan fungsi fruitful, agar namanya terdengar lebih baik. Contoh pertama fungsi fruitful adalah area
, yang mengembalikan luas/area dari sebuah lingkaran yang radiusnya telah ditentukan:
def area(radius): b = 3.14159 * radius**2 return b
Kita telah melihat pernyataan return
sebelumnya, tapi pada fungsi fruitful pernyataan return
memerlukan nilai return. Pernyataan ini berarti: nilai dulu ekspresi return, lalu return/kembalikan nilainya langsung sebagai hasil (buah) dari fungsi ini. Ekspresi yang disediakan bisa ditulis secara acak hingga rumit, jadi kita bisa menulis ulang fungsi ini seperti contoh berikut:
def area(radius): return 3.14159 * radius * radius
Tapi dilain sisi, variabel temporer seperti b
pada contoh diatas akan membuat proses debugging jauh lebih mudah.
Terkadang akan sangat berguna ketika kita memiliki pernyataan return
lebih dari satu, yaitu ada satu pada masing-masing cabang pengkondisian. Kita sudah pernah melihat fungsi built-in abs
, sekarang kita melihat bagaimana kita menulis punya kita sendiri:
def absolute_value(x): if x < 0: return -x else: return x
Cara lain untuk menulis fungsi diatas adalah dengan menghilangkan else
dan hanya mengikuti kondisi if
dengan pernyataan return
kedua.
def absolute_value(x): if x < 0: return -x return x
Coba renungkan mengenai versi ini dan yakinkan diri mu kalau keduanya bekerja sama persis. Pahami antara kedua versi ini.
Kode yang tampak setelah pernyataan return
, atau ditempat lainnya tidak akan pernah dicapai oleh flow of execution, dinamakan dengan dead code, atau unreachable code.
Pada fungsi fruitful, adalah ide yang sangat bagus untuk memastikan kalau setiap jalur yang dilalui program selalu mencapai pernyataan return
. Versi berikut dari absolute_value
gagal mengikuti ide ini:
def bad_absolute_value(x): if x < 0: return -x elif x > 0: return x
Versi ini tidak benar karena jika yang terjadi adalah x
sama dengan 0, maka tak ada satu pun dari kondisi yang ada bernilai benar, dan fungsinya akan berakhir tanpa mencapai pernyataan return
. Pada kasus ini, nilai return-nya adalah nilai spesial yang dinamakan dengan None:
>>> print(bad_absolute_value(0))
None
Semua fungsi Python mengembalikan None
ketika mereka tidak mengembalikan nilai lain.
Selain itu, juga dimungkinkan untuk menggunakan pernyataan return pada pertengahan perulangan for
, yang dalam kasus tertentu dapat digunakan untuk mengontrol penghentian segera fungsi yang berjalan. Mari asumsikan kita ingin sebuah fungsi yang mencari kata pada daftar kata-kata.
def find_first_2_letter_word(xs): for wd in xs: if len(wd) == 2: return wd return ""
>>> find_first_2_letter_word(["This", "is", "a", "dead", "parrot"])
'is'
>>> find_first_2_letter_word(["I", "like", "cheese"])
''
Coba pahami kode ini dan yakinkan dirimu kalau pada contoh kasus return
pertama, fungsinya me-return sementara masih memproses elemen kedua: yang artinya ia tidak mesti harus melintasi semua list untuk bisa me-return nilai yang sudah ia temukan.
6.2. Pengembangan Program
Pada titik ini, kamu harusnya sudah bisa membaca sebuah fungsi dan menjabarkan apa yang mereka lakukan. Juga, jika kamu sudah sering berlatih, kamu tentu sudah menulis beberapa fungsi-fungsi kecil. Seiring kamu menulis fungsi yang lebih besar, kamu mungkin mulai mengalami kesulitan-kesulitan, khususnya yang berhubungan dengan runtime dan semantic error.
Untuk mensiasati pengembangan program yang kerumitannya makin tinggi, kita akan memperkenalkan teknik yang bernama incremental development (pengembangan bertahap). Tujuan dari incremental development adalah untuk menghindari sesi debugging yang lama dengan hanya menambah dan menguji sejumlah kecil kode pada satu waktu.
Sebagai contoh, anggap kita ingin mencari jarak antara dua titik, dengan koordinat (x1, y1) dan (x2, y2). Dengan teori Pitagoras, jarak itu didapat dari:
Langkah pertama adalah mempertimbangkan seperti apa bentuk dari fungsi distance
pada Python yang akan kita buat. Dengan kata lain, apa inputnya (parameter) dan apa outputnya (nilai return)?
Pada kasus ini, dua titik itu adalah inputnya, yang mana kita bisa merepresentasikannya menggunakan empat parameter. Nilai returnnya adalah distance (jaraknya), yang mana merupakan sebuah nilai floating-point.
Sejauh ini kita sudah bisa menulis outline dari fungsi yang menangkap pemikiran pemikiran kita yaitu:
def distance(x1, y1, x2, y2): return 0.0
Tentu nampak jelas, fungsi dengan versi ini tidak menghitung jarak; ia selalu me-return nol. Tapi secara sintaks itu masih benar, dan akan bisa jalan, yang artinya bahwa kita bisa mengujinya sebelum kita membuatnya lebih rumit.
Untuk mencoba fungsinya, kita memanggilnya dengan nilai sampel:
>>> distance(1, 2, 4, 6)
0.0
Kita memilih nilai tersebut agar jarak horisontalnya sama dengan 3 dan jarak vertikalnya sama dengan 4; dengan cara itu, hasilnya adalah 5 (sisi miring/hipotenuse dari segitiga 3-4-5). Ketika mencoba sebuah fungsi, tentu akan sangat berguna dan perlu untuk mengetahui hasilnya yang benar.
Pada titik ini kita bisa memastikan bahwa fungsinya benar secara syntaks, dan kita bisa mulai menambahkan baris-baris kode. Setelah setiap tahapan perubahan, kita menguji fungsinya lagi. Jika sebuah error terjadi pada titik manapun, kita tahu dimana ia tentunya berada -- ya, ia tentu berada pada baris terakhir yang kita tambahkan.
Langkah logis pertama pada perhitungannya adalah untuk menemukan selisih dari x2-x1 dan y2-y1. Kita akan merujuk ke nilai-nilai itu menggunakan nama variabel temporer dx
dan dy
.
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 return 0.0
Jika kita memanggil fungsi dengan argumennya seperti terlihat diatas, ketika flow of execution sampai pada pernyataan return, dx
harusnya bernilai 3 dan dy
bernilai 4. Kita bisa memeriksa kalau itu memang benar melalui PyScripter dengan menaruh kursor pada pernyataan return
, dan menjalankan program agar menghentikan eksekusi ketika sampai pada kursor (menggunakan tombol F4). Lalu kita mengamati variabel dx
dan dy
dengan mengarahkan mouse pada mereka, untuk memastikan bahwa fungsinya mendapatkan parameter yang tepat dan melakukan perhitungan pertama dengan benar. Jika tidak, berarti hanya ada beberapa baris kode yang harus diperiksa lagi.
Selanjutnya kita menghitung jumlah kuadrat dari dx
dan dy
:
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dsquared = dx*dx + dy*dy return 0.0
Dan lagi, kita bisa menjalakan program pada tahap ini dan memeriksa nilai dari dssquared
(yang mana seharusnya bernilai 25).
Akhirnya, menggunakan eksponen fraksional 0.5
untuk mencari akar kuadrat, kita menghitung dan mengembalikan hasilnya:
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 result = dsquared**0.5 return result
Jika bisa berjalan dengan benar, maka selesai sudah. Jika tidak, kamu mungkin perlu memeriksa nilai dari result
sebelum pernyataan return.
Ketika kamu memulai, kamu bisa menambah hanya sebaris atau dua baris kode saja. Seiring meningkatnya pengalaman, kamu bisa merasakan dirimu menulis dan melakukan debugging bilahan konseptual yang lebih besar. Baik dengan cara, menguji kode selangkah demi selangkah dan memastikan kalau setiap langkah sesuai dengan harapan bisa menghemat banyak waktu mu untuk debugging. Seiring meningkatnya skil pemrograman mu, kamu akan merasakan dirimu bisa mengatur bilahan yang lebih besar dan besar lagi: ini sangat mirip dengan bagaimana kita belajar membaca mulai dari huruf, suku kata, kata-kata, ungkapan, kalimat, paragraf, dll., atau bagaimana kita belajar membilah musik -- dari not tunggal hingga chord, bar, frasa, dan seterusnya.
Aspek kunci dari prosesnya adalah:
- Mulai dengan kerangka program yang bisa jalan dulu dan buat perubahan kecil secara bertahap. Pada setiap titik, jika terjadi error, kamu akan tahu dengan pasti dimana ia berada.
- Gunakan variabel temporer untuk merujuk ke nilai saat ini jadi kamu bisa dengan mudah memeriksa dan menguji mereka.
- Setelah programnya bekerja, bersantai dulu tidak apa-apa, lalu duduk lagi, dan bermain-mainlah dengan pilihan-pilihan mu. (Ada penelitian menarik yang menghubungkan "bermain-main" dengan pemahaman yang lebih baik, pembelajaran yang lebih baik, lebih bisa menikmati, dan lebih punya pola pikir positif tentang apa yang bisa kamu capai -- jadi luangkan beberapa waktu untuk beremeh temeh!) Kamu juga bisa mengokohkan banyak pernyataan menjadi satu gabungan ekspresi yang lebih besar, atau mengganti nama variabel yang kamu gunakan, atau jika kamu bisa membuat fungsinya lebih pendek. Garis pedoman yang baik adalah bertujuan untuk membuat kodenya semudah mungkin untuk dibaca oleh orang lain.
Berikut adalah versi lain dari fungsi tersebut. Ia menggunakan fungsi akar pangkat dua yang berada di dalam modul math
(kita akan belajar tentang modul beberapa saat lagi). Yang mana yang lebih kamu suka? Yang manakah yang terlihat lebih "mirip" dengan rumus Pitagoras yang kita mulai tadi?
import math def distance(x1, y1, x2, y2): return math.sqrt( (x2-x1)**2 + (y2-y1)**2 )
>>> distance(1, 2, 4, 6)
5.0
6.3. Melakukan Debugging Dengan Fungsi print
Teknik lain untuk debugging yang juga powerful (sebuah alternatif dari single-stepping dan pemeriksaan variabel program), adalah menambahkan fungsi print
ekstra pada tempat yang benar-benar diperlukan pada kode mu. Kemudian, dengan mengamati output dari program, kamu bisa memeriksa apakah algoritmanya melakukan apa yang kamu harapkan. Dengan catatan, harap pahami dengan baik hal-hal berikut:
- Kamu harus punya solusi yang jelas mengenai masalah, dan harus tahu apa yang mestinya terjadi sebelum kamu melakukan debugging pada program. Lakukan pemecahan masalah pada selembar kertas (sebaiknya gunakan flowchart untuk mencatat langkah-langkah yang kamu ambil) sebelum kamu memfokuskan diri dengan menulis kode. Ingat kalau menulis program saja tidak akan memecahkan masalahnya -- itu sederhananya hanya mengotomatiskan langkah-langkah manual yang mesti kamu ambil. Jadi pertama pastikan kamu punya pulpen - dan - kertas yang berisi solusi panduan yang benar-benar bekerja dan bisa diandalkan. Kemudian barulah pemrograman hanya tentang membuat langkah-langkah manual tersebut terjadi secara otomatis.
- Jangan menulis fungsi-fungsi yang penuh bualan (chatterbox function). Sebuah fungsi pembual adalah fungsi fruitful yang: melakukan hal selain tugas utamanya, juga menanyakan pengguna berbagai input, atau mencetak output yang tidak berguna, dan ketika semuanya akan menjadi jauh lebih berguna jika dia diam dan melakukan tugasnya tanpa berisik.
Sebagai contoh, kita sudah melihat fungsi built-in seperti range
, max
dan abs
. Tak ada satupun dari fungsi tersebut menjadi block bangunan yang berguna untuk program lain jika mereka meminta pengguna untuk memasukan sebuah input, atau mencetak hasil mereka sementara mereka melakukan tugasnya. Bisa kalian bayangkan bagaimana ruwetnya program kita jika fungsi-fungsi itu melakukan hal itu.
Jadi tips yang baik adalah menghindari memanggil fungsi print
dan fungsi input
didalam fungsi fruitful, kecuali tujuan utama dari fungsi mu adalah untuk melakukan input dan output. Satu pengecualian pada aturan ini adalah kita bisa sementara waktu menghamburkan beberapa pemanggilan pada fungsi print
kedalam kode kita untuk membantu melakukan debug dan memahami apa yang terjadi ketika kode dijalankan, tapi setelah selesai dan programnya sudah berjalan dengan baik tentu semuanya harus dihilangkan.
6.4. Komposisi
Seperti apa yang kamu harapkan saat ini, kamu bisa memanggil satu fungsi di dalam fungsi lainnya. Kemampuan ini dinamakan dengan komposisi.
Sebagai salah satu contoh, kita akan menulis sebuah fungsi yang mengambil dua titik, pusat dari lingkaran dan satu lagi tepi lingkaran, dan menghitung luas dari lingkaran.
Asumsikan kalau titik pusat disimpan pada variabel xc
dan yc
, dan titik tepinya pada xp
dan yp
. Langkah pertama adalah menemukan radius dari lingkaran, yang mana merupakan jarak antara dua titik tersebut. Untungnya, kita telah menulis fungsi, distance
, yang bisa melakukan hal itu, jadi sekarang semua yang harus kita lakukan adalah cukup menggunakannya:
radius = distance(xc, yc, xp, yp)
Langkah kedua adalah mencari luas dari lingkaran dengan radius tersebut dan mengembalikannya. Sekali lagi kita akan menggunakan fungsi kita sebelumnya:
result = area(radius) return result
Membalutnya kedalam fungsi, hingga menjadi:
def area2(xc, yc, xp, yp): radius = distance(xc, yc, xp, yp) result = area(radius) return result
Kita menamakan fungsi ini area2
untuk membedakannya dari fungsi area
yang didefinisikan sebelumnya.
Variabel temporer radius
dan result
berguna untuk pengembangan, debugging, dan single-stepping pada kode untuk mengamati apa yang terjadi, tapi ketika programnya bekerja dengan baik, kita bisa membuatnya menjadi lebih ringkas dengan mengkomposisi pemanggilan fungsinya:
def area2(xc, yc, xp, yp): return area(distance(xc, yc, xp, yp))
6.5. Fungsi Boolean
Fungsi bisa mengembalikan nilai Boolean, yang mana sering berguna untuk menyembunyikan tes rumit di dalam fungsi. Sebagai contoh:
def is_divisible(x,y): """ Tes jika x bisa dibagi dengan y """ if x % y == 0: return True else: return False
Sangat umum untuk memberikan nama fungsi Boolean yang terdengar seperti pertanyaan yang jawabannya iya/tidak. is_divisible
mengembalikan baik True atau False untuk menandakan apakah x
bisa atau tidak bisa dibagi dengan y
.
Kita bisa membuat fungsinya lebih ringkas dengan memanfaatkan keuntungan dari fakta bahwa konfisi dari pernyataan if
sendiri merupakan ekspresi Boolean. Kita bisa me-returnnya langsung, tanpa perlu pernyataan if
lagi:
def is_divisable(x, y): return x % y == 0
Berikut output ketka fungsi baru ini dijalankan:
>>> is_divisible(6, 4)
False
>>> is_divisible(6, 3)
True
Fungsi boolean sering kali digunakan pada pernyataan kondisional:
if is_divisable(x, y): ... # Lakukan sesuatu ... else: ... # Lakukan sesuatu lainnya ...
Mungkin lebih menggiurkan kalau ditulis menjadi:
if is_divisible(x, y) == True:
tapi perbandingan ekstranya ( == True ) tidak perlu ditulis agar tidak panjang.
6.6. Style Dalam Pemrograman
Aspek kejelasan dan bisa dibaca sangat penting untuk programmer, karena pada praktiknya program lebih sering dibaca dan dimodifikasi lalu baru ditulis. Tapi, seperti aturan umumnya, kita terkadang melanggarnya. Maka dari itu, kebanyakan dari contoh kode pada buku ini akan konsisten dengan Python Enhancement Proposal 8 (PEP 8), yang merupakan sebuah panduan style yang dikembangkan oleh komunitas Ptyhon.
Kita akan berbicara lebih banyak lagi mengenai style seiring bertambah rumitnya program kita, tapi sebagai awal beberapa poin ini akan bisa membantu untuk memahami poinnya:
- gunakan 4 spasi (jangan tab) untuk indentasi
- batasi panjang satu baris menjadi 78 karakter
- ketika menamai indentifier, gunakan
CamelCase
untuk kelas (kita akan mempelajari tentang kelas secepatnya) danlower_case_with_underscore
untuk fungsi dan variabel. - tempatkan
import
pada bagian atas file - jaga definisi fungsi tetap bersama, jangan meletakan definisi fungsi secara acak
- gunakan docstring untuk mendokumentasikan fungsi
- gunakan dua baris kosong untuk memisahkan definisi fungsi satu sama lain
- jaga pernyataan tingkat atas, termasuk pemanggilan fungsi, bersama-sama pada bagian bawah dari program
6.7. Unit Testing
Pada umumnya merupakan sebuah praktik yang baik dalam pengembangan perangkat lunak untuk menyertakan unit testing (pengujian unit) terotomatisasi dari kode sumber. Unit testing menyediakan cara yang secara otomatis memastikan kalau setiap potongan kode, seperti fungsi, bisa berjalan dengan baik. Ini memungkinkan kita untuk merubah implementasi dari fungsi dilain waktu dan dengan cepat menguji apakah ia masih melakukan apa yang seharusnya dilakukan.
Beberapa tahun belakangan ini organisasi-organisasi memiliki pandangan bahwa aset berharga mereka adalah kode program dan dokumentasi. Organisasi tersebut sekarang akan menghabiskan porsi besar dari anggaran belanja perangkat lunak mereka pada kerajinan (dan pelestarian) pengujian aset mereka itu.
Unit testing juga memaksa programmer untuk berpikir mengenai kasus yang berbeda yang perlu ditangani oleh fungsi. Kamu juga hanya perlu menulis pengujiannya sekali pada script, ketimbang harus memasukan data pengujian yang sama lagi dan lagi seiring kamu mengembangkan kode mu.
Kode ekstra pada program mu yang tujuannya membuat debugging atau pengujian menjadi lebih mudah dinamakan sebagai scaffolding.
Sekumpulan pengujian untuk beberapa kode dinamakan sebagai test suite.
Terdapat berbagai cara untuk melakukan unit testing di Python -- tapi pada tahap ini kita akan mengabaikan apa yang komunitas Python biasa gunakan, dan kita akan mulai dengan dua fungsi yang akan kita tulis sendiri. Kita akan menggunakan mereka untuk menulit unit test kita.
Mari mulai dengan fungsi absolute_value
yang kita tulis sebelumnya pada bab ini. Ingat bahwa kita menulis beberapa versi berbeda, yang mana yang terakhir ditulis salah, dan punya bug. Bisakah pengujiannya menemukan bug ini?
Pertama kita merencanakan pengujian kita. Kita perlu mengetahui jika fungsinya me-return nilai yang benar ketika argumennya negatif, atau ketika argumennya positif, atau , ketika argumennya nol. Ketika kamu merencanakan pengujian, kamu akan selalu perlu untuk memikirkan dengan hati-hati mengenai kasus "tepi" -- disini, sebuah argumen dari 0 ke absolute_value
berada pada tepi dari perubahan prilaku fungsi, dan seperti yang kita lihat pada awal bab ini, merupakan titik yang paling mudah untuk programmer membuat sebuah kesalahan! Jadi merupakan kasus yang baik untuk menyertakannya pada test suite kita.
Kita akan menulis sebuah fungsi helper untuk mengecek hasil dari satu pengujian. Ia akan mengambil sebuah argumen boolean dan akan mencetak pesan apakah pengujiannya berhasil atau gagal. Baris pertama dari body (setelah docstring fungsi) secara ajaib menentukan nomer baris pada skrip dimana pemanggilan dilakukan. Ini memungkinkan kita untuk mencetak nomer baris dari pengujian, yang mana akan membantu ketika kita ingin mengidentifikasi pengujian yang berhasil dan gagal.
import sys def test(did_pass): """ Cetak hasil dari pengujian. """ linenum = sys._getframe(1).f_lineno #Dapatkan nomer baris dari pemanggil if did_pass: msg = "Pengujian pada baris {0} berhasil.".format(linenum) else: msg = ("Pengujian pada baris {0} GAGAL.".format(linenum)) print(msg)
Juga terdapat pembentukan string yang sedikit rumit menggunakan metode format
, kita akan abaikan ini sementara waktu, dan kita bahas dengan detail pada bab berikutnya. Tapi dengan ditulisnya fungsi ini, kita bisa melanjutkan untuk membangun test suite kita:
def test_suite(): """ Menjalankan suite tes untuk kode pada modul ini (file ini). """ test(absolute_value(17) == 17) test(absolute_value(-17) == 17) test(absolute_value(0) == 0) test(absolute_value(3.14) == 3.14) test(absolute_value(-3.14) == 3.14) test_suite() # Disini merupakan pemanggilan untuk menjalankan pengujiannya
Disini kamu akan melihat kalau kita telah membangun lima pengujian pada test suite kita. Kita bisa menjalankan ini pada versi pertama atau kedua (versi yang benar) dari absolute_value
, dan kita akan mendapatkan output seperti:
Pengujian pada baris 25 berhasil.
Pengujian pada baris 26 berhasil.
Pengujian pada baris 27 berhasil.
Pengujian pada baris 28 berhasil.
Pengujian pada baris 29 berhasil.
Tapi mari katakan kamu merubah fungsinya menjadi versi yang salah seperti ini:
def absolute_value(n): # Versi buggy """ Hitung nilai absolut dari n """ if n < 0: return 1 elif n > 0: return n
Bisakan kamu menemukan dua kesalahan pada kode tersebut? Test suite kita bisa menemukannya! Kita mendapatkan:
Pengujian pada baris 25 berhasil.
Pengujian pada baris 26 GAGAL.
Pengujian pada baris 27 GAGAL.
Pengujian pada baris 28 berhasil.
Pengujian pada baris 29 GAGAL.
Terdapat tiga contoh dari test yang gagal.
Terdapat pernyataan Python built-in bernama assert yang melakukan hal yang hampir sama seperti fungsi test kita (tapi kalau menggunakan assert maka programnya akan berhenti pada pengujian pertama yang gagal). Kamu mungkin ingin membaca tentangnya, dan menggunakannya ketimbang menggunakan fungsi pengujian yang kita buat tadi.
6.8. Glosarium
fungsi Boolean
Fungsi yang mengembalikan nilai Boolean. Nilai yang ada pada tipe bool
hanyalah True
dan False
.
chatterbox function (fungsi pembual)
Sebuah fungsi yang berinteraksi dengan pengguna (menggunakan input
atau print
) ketika mestinya tidak ia lakukan. Fungsi pendiam yang hanya mengkonversi argumen inputnya menjadi output merupakan satu yang paling berguna, kebalikan dari fungsi pembual.
composition (of function) - komposisi fungsi
Memanggil satu fungsi dari dalam body fungsi lainnya, atau menggunakan nilai return
dari satu fungsi sebagai sebuah argumen untuk pemanggilan fungsi lainnya.
dead code
Bagian dari program yang tidak mungkin bisa dieksekusi, sering kali karena terletak setelah pernyataan return.
fruitful function
Fungsi yang menghasilkan nilai return ketimbang None
.
incremental development (pengembangan bertahap)
Rencana pengembangan program yang bertujuan untuk menyederhanakan proses debugging dengan menambahkan dan menguji hanya sejumlah kecil dari kode pada satu waktu.
None
Nilai spesial pada Python. Salah satu penggunaannya di Python adalah ia dikembalikan (di-return) oleh fungsi yang tidak mengeksekusi pernyataan return dengan argumen return.
return value
Nilai yang diberikan sebagai hasil dari sebuah pemanggilan fungsi.
scaffolding
Kode yang digunakan selama pengembangan program untuk menuntun kita pada tahap pengembangan dan debugging. Kode unit test yang kita tambahkan pada bab ini adalah contoh dari scaffolding.
temporary variable (variabel temporer)
Variabel yang digunakan untuk menyimpan nilai saat ini pada perhitungan rumit untuk keperluan pengembangan dan debugging.
test suite
Sekumpulan pengujian-pengujian dari beberapa kode yang telah kamu tulis.
unit testing
Prosedur otomatis yang digunakan untuk memastikan bahwa unit individu dari kode bekerja sebagai mana mestinya. Memiliki test suite sangat berguna ketika seseorang memodifikasi atau mengembangkan kodenya: ia akan menyediakan jaring pengaman agar kita tidak mundur lagi karena adanya bug baru yang tak sengaja kita masukan pada kode yang sebelumnya sudah bekerja dengan baik. Istilah regression testing (pengujian kemunduran) sering kali digunakan untuk mengungkapkan ide ini kalau kita tidak ingin untuk melangkah mundur!
6.9. Latihan
Semua latihan berikut harus ditambahkan ke satu file. Pada file tersebut, kamu mesti juga menambah fungsi scaffolding test
dan test_suite
seperti terlihat diatas, lalu kemudian, seiring kamu mulai mengerjakan latihannya, tambahkan pengujian baru ke test suite mu. (copy paste kodenya ke Python editor.)
Setelah menyelesaikan setiap latihan, pastikan kalau semua pengujian berhasil.
1. Empat arah mata angin bisa disingkat menjadi string satu huruf dalam bahasa Inggris seperti "N", "E", "S", dan "W". Tulis fungsi turn_clockwise
yang mengambil satu dari arah mata angin tersebut sebagai parameternya, dan kembalikan arah mata angin selanjutnya kearah yang berlawanan. Berikut adalah beberapa pengujian yang mesti berhasil:
test(turn_clockwise("N") == "E") test(turn_clockwise("W") == "N")
Kamu mungkin bertannya "Bagaimana jika argumen fungsinya merupakan nilai selain keempat nilai diatas?" Untuk semua kasus lainnya, fungsinya semestinya me-return nilai None
:
test(turn_clockwise(42) == None) test(turn_clockwise("rubbish") == None)
2. Tulis sebuah fungsi day_name
yang mengkonversi sebuah bilangan integer 0 hingga 6 menjadi nama-nama hari. Asumsikan kalau 9 adalah "Sunday". Sekali lagi, return None jika argumen fungsinya tidak valid. Berikut merupakan beberapa pengujian yang harus berhasil dijalankan:
test(day_name(3) == "Wednesday") test(day_name(6) == "Saturday") test(day_name(42) == None)
3. Tulis kebalikan dari fungsi day_num
yang mana sekarang menerima nama, dan me-return nomer:
test(day_num("Friday") == 5) test(day_num("Sunday") == 0) test(day_num(day_name(3)) == 3) test(day_name(day_num("Thursday")) == "Thursday")
Sekali lagi, jika fungsi ini diberikan argumen yang tidak valid, ia harus me-return None
:
test(day_num("Halloween") == None)
4. Tulis fungsi yang membantu menjawab pertanyaan seperti '"Hari ini adalah Wednesday. Saya pergi berlibur 19 hari dari sekarang. Hari apakah itu?"' Jadi fungsinya harus mengambil nama hari dan argumen delta
-- jumlah dari hari untuk ditambahkan -- dan harus me-return nama hari yang dimaksud:
test(day_add("Monday", 4) == "Friday") test(day_add("Tuesday", 0) == "Tuesday") test(day_add("Tuesday", 14) == "Tuesday") test(day_add("Sunday", 100) == "Tuesday")
Petunjuk: gunakan dua fungsi pertama yang ditulis diatas untuk membantu mu menulis yang ini.
5. Bisakah fungsi day_add
mu bekerja dengan delta negatif? Sebagai contoh, -1 harusnya kemarin, atau -7 akan menjadi seminggu yang lalu:
test(day_add("Sunday", -1) == "Saturday") test(day_add("Sunday", -7) == "Sunday") test(day_add("Tuesday", -100) == "Sunday")
Jika fungsi mu sudah bekerja, jelaskan kenapa. Jika belum, buat biar bisa bekerja.
Petunjuk: Bermainlah dengan beberapa kasus menggunakan fungsi modulus % (yang dijelaskan pada awal bab sebelumnya). Khususnya, cari tahu apa yang terjadi pada x % 7
ketika x adalah negatif.
6. Tulis fungsi day_in_month
yang mengambil nama-nama bulan, dan mereturn tanggal dari hari. Abaikan tahun kabisat:
test(days_in_month("February") == 28) test(days_in_month("December") == 31)
Jika fungsinya diberikan argumen yang tidak valid, ia mesti mereturn None
.
7. Tulis fungsi to_secs yang mengkonversi jam, menit dan detik dari sejumlah bilangan dalam detik. Berikut adalah beberapa pengujian yang mesti berhasil dilewati:
test(to_secs(2, 30, 10) == 9010) test(to_secs(2, 0, 0) == 7200) test(to_secs(0, 2, 0) == 120) test(to_secs(0, 0, 42) == 42) test(to_secs(0, -10, 10) == -590)
8. Kembangkan to_sec
sehingga ia bisa mengatasi nilai riil sebagai input. Ia mesti selalu me-return sebuah bilangan integer dari detik (dibulatkan menjadi nol):
test(to_secs(2.5, 0, 10.71) == 9010) test(to_secs(2.433,0,0) == 8758)
9. Tulis tiga fungsi yang merupakan "kebalikan" dari to_secs
:
hours_in
mengembalikan semua bilangan integer dalam jam yang direpresentasikan oleh jumlah total dari detik.minutes_in
mengembalikan semua bilangan integer dalam menit tersisa dalam jumlah total dari detik, setelah jamnya dikeluarkan.seconds_in
mengembalikan detik tersisa direpresentasikan oleh jumlah total dari detik.
Kamu bisa mengasumsikan kalau jumlah total dari detik yang lewat pada fungsi tersebut merupakan sebuah integer. Berikut adalah beberapa kasus pengujiannya:
test(hours_in(9010) == 2) test(minutes_in(9010) == 30) test(seconds_in(9010) == 10)
Unit test sering memiliki kelebihan sekunder ini, yaitu adanya kejelasan spesifikasi. Jika kamu menulis test suite mu sendiri, pertimbangkan agar menjadi bagian dari proses pemecahan masalah seperti kamu menanyakan pertanyaan tentang apa yang kamu benar-benar harapkan terjadi, dan apakah kamu telah mempertimbangkan semua kasus yang mungkin terjadi.
Karena buku kita berjudul Bagaimana Berpikir Layaknya .... kamu mungkin menikmati membaca setidaknya satu referensi tentang berpikir, dan tentang ide menyenangkan seperti fluid intelligence, kunci bahan-bahan dalam pemecehan masalah. Lihat, sebagai contoh, http://psychology.about.com/od/cognitivepsychology/a/fluid-crystal.htm. Belajar Ilmu Komputer memerlukan campuran yang baik dari kecerdasan yang encer dan terkristal.
10. Yang mana dari pengujian berikut yang gagal? Coba jelaskan.
test(3 % 4 == 0) test(3 % 4 == 3) test(3 / 4 == 0) test(3 // 4 == 0) test(3+4 * 2 == 14) test(4-2+2 == 0) test(len("hello, world!") == 13)
11. Tulis fungsi compare
yang mengembalikan 1
jika a > b
, 0
jika a == b
, dan -1
jika a < b
test(compare(5, 4) == 1) test(compare(7, 7) == 0) test(compare(2, 3) == -1) test(compare(42, 1) == 1)
12. Tulis fungsi bernama hypotenuse
yang mengembalikan panjang dari hypotenuse
dari segitiga yang benar sesuai dengan panjang dari dua kakinya sebagai parameter:
test(hypotenuse(3, 4) == 5.0) test(hypotenuse(12, 5) == 13.0) test(hypotenuse(24, 7) == 25.0) test(hypotenuse(9, 12) == 15.0)
13. Tulis fungsi slope(x1, y1, x2, y2)
yang mengembalikan lereng dari garis melalui titik (x1, y1) dan (x2, y2). Pastikan implementasin dari slope
mu bisa melewat pengujian berikut:
test(slope(5, 3, 4, 2) == 1.0) test(slope(1, 2, 3, 2) == 0.0) test(slope(1, 2, 3, 3) == 0.5) test(slope(2, 4, 1, 2) == 2.0)
Kemudian lakukan pemanggilan ke slope dari fungsi baru bernama intercept(x1, y1, x2, y2)
yang mengembalikan pemotongan dari garis y melalui titik (x1, y1)
dan (x2, y2)
test(intercept(1, 6, 3, 12) == 3.0) test(intercept(6, 1, 1, 6) == 7.0) test(intercept(4, 6, 12, 8) == 5.0)
14. Tulis fungsi bernama is_even(n)
yang mengambil sebuah integer sebagai argumen dan mengembalikan True
jika argumennya merupakan bilangan genap dan False
jika ganjil.
Tambahkan pengujian mu sendiri untuk menguji test suitenya.
15. Sekarang tulis fungsi is_odd(n)
yang mengembalikan True
ketika n
adalah ganjil dan False
jika sebaliknya. Sertakan juga unit test untuk fungsi ini.
Terakhir, modifikasi agar menggunakan call ke is_even untuk menentukan jika argumennya adalah sebuah integer ganji, dan memastikan semua pengujiannya berhasil.
16. Tulis fungsi is_factor(f, n)
yang bisa melewati pengujian berikut:
test(is_factor(3, 12)) test(not is_factor(5, 12)) test(is_factor(7, 14)) test(not is_factor(7, 15)) test(is_factor(1, 15)) test(is_factor(15, 15)) test(not is_factor(25, 15))
Kegunaan penting dari unit test adalah bisa juga digunakan sebagai "spesifikasi" yang tidak ambigu dari apa yang diharapkan. Kasus pengujian tersebut menjawab pertanyaan Apakah kita memperlakukan 1 dan 15 sebagai faktor dari 15?
17. Tulis is_multiple
untuk melewati unit test berikut:
test(is_multiple(12, 3)) test(is_multiple(12, 4)) test(not is_multiple(12, 5)) test(is_multiple(12, 6)) test(not is_multiple(12, 7))
Bisakah kamu menemukan cara untuk menggunakan is_factor
dalam definisi dari is_multiple
yang kamu buat?
18. Tulis fungsi f2c(t)
yang didesain agar mengembalikan nilai integer dari derajat Celcius yang paling mendekati dengan temperatur dalam Fahrenheit. (petunjuk: kamu akan perlu mempergunakan fungsi built-in, round
. Coba cetak round.__doc_
di Python shell atau lihat menu help mengenai fungsi round
, dan coba bereksperimen hingga kamu nyaman dan paham bagaimana cara kerja dan menggunakannya.)
test(f2c(212) == 100) # Titik didih air test(f2c(32) == 0) # Titik beku air test(f2c(-40) == -40) # Wow, what an interesting case! test(f2c(36) == 2) test(f2c(37) == 3) test(f2c(38) == 3) test(f2c(39) == 4)
19. Sekarang lakukan kebalikannya: tulis fungsi c2f
yang mengkonversi Celcius ke Fahrenheit:
test(c2f(0) == 32) test(c2f(100) == 212) test(c2f(-40) == -40) test(c2f(12) == 54) test(c2f(18) == 64) test(c2f(-48) == -54)
Mari Gabung
Halo
, Ada yang ingin disampaikan? Jangan sungkan untuk gabung diskusi ini. Silahkan Login dulu atau Daftar baru.