Operasi utuh array

Aritmetika per elemen, assignment array, dan fungsi elemental tanpa perulangan eksplisit.

Daftar isi

Fortran menerapkan operasi aritmetika pada array secara utuh (whole array), yakni seluruh elemen diolah dalam satu ekspresi tanpa menulis indeks satu per satu. Topik ini membahas aritmetika per elemen, assignment array, fungsi elemental, serta penugasan bersyarat dengan where. Setelah membacanya, perhitungan yang lazimnya memerlukan perulangan eksplisit dapat ditulis sebagai satu ekspresi array yang menyatakan maksudnya secara langsung.

Aritmetika per elemen dan assignment#

Operator +, -, *, /, dan ** bekerja per elemen apabila salah satu operannya berupa array. Ekspresi a + b menghasilkan array yang setiap elemennya merupakan jumlah elemen a dan b pada posisi yang sama, sedangkan 2.0 * a mengalikan setiap elemen dengan skalar. Satu-satunya syaratnya adalah kedua operan array saling cocok bentuk (conformable), yakni berbentuk sama, atau salah satu operannya berupa skalar. Assignment, yakni pemberian nilai melalui tanda sama dengan, mengikuti aturan yang sama, sehingga c = a menyalin seluruh elemen a ke c tanpa perulangan apa pun.

Program berikut mencontohkan operasi-operasi tersebut pada array empat elemen, dan hasil setiap operasi dicetak satu baris. Setiap pernyataan disertai komentar yang menyebut operasinya. Semua baris keluarannya dapat dihitung ulang dengan cepat karena nilai yang dipilih sederhana.

fortran demo-operasi.f90
program demo_operasi
  implicit none
  real :: a(4) = [1.0, 4.0, 9.0, 16.0]
  real :: b(4) = [10.0, 20.0, 30.0, 40.0]
  real :: c(4)

  c = a + b                    ! penjumlahan per elemen
  print *, "a + b   =", c
  c = 2.0 * a                  ! skalar dikalikan pada tiap elemen
  print *, "2.0 * a =", c
  c = sqrt(a)                  ! fungsi elemental bekerja per elemen
  print *, "sqrt(a) =", c

  where (a > 5.0)              ! penugasan dengan mask logika
     c = sqrt(a)
  elsewhere
     c = 0.0
  end where
  print *, "where   =", c
end program demo_operasi
bash
# kompilasi program contoh
gfortran demo-operasi.f90 -o demo-operasi
# jalankan program
./demo-operasi
text Keluaran
 a + b   =   11.0000000       24.0000000       39.0000000       56.0000000
 2.0 * a =   2.00000000       8.00000000       18.0000000       32.0000000
 sqrt(a) =   1.00000000       2.00000000       3.00000000       4.00000000
 where   =   0.00000000       0.00000000       3.00000000       4.00000000

Empat baris keluaran masing-masing berasal dari satu pernyataan assignment, dan tidak ada satu perulangan pun yang ditulis di program. Perhatikan bahwa sqrt(a) menghasilkan 1.0, 2.0, 3.0, dan 4.0 karena a memuat kuadrat dari keempat bilangan itu, sehingga hasilnya mudah diperiksa sekilas.

Fungsi elemental#

Fungsi intrinsik seperti sqrt, abs, sin, dan exp tergolong elemental, yakni didefinisikan untuk satu skalar tetapi diterapkan otomatis pada setiap elemen apabila argumennya array. Bentuk hasilnya mengikuti bentuk argumen arraynya, sehingga sqrt(a) untuk a yang berisi empat elemen menghasilkan empat nilai akar kuadrat. Fungsi yang sama tetap menerima argumen skalar, sehingga satu nama fungsi melayani dua bentuk pemanggilan.

Sifat elemental juga dapat diberikan pada fungsi buatan sendiri melalui awalan elemental pada deklarasi prosedurnya. Fungsi semacam itu ditulis untuk satu elemen skalar, sedangkan penanganan bentuk arraynya dilakukan otomatis oleh compiler pada setiap pemanggilan. Dengan cara ini, aturan perhitungan cukup ditulis satu kali untuk satu elemen, dan seluruh array mengikutinya tanpa kode tambahan.

fortran Pemanggilan fungsi elemental pada skalar dan array
! tidak-dijalankan
y = sqrt(a)        ! array: akar kuadrat dihitung per elemen
z = sqrt(16.0)     ! skalar: berfungsi seperti pemanggilan biasa

Perbandingan dengan perulangan eksplisit#

Operasi utuh array hanyalah penulisan singkat untuk perulangan yang mengolah elemen satu per satu. Kedua bentuk berikut menghasilkan nilai c yang persis sama:

fortran Perulangan eksplisit dibandingkan operasi utuh array
! tidak-dijalankan
do i = 1, 4
   c(i) = a(i) + b(i)   ! versi perulangan
end do
c = a + b               ! versi utuh array, hasilnya sama

Bentuk utuh array lebih pendek, tidak memerlukan variabel kendali i, dan menyatakan maksud perhitungan secara langsung. Perulangan itu tetap terjadi, tetapi ditambahkan oleh compiler, yakni program yang menerjemahkan kode sumber menjadi program mesin. Compiler bebas menentukan urutan evaluasi antarelemen karena standar tidak menetapkannya. Kebebasan ini dipakai untuk menyusun akses memori dan perhitungan seefisien mungkin pada target mesinnya, tanpa perubahan apa pun pada kode sumbernya.

Penugasan bersyarat dengan where#

Konstruksi where membatasi sebuah assignment array hanya pada elemen yang lolos syarat. Syaratnya berupa mask, yakni array logika yang bentuknya sama dengan array yang ditugasi. Pada program di atas, where (a > 5.0) memilih elemen ketiga dan keempat karena hanya keduanya yang bernilai di atas 5.0, sehingga c berisi nol untuk dua elemen pertama dan akar kuadrat untuk dua elemen terakhir. Cabang elsewhere menampung elemen yang gagal syarat, dan cabang ini boleh ditiadakan apabila elemen tersebut tidak perlu diubah.

where melengkapi operasi utuh array untuk penugasan yang tidak berlaku seragam pada semua elemen, misalnya mengganti nilai yang berada di luar rentang wajar atau menonaktifkan hasil pengukuran yang ditandai gagal. Satu mask dapat menaungi beberapa pernyataan assignment sekaligus sebelum konstruksi ditutup dengan end where. Bentuk ini menggantikan perulangan dengan percabangan di dalamnya, sehingga niat perhitungannya tetap terbaca sebagai satu kesatuan.

Kesalahan umum#

Kesalahan pertama adalah menerapkan operasi pada array yang bentuknya berbeda. Apabila bentuk kedua operan diketahui saat kompilasi, gfortran menolaknya dengan pesan bahwa operan tidak cocok bentuk. Apabila bentuk baru diketahui saat program berjalan, misalnya pada array allocatable, pelanggaran yang sama dapat lolos tanpa pesan dan hasilnya tidak dapat dipercaya, sehingga aktifkan opsi -fcheck=bounds agar pelanggaran itu dilaporkan sebagai error saat berjalan. Samakan bentuk kedua sisi, atau gunakan section untuk memotong operannya sebelum operasi dijalankan.

Kesalahan kedua adalah memanggil fungsi elemental dengan tipe argumen yang tidak didukungnya. Fungsi sqrt hanya menerima real dan complex, sehingga pemanggilan sqrt(a) atas array integer gagal saat kompilasi dengan pesan bahwa argumennya harus bertipe REAL atau COMPLEX. Konversi dengan intrinsik real memperbaikinya, seperti pada bentuk berikut:

fortran Konversi tipe sebelum pemanggilan fungsi elemental
! tidak-dijalankan
y = sqrt(real(a))    ! konversi array integer ke real sebelum akar kuadrat

Perhatian serupa juga berlaku pada campuran operasi integer dan real. Ekspresi a / 2 dengan a bertipe integer melakukan pembagian bilangan bulat, sedangkan maksud perhitungannya biasanya pembagian real. Tuliskan a / 2.0 atau lakukan konversi eksplisit agar aturan tipe ekspresi mengikuti maksud perhitungannya.

Ringkasan#

  • Operan array pada +, *, dan operator lain bekerja per elemen, dengan syarat bentuknya sama atau salah satu operannya skalar.
  • Fungsi elemental diterapkan otomatis pada setiap elemen argumen arraynya, dan hasilnya berbentuk sama dengan argumennya.
  • Operasi utuh array setara dengan perulangan eksplisit, tetapi menyatakan maksud lebih langsung dan membiarkan compiler mengatur pelaksanaannya.
  • where membatasi assignment array pada elemen yang lolos mask logika, dengan cabang opsional elsewhere.

Referensi#