Menulis Fungsi

Membuat fungsi sendiri dengan function(), argumen, dan nilai balik.

Daftar isi

Topik ini membahas cara membuat fungsi sendiri di R dengan konstruktor function(), mulai dari anatomi fungsi, nilai balik, argumen default, sampai penampung argumen .... Dua fungsi contoh dibangun bertahap, yaitu nilai_huruf() yang mengonversi angka nilai menjadi huruf predikat dan rata_rata_aman() yang aman terhadap nilai NA. Setelah membacanya, perintah yang berulang dalam analisis dapat dirangkum menjadi satu fungsi bernama dan dipanggil ulang kapan pun diperlukan.

Anatomi fungsi#

Fungsi dibuat dengan menugaskan hasil function() ke sebuah nama, sehingga pola lengkapnya berupa nama_fungsi <- function(argumen) { tubuh }. Kurung setelah function menampung daftar argumen, sedangkan kurung kurawal memuat tubuh (body), yaitu deretan ekspresi yang dieksekusi setiap kali fungsi dipanggil. Nama fungsi mengikuti aturan penamaan objek R dan sebaiknya menggambarkan apa yang dilakukannya, misalnya gandakan untuk fungsi yang melipatgandakan nilai. Fungsi berikut menerima satu argumen dan menghasilkan dua kali nilainya:

r
gandakan <- function(nilai) {
  nilai * 2
}
gandakan(c(70, 80, 90))
text Keluaran
[1] 140 160 180

Fungsi buatan sendiri dipanggil persis seperti fungsi bawaan, yaitu dengan nama dan kurung yang mengisi argumennya. Tubuh fungsi dieksekusi pada setiap pemanggilan, dan argumen nilai hanya hidup di dalam tubuh fungsi tersebut. Menulis fungsi kecil untuk satu keperluan membuat kode lebih mudah diuji, karena perilakunya dapat diperiksa lewat beberapa pemanggilan singkat sebelum digunakan dalam analisis yang lebih besar. Nama fungsi sebaiknya ditulis dengan huruf kecil dan pemisah garis bawah, misalnya gandakan atau rata_rata_aman, agar seluruh nama fungsi dalam satu proyek menaati pola yang sama.

Nilai balik fungsi#

Nilai balik (return value) sebuah fungsi adalah nilai dari ekspresi terakhir yang dieksekusi pada tubuhnya. Pada gandakan() di atas, ekspresi nilai * 2 berada paling akhir, sehingga hasil perkaliannya langsung menjadi nilai balik fungsi. Nilai balik itupun dapat ditugaskan ke variabel atau disalurkan ke fungsi lain, persis seperti nilai balik fungsi bawaan. Pernyataan return() juga dapat digunakan, baik untuk menyatakan nilai balik secara eksplisit maupun untuk mengakhiri eksekusi lebih awal. Fungsi nilai_huruf() berikut menggabungkan kedua bentuk nilai balik itu, yaitu return() untuk menangani NA lebih dahulu dan rantai if else sebagai nilai baliknya:

r
nilai_huruf <- function(nilai) {
  if (is.na(nilai)) {
    return(NA_character_)
  }
  if (nilai >= 85) {
    "A"
  } else if (nilai >= 70) {
    "B"
  } else if (nilai >= 55) {
    "C"
  } else {
    "D"
  }
}
nilai_huruf(92)
nilai_huruf(78)
nilai_huruf(NA)
text Keluaran
[1] "A"
[1] "B"
[1] NA

Pemanggilan pertama menghasilkan “A” karena 92 melampaui batas 85, sedangkan pemanggilan kedua jatuh ke cabang “B”. Pemanggilan ketiga memperlihatkan kegunaan return() di awal tubuh: tanpa pemeriksaan is.na(), perbandingan nilai >= 85 bernilai NA dan pernyataan if melaporkan error karena syaratnya bukan TRUE. Dengan return(NA_character_), kasus NA selesai ditangani sebelum rantai perbandingan dijalankan.

Argumen default#

Argumen default adalah argumen yang memiliki nilai bawaan dan boleh tidak diisi saat pemanggilan. Nilai bawaannya dituliskan pada daftar argumen dengan tanda =, dan konvensinya menempatkan argumen data lebih dahulu diikuti opsi penyesuaian di belakang. Fungsi rata_rata_aman() berikut membungkus mean() dengan argumen na.rm yang bawaannya TRUE, sehingga nilai NA otomatis diabaikan tanpa perlu menyebut argumennya:

r
rata_rata_aman <- function(x, na.rm = TRUE) {
  mean(x, na.rm = na.rm)
}
nilai_ali <- c(85, 90, NA, 78, 88)
rata_rata_aman(nilai_ali)
rata_rata_aman(nilai_ali, na.rm = FALSE)
text Keluaran
[1] 85.25
[1] NA

Pemanggilan pertama memakai nilai bawaan, sedangkan pemanggilan kedua menyebut na.rm = FALSE sehingga satu nilai NA membuat hasilnya ikut NA. Pilihan bawaan sebaiknya merupakan perilaku yang paling sering diinginkan, karena setiap pemanggilan yang ingin berperilaku berbeda harus menuliskan argumennya secara lengkap.

Fungsi yang akan dipakai lama sebaiknya disertai komentar dokumentasi berformat roxygen, yaitu komentar berawalan #' yang diletakkan tepat di atas definisi fungsinya. Bagian @param menjelaskan setiap argumen dan bagian @return menjelaskan nilai baliknya. Kebiasaan ini tidak mengubah perilaku kode sama sekali, tetapi menyimpan informasi penting di tempat terdekat dengan kodenya, dan formatnya dapat diterjemahkan menjadi halaman dokumentasi resmi oleh alat pengemas seperti devtools::document().

Menampung argumen dengan …#

Fungsi yang membungkus fungsi lain sering perlu meneruskan opsi tambahan tanpa mendaftarkannya satu per satu. Titik tiga ... pada daftar argumen menampung semua argumen tambahan yang diberikan pemanggil, lalu meneruskannya apa adanya ke fungsi di dalam tubuh. Fungsi ringkas() berikut meneruskan isi ... ke mean() dan sd() sekaligus:

r
ringkas <- function(x, ...) {
  c(rata_rata = mean(x, ...), simpangan_baku = sd(x, ...))
}
ringkas(nilai_ali)
text Keluaran
     rata_rata simpangan_baku 
            NA             NA 

Kedua komponen hasilnya bernilai NA karena nilai_ali memuat satu NA dan ringkas() belum diberi argumen na.rm. Argumen tambahan cukup diberikan pada pemanggilan, dan ... meneruskannya ke kedua fungsi di dalam tubuh:

r
ringkas(nilai_ali, na.rm = TRUE)
text Keluaran
     rata_rata simpangan_baku 
     85.250000       5.251984 

Kali ini na.rm = TRUE tiba di mean() dan sd() melalui ... tanpa perubahan apa pun. Mekanisme ini menjaga pembungkus tetap ringkas, tetapi salah ketik pada nama argumen tambahan tidak terdeteksi oleh ringkas() karena apa pun yang diberikan langsung diteruskan ke dalam.

Pola yang sama dipakai banyak fungsi bawaan R. Argumen na.rm tidak tercantum pada daftar argumen mean() karena diteruskan lewat ... ke fungsi di balik layar, dan kebiasaan itu terbaca langsung dari args(mean) yang menampilkan function (x, ...). Karena penampung ini menerima apa saja, nama argumen tambahan sebaiknya tetap ditulis lengkap setiap kali pemanggilannya menyertakan opsi penting.

Kesalahan umum#

Kesalahan yang sering terjadi adalah menganggap fungsi tidak mengembalikan apa pun, padahal R selalu mengembalikan nilai ekspresi terakhirnya, apa pun bentuk ekspresi itu. Fungsi berikut diakhiri dengan pemanggilan cat(), sehingga nilai baliknya adalah NULL dari cat(), bukan vektor ringkasan yang sudah dihitung:

r
deskripsi_salah <- function(x) {
  hasil <- c(rata_rata = mean(x, na.rm = TRUE), simpangan_baku = sd(x, na.rm = TRUE))
  cat("Perhitungan selesai\n")
}
deskripsi_salah(nilai_ali)
text Keluaran
Perhitungan selesai

Teks “Perhitungan selesai” tampil karena cat() mencetaknya ke konsol, tetapi objek hasil tidak pernah keluar dari fungsi. Apabila hasil pemanggilan ditugaskan ke sebuah objek, isinya hanya NULL. Perbaikannya adalah menjadikan hasil sebagai ekspresi terakhir atau menyatakannya dengan return():

r
deskripsi <- function(x) {
  hasil <- c(rata_rata = mean(x, na.rm = TRUE), simpangan_baku = sd(x, na.rm = TRUE))
  return(hasil)
}
deskripsi(nilai_ali)
text Keluaran
     rata_rata simpangan_baku 
     85.250000       5.251984 

Aturan praktisnya, akhiri tubuh fungsi dengan ekspresi yang nilainya ingin dikembalikan. cat() dan print() tetap berguna untuk pesan sampingan, tetapi keduanya bukan pengganti nilai balik. Kesalahan ini sering lolos karena konsol tetap menampilkan sesuatu, yaitu teks sampingan dari cat(), sehingga keluarannya kelihatan benar. Pemeriksaan sederhana berupa penugasan hasil ke sebuah objek lalu pencetakan objeknya langsung mengungkap apakah fungsi mengembalikan nilai yang benar.

Ringkasan#

  • Fungsi dibuat dengan nama <- function(argumen) { tubuh } dan dipanggil seperti fungsi bawaan.
  • Nilai balik sebuah fungsi adalah nilai ekspresi terakhirnya, sedangkan return() menyatakannya secara eksplisit atau mengakhiri eksekusi lebih awal.
  • Argumen default mengisi pemanggilan yang tidak menyebutnya, dan ... meneruskan argumen tambahan ke fungsi di dalam tubuh.
  • Fungsi yang diakhiri cat() atau penugasan mengembalikan NULL atau nilai yang tidak tercetak, sehingga tubuh fungsi diakhiri dengan ekspresi hasil yang dimaksud.

Referensi#