Fungsi dan subrutin

Fungsi dan subrutin sebagai prosedur Fortran beserta semantik pass-by-reference dan antarmuka eksplisit.

Daftar isi

Fortran menata perhitungan yang berulang ke dalam prosedur (procedure), yaitu unit kode bernama yang dapat dipanggil berkali-kali dari bagian mana pun di dalam program. Bahasa ini menyediakan dua bentuk prosedur, yaitu fungsi (function) yang mengembalikan satu nilai melalui namanya dan subrutin (subroutine) yang menyerahkan hasil melalui argumennya. Topik ini membahas cara mendefinisikan keduanya, kapan masing-masing bentuk dipilih, semantik pass-by-reference (pass by reference) pada argumen, serta prosedur internal yang ditempatkan setelah pernyataan contains. Program uji di sepanjang topik dijalankan dengan gfortran, dan keluarannya ditampilkan apa adanya.

Fungsi dan hasil yang dikembalikan#

Fungsi (function) adalah prosedur yang mengembalikan tepat satu nilai dan dipanggil di dalam ekspresi, sebagaimana fungsi matematika pada umumnya. Definisinya dimulai dengan tipe hasil, kata function, nama fungsi, dan daftar argumen di dalam tanda kurung. Hasil perhitungan diberikan ke variabel yang namanya sama dengan nama fungsi sebelum eksekusi mencapai pernyataan end function. Karena nilainya dikembalikan, pemanggil dapat menempatkan fungsi langsung pada sisi kanan penugasan, di dalam ekspresi aritmetika, maupun sebagai argumen prosedur lain.

Fungsi juga dapat ditulis dengan klausa result(nama) sehingga variabel hasil memiliki nama tersendiri, terpisah dari nama fungsinya. Bentuk ini berguna ketika nama fungsi panjang, karena penugasan hasil dapat dilakukan pada variabel yang lebih ringkas tanpa mengubah nama yang dipakai pemanggil. Kedua bentuk berikut menghitung konversi suhu yang sama dari derajat Celsius ke derajat Fahrenheit.

fortran Dua bentuk penulisan fungsi konversi suhu
! tidak-dijalankan
! fungsi dengan tipe hasil di depan nama
real function ke_fahrenheit(celsius)
  implicit none
  real :: celsius
  ke_fahrenheit = celsius * 9.0 / 5.0 + 32.0
end function ke_fahrenheit

! fungsi yang sama dengan klausa result
real function suhu_f(celsius) result(fahrenheit)
  implicit none
  real :: celsius
  fahrenheit = celsius * 9.0 / 5.0 + 32.0
end function suhu_f

Pernyataan implicit none diulang di dalam tubuh setiap fungsi karena aturan penentuan tipe variabel secara implisit berlaku per lingkup (scope) Fortran, bukan diwarisi dari unit yang memuatnya. Tanpa pengulangan itu, salah ketik nama variabel dapat lolos sebagai variabel baru yang dikenali secara implisit. Kedua definisi di atas adalah prosedur eksternal (external), yaitu unit yang berdiri sendiri di luar program utama, sehingga keduanya tidak dapat dijalankan tanpa adanya program utama yang memanggilnya.

Subrutin dan pemanggilan dengan call#

Subrutin (subroutine) adalah prosedur yang tidak mengembalikan nilai melalui namanya, melainkan menyerahkan hasil melalui argumen dan dipanggil dengan pernyataan call. Bentuk ini cocok untuk perhitungan yang menghasilkan lebih dari satu nilai, misalnya statistik ringkas dari sekumpulan nilai ujian. Program berikut memuat satu fungsi konversi suhu dan satu subrutin statistik. Keduanya ditempatkan setelah pernyataan contains, yaitu bentuk prosedur internal yang dibahas lebih rinci pada bagian Prosedur internal dengan contains.

fortran statistik.f90
program demo_prosedur
  implicit none
  real, parameter :: nilai(6) = [72.5, 88.0, 91.5, 65.0, 79.0, 84.5]
  real :: rata_rata, terendah, tertinggi

  ! fungsi dipanggil langsung di dalam ekspresi cetak
  print '(a, f8.2)', 'Suhu 25.0 C = ', ke_fahrenheit(25.0)
  print '(a, f8.2)', 'Suhu 37.5 C = ', ke_fahrenheit(37.5)

  ! subrutin dipanggil dengan pernyataan call
  call hitung_statistik(nilai, rata_rata, terendah, tertinggi)
  print '(a, f8.2)', 'Rata-rata kelas = ', rata_rata
  print '(a, f8.2)', 'Nilai terendah  = ', terendah
  print '(a, f8.2)', 'Nilai tertinggi = ', tertinggi

contains

  ! fungsi internal: menerima suhu Celsius dan mengembalikan Fahrenheit
  real function ke_fahrenheit(celsius)
    implicit none
    real :: celsius
    ke_fahrenheit = celsius * 9.0 / 5.0 + 32.0
  end function ke_fahrenheit

  ! subrutin internal: mengisi tiga argumen hasil sekaligus
  subroutine hitung_statistik(daftar, rata2, terendah, tertinggi)
    implicit none
    real :: daftar(:)
    real :: rata2, terendah, tertinggi
    integer :: i

    rata2 = sum(daftar) / size(daftar)
    terendah = daftar(1)
    tertinggi = daftar(1)
    do i = 2, size(daftar)
      if (daftar(i) < terendah) terendah = daftar(i)
      if (daftar(i) > tertinggi) tertinggi = daftar(i)
    end do
  end subroutine hitung_statistik

end program demo_prosedur

Argumen yang dideklarasikan pada kepala prosedur disebut argumen dummy (dummy argument), sedangkan nilai yang dikirim pemanggil disebut argumen aktual (actual argument). Pada subrutin hitung_statistik, argumen dummy daftar(:) berbentuk diasumsikan (assumed shape), yaitu bentuk yang ukurannya menyesuaikan array aktual saat runtime, yakni saat program berjalan. Array (array) adalah kumpulan nilai berindeks dan menjadi struktur data utama Fortran, sehingga prosedur yang menerima array merupakan hal yang lazim pada kode numerik. Bentuk diasumsikan semacam ini menuntut antarmuka eksplisit (explicit interface), yaitu keadaan ketika compiler, program yang menerjemahkan kode sumber menjadi program mesin, mengenali seluruh ciri prosedur pada titik pemanggilannya. Syarat itu otomatis terpenuhi pada contoh ini karena prosedurnya internal, sehingga tidak ada deklarasi antarmuka tambahan yang perlu ditulis.

bash
# mengompilasi program statistik
gfortran statistik.f90 -o statistik
# menjalankan hasil kompilasi
./statistik
text Keluaran
Suhu 25.0 C =    77.00
Suhu 37.5 C =    99.50
Rata-rata kelas =    80.08
Nilai terendah  =    65.00
Nilai tertinggi =    91.50

Semantik pass-by-reference#

Secara bawaan, Fortran meneruskan argumen dengan semantik pass-by-reference (pass by reference), yaitu argumen dummy menunjuk ke lokasi memori yang sama dengan argumen aktualnya. Akibatnya, penugasan pada argumen dummy langsung mengubah variabel asli di pemanggil tanpa pernyataan pengembalian apa pun. Program berikut menaikkan satu variabel suhu melalui subrutin, lalu mencetak nilainya sebelum dan sesudah pemanggilan.

fortran rujukan.f90
program rujukan
  implicit none
  real :: suhu

  suhu = 20.0
  print '(a, f6.1)', 'Sebelum dipanggil: ', suhu
  call naikkan(suhu, 5.5)
  print '(a, f6.1)', 'Sesudah dipanggil: ', suhu

contains

  ! argumen dummy derajat menunjuk ke argumen aktual suhu
  subroutine naikkan(derajat, tambahan)
    implicit none
    real :: derajat, tambahan
    derajat = derajat + tambahan
  end subroutine naikkan

end program rujukan
bash
# mengompilasi program rujukan
gfortran rujukan.f90 -o rujukan
# menjalankan hasil kompilasi
./rujukan
text Keluaran
Sebelum dipanggil:   20.0
Sesudah dipanggil:   25.5

Keluaran menunjukkan nilai suhu berubah dari 20.0 menjadi 25.5, meskipun program utama tidak memuat penugasan apa pun pada suhu. Perubahan itu terjadi karena derajat dan suhu menunjuk ke alamat memori yang sama selama prosedur berjalan, sehingga penugasan pada derajat di dalam naikkan pada dasarnya adalah penugasan pada suhu itu sendiri. Semantik ini membuat subrutin Fortran efisien untuk array besar karena datanya tidak disalin, tetapi sekaligus menuntut kehati-hatian pada argumen yang tidak boleh berubah.

Prosedur internal dengan contains#

Kedua program di atas menempatkan prosedurnya setelah pernyataan contains di akhir unit program. Prosedur semacam itu disebut prosedur internal dan hanya dapat dipanggil dari dalam host-nya, yaitu program yang memuatnya. Hubungan ini membentuk asosiasi host (host association), sehingga prosedur internal dapat membaca dan mengubah variabel yang dideklarasikan di host tanpa meneruskannya sebagai argumen. Prosedur internal bukan satu-satunya pilihan, karena prosedur juga dapat dikumpulkan di dalam modul bersama deklarasi dan variabel yang menyertainya.

Antarmuka prosedur internal bersifat eksplisit secara otomatis karena compiler selalu melihat definisinya bersama pemanggilnya. Dari antarmuka eksplisit itu, compiler dapat memeriksa jumlah argumen, tipe argumen, dan bentuk array pada setiap pemanggilan. Kesalahan seperti mengirim tiga argumen ke prosedur yang meminta empat langsung tertangkap saat kompilasi, bukan muncul sebagai perilaku salah yang sulit dilacak saat program berjalan. Keuntungan inilah yang menjadikan contains cara bawaan untuk menyusun program kecil hingga menengah.

Memilih antara fungsi dan subrutin#

Fungsi dan subrutin melayani kebutuhan yang berbeda, dan perbedaannya dapat diringkas ke dalam tiga aspek, yaitu nilai kembali, cara pemanggilan, dan jumlah hasil yang lazim. Tabel berikut membandingkan kedua bentuk prosedur pada ketiga aspek itu. Contoh konversi suhu dan statistik nilai ujian pada bagian-bagian sebelumnya masing-masing mewakili salah satu sisi tabel tersebut.

AspekFungsiSubrutin
Nilai kembaliSatu nilai melalui nama fungsiTidak ada, hasil lewat argumen
Cara memanggilDi dalam ekspresiPernyataan call
Jumlah hasil yang lazimSatuBeberapa, termasuk array

Fungsi dipilih ketika prosedur menghasilkan satu nilai dan tidak mengubah argumennya, sehingga pemanggilannya terbaca seperti ekspresi matematika. Subrutin dipilih ketika hasilnya lebih dari satu nilai, ketika array besar perlu diperbarui di tempat, atau ketika tugasnya berupa aksi seperti membaca masukan atau mencetak laporan. Subrutin hitung_statistik pada statistik.f90 adalah contoh yang tepat karena ketiga hasilnya tidak dapat dikembalikan melalui satu nama fungsi.

Ringkasan#

  • Fortran menyediakan dua bentuk prosedur, yaitu fungsi yang mengembalikan satu nilai lewat namanya dan subrutin yang menyerahkan hasil lewat argumen serta dipanggil dengan call.
  • Argumen diteruskan dengan semantik pass-by-reference, sehingga penugasan pada argumen dummy langsung mengubah argumen aktualnya dan kontrak intent diperlukan agar compiler dapat menjaga argumen yang tidak boleh berubah.
  • Prosedur internal di bawah contains memperoleh antarmuka eksplisit secara otomatis, sehingga kesalahan jumlah, tipe, dan bentuk argumen tertangkap saat kompilasi.

Referensi#