Slicing dan section berstride

Section array dengan triplet bawah:atas:stride yang dipakai sebagai nilai kelas satu.

Daftar isi

Section adalah potongan array yang dipilih melalui rentang indeks, dan Fortran memperlakukannya sebagai nilai kelas satu (first-class), yakni nilai yang dapat ditugaskan, dikirim ke fungsi, dan dimanipulasi sebagaimana array utuh. Topik ini membahas triplet bawah : atas : stride pada setiap dimensi, penggunaan section sebagai nilai kelas satu, serta kesalahan yang lazim terjadi saat memotong array. Setelah membacanya, pemilihan sebagian elemen array dapat ditulis tanpa variabel bantu dan tanpa perulangan penyalinan manual.

Triplet bawah:atas:stride#

Rentang indeks pada section ditulis dengan triplet bawah : atas : stride. Bagian bawah dan atas menentukan elemen pertama dan terakhir yang disertakan dalam section, sedangkan stride adalah besar langkah antarindeks, yang memungkinkan pemilihan elemen berjarak tetap. Bila salah satu bagian ditiadakan, Fortran menggantinya dengan nilai bawaan, yaitu batas bawah array untuk bawah, batas atas array untuk atas, dan stride satu. Dengan aturan ini, a(:3) sama artinya dengan a(1:3), dan a(4:) berarti elemen keempat sampai terakhir.

Stride negatif membuat pemilihan berjalan menurun dari batas bawah, sehingga a(6:1:-1) menghasilkan seluruh elemen dalam urutan terbalik. Kombinasi ini sering digunakan untuk membalik urutan data tanpa perulangan penyalinan manual. Program berikut mempraktikkan triplet tersebut pada array enam elemen, termasuk penggunaannya di sisi kiri assignment, yakni pemberian nilai lewat tanda sama dengan.

fortran demo-section.f90
program demo_section
  implicit none
  integer :: a(6) = [10, 20, 30, 40, 50, 60]
  integer :: v(3)
  real    :: m(3, 3) = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], [3, 3])

  print *, "a(2:4)      =", a(2:4)
  print *, "a(:3)       =", a(:3)
  print *, "a(4:)       =", a(4:)
  print *, "a(1:6:2)    =", a(1:6:2)
  print *, "a(6:1:-1)   =", a(6:1:-1)

  v = a(2:6:2)                       ! section di-assign ke array lain
  print *, "v           =", v
  print *, "sum(a(2:4)) =", sum(a(2:4))  ! section sebagai argumen fungsi

  a(:3) = 0                          ! section pada sisi kiri assignment
  print *, "a           =", a

  print *, "m(:, 2)     =", m(:, 2)
  print *, "m(3, :)     =", m(3, :)
end program demo_section
bash
# kompilasi program contoh
gfortran demo-section.f90 -o demo-section
# jalankan program
./demo-section
text Keluaran
 a(2:4)      =          20          30          40
 a(:3)       =          10          20          30
 a(4:)       =          40          50          60
 a(1:6:2)    =          10          30          50
 a(6:1:-1)   =          60          50          40          30          20          10
 v           =          20          40          60
 sum(a(2:4)) =          90
 a           =           0           0           0          40          50          60
 m(:, 2)     =   4.00000000       5.00000000       6.00000000
 m(3, :)     =   3.00000000       6.00000000       9.00000000

Keluaran memperlihatkan setiap bentuk section satu per satu. Baris a(1:6:2) memuat elemen kesatu, ketiga, dan kelima karena langkahnya dua, sedangkan a(6:1:-1) membalik seluruh isi array. Baris a yang terakhir menunjukkan akibat a(:3) = 0, yaitu tiga elemen pertama tertimpa nol karena section itu berada di sisi kiri assignment.

Section sebagai nilai kelas satu#

Section berperilaku seperti array biasa pada hampir semua konteks. Ia dapat ditugaskan ke array lain yang bentuknya cocok, dikirim sebagai argumen fungsi intrinsik seperti sum, atau diisi nilai melalui assignment dengan sisi kiri berupa section. Pada program di atas, v = a(2:6:2) menyalin elemen kedua, keempat, dan keenam ke v, sedangkan sum(a(2:4)) menjumlahkan tiga elemen tengah menjadi 90 tanpa variabel bantu apa pun.

Cara memilih elemen juga tidak terbatas pada triplet. Subscript, yakni ekspresi di dalam tanda kurung pemilih elemen, boleh berupa vektor berisi indeks yang diinginkan, misalnya a([5, 3, 1]) untuk mengambil elemen kelima, ketiga, dan pertama dalam urutan itu. Subscript berupa vektor memberi kebebasan memilih elemen tak berurutan dengan satu ekspresi, kemampuan yang tidak dimiliki triplet.

fortran Subscript berupa vektor indeks
! tidak-dijalankan
print *, a([5, 3, 1])     ! elemen kelima, ketiga, lalu pertama
b = a([1, 1, 2])          ! sah di sisi kanan, indeks boleh berulang

Section juga mudah dikirim ke prosedur buatan sendiri. Argumen dummy, yakni nama formal di dalam prosedur yang menampung argumen aktual saat pemanggilan, dapat dideklarasikan dengan bentuk diasumsikan (assumed shape) yang ditulis x(:). Bentuk semacam ini menerima section apa pun sekaligus mengikuti bentuk section pengirimnya, sehingga prosedur tidak perlu mengetahui ukuran array asalnya. Penerusan berlangsung melalui deskriptor array, yaitu metadata yang memuat alamat dan bentuk datanya, tanpa penyalinan elemen, sehingga mengirim section besar tetap efisien. Pola ini melengkapi sifat nilai kelas satu, karena section dapat diperlakukan sama dengan array utuh pada setiap konteks pemanggilan prosedur.

fortran Fungsi dengan argumen bentuk diasumsikan
! tidak-dijalankan
function rata_rata(x) result(r)
   real, intent(in) :: x(:)      ! bentuk diasumsikan, mengikuti section pengirim
   real :: r
   r = sum(x) / size(x)
end function rata_rata

! pemanggilan dari program utama:
rata = rata_rata(a(2:6:2))        ! section berstride dikirim apa adanya

Section pada array dua dimensi#

Pada array dimensi banyak, setiap dimensi menetapkan rentangnya sendiri, dan dimensi yang diambil utuh cukup diberi tanda titik dua tanpa triplet. Ekspresi m(:, 2) mengambil kolom kedua seutuhnya, yakni seluruh baris pada kolom itu, sedangkan m(3, :) mengambil baris ketiga seutuhnya. Triplet tetap berlaku per dimensi, sehingga m(1:3:2, :) memilih baris pertama dan ketiga dari semua kolom.

Hasil pemotongan mengikuti susunan elemen pada array sumbernya, karena section hanya memilih elemen dan tidak mengurutkannya ulang. Baris m(:, 2) pada keluaran program di atas memuat 4.0, 5.0, dan 6.0, yaitu tepat kolom kedua hasil reshape yang terisi kolom demi kolom. Section dua dimensi seperti ini menjadi bahan utama perhitungan numerik, misalnya mengambil satu blok dari array berdimensi tinggi untuk dikirim ke prosedur lain tanpa penyalinan manual.

Kesalahan umum#

Kesalahan pertama adalah menganggap triplet selalu menghasilkan elemen. Rentang a(1:6:-1) memakai stride negatif, sehingga pemilihan dimulai dari batas bawah dan berjalan menurun ke arah batas atas. Karena nilai batasnya tertulis 1 sampai 6, arah tersebut tidak memuat satu elemen pun dan hasilnya section kosong tanpa pesan error. Bentuk yang benar untuk membalik array adalah a(6:1:-1). Program yang terus bekerja dengan array kosong biasanya menunjukkan triplet yang tertukar arahnya, dan pemeriksaan size atas section membantu menemukannya.

Kesalahan kedua adalah menugaskan section ke array tetap dengan jumlah elemen berbeda, misalnya v(2) = a(2:4). Sisi kanan berisi tiga elemen sedangkan v hanya menampung dua, sehingga program berhenti dengan pesan error bentuk tidak cocok saat berjalan. Perbaikannya adalah menyamakan banyaknya elemen kedua sisi, atau menyimpan hasil section pada array allocatable yang menyesuaikan ukurannya sendiri.

Ringkasan#

  • Triplet bawah : atas : stride memilih elemen array, dengan nilai bawaan batas array dan stride satu bila bagian itu ditiadakan.
  • Section dapat ditugaskan, dikirim ke fungsi, dan ditempatkan di sisi kiri assignment sebagaimana array utuh.
  • Subscript berupa vektor seperti a([5, 3, 1]) memilih elemen tak berurutan, tetapi indeksnya tidak boleh berulang pada sisi kiri assignment.
  • Rentang dengan arah stride yang salah menghasilkan section kosong tanpa pesan error.

Referensi#