Fungsi (4)

Mʀ Bee
6 min readDec 19, 2016

--

.

Fungsi kembar

Semua pengenal dalam kode program harus bersifat unik artinya setiap pengenal hanya ada satu di setiap lingkupnya. Pada umumnya, keunikan pengenal adalah dari namanya (identifier). Misalnya, Swift tak mengijinkan kode program seperti berikut ini:

let a = 100
var a = 10 // nama a telah digunakan ⬅︎ salah!

Kode program di atas akan menghasilkan kesalahan karena telah ada penyimpan bernama sama. Namun keunikan nama pengenal tidak berlaku pada fungsi sehingga dalam Swift dimungkinkan beberapa fungsi memiliki nama yang sama. Hal ini disebut dengan function overloading atau fungsi dengan tugas berlebih, atau kita sebut saja dengan fungsi kembar.

Fungsi kembar bisa terjadi karena faktor keunikan fungsi tidak hanya dari nama fungsinya saja, tetapi juga termasuk struktur parameter fungsi. Struktur parameter fungsi terdiri dari nama dan jumlah parameter, beserta tipe data keluaran fungsi, jika ada. Dua fungsi (atau lebih) bisa saja memiliki nama yang sama tetapi harus memiliki struktur parameter yang berbeda. Contoh:

// fungsi add versi 1
func
add(_ a: Int, b: Int) -> Int {
return a+b
}
// fungsi add versi 2
func
add(a: Int, b: Int) -> Int {
return a+b
}
// fungsi add versi 3
func
add(a: Double, b: Double) -> Double {
return a+b
}
// fungsi add versi 4
func
add(_ a: Double, b: Int) -> Double {
return a + Double(b)
}
let a = add(1, b:2)
let b = add(a:3, b:4)
let c = add(a:1.2, b:3.4)
let d = add(1.2, b:3)
print(a)
print(b)
print(c)
print(d)

Pada contoh di atas, ada 4 (empat) versi fungsi add (namanya sama), tetapi masing-masing fungsi mempunyai ciri khas yang berbeda. Jika ditulis judulnya saja, keempat fungsi add di atas cirinya adalah sebagai berikut:

func add(_ a: Int, b: Int) -> Int         // versi 1
func add(a: Int, b: Int) -> Int // versi 2
func add(a: Double, b: Double) -> Double // versi 3
func add(_ a: Double, b: Int) -> Double // versi 4

Fungsi add versi 1 mirip dengan yang versi 2, bedanya di nama parameter pertama, di mana versi 1 tidak mempunyai nama sebutan sedang versi 2 mempunyai nama sebutan. Fungsi add versi 1 juga mirip dengan versi 3, bedanya di tipe data parameter dan tipe data keluarannya, di mana versi 1 parameter dan keluarannya bertipe bilangan bulat sedang versi 3 bertipe bilangan pecahan. Fungsi add versi 1 dengan versi 4 juga cukup mirip pada nama parameter, bedanya di tipe data salah satu parameternya berbeda. Dan seterusnya.

Bagaimana jika ada fungsi bernama sama dengan parameter sama namun keluarannya berbeda? Coba kita tambahkan baris berikut ke dalam contoh program di atas:

// fungsi add versi 5
func
add(_ a: Int, b: Int) -> Double {
return Double(a+b)
}
let e = add(1, b:2) // pemanggilan fungsi membingungkan ⬅︎ salah!print(e)

Fungsi add versi 5 ini parameternya persis sama dengan versi 1, namun tipe data keluaran fungsinya berbeda. Ternyata tambahan kode program di atas menghasilkan kesalahan. Hal ini disebabkan Swift gagal melakukan deteksi otomatis tipe keluaran fungsi pada pemanggilan fungsi let e = add(1,b:2) karena penulisannya sama persis dengan fungsi add versi 1 (yaitu pada penyimpan a). Perbedaannya terletak pada tipe data keluaran fungsi. Swift tak bisa menentukan fungsi mana yang harus dipanggil, versi 1 atau versi 5.

Untuk memperbaiki kesalahan di atas, kita harus memperjelas tipe data penyimpan e agar Swift bisa menentukan fungsi add versi mana yang akan dipanggil. Jika e bertipe Int maka yang dipanggil adalah versi 1, sedang jika e bertipe Double maka yang dipanggil adalah versi 5. Demikian juga pada pemanggilan fungsi add untuk penyimpan a pun harus diperjelas. Setelah diperjelas maka contoh program di atas bisa berjalan dengan baik, seperti pada gambar di bawah ini.

Menjalankan contoh program di atas di Xcode Playground. Sumber: sendiri.

Lingkup dan akses

Sebagaimana dalam setiap bahasa pemrograman, dalam Swift juga dikenal lingkup (scope). Dan sebagai keluarga bahasa C, Swift menandai lingkup baru dengan tanda {} yang juga merupakan penanda kelompok perintah. Artinya, setiap ada kelompok perintah baru, otomatis tercipta lingkup baru di dalam lingkup sebelumnya (yang lebih besar). Karena fungsi juga menggunakan penanda kelompok perintah maka setiap fungsi juga memiliki lingkupnya sendiri.

Sifat akses lingkup adalah dari dalam ke luar. Maksudnya, kode program yang ada di lingkup lebih dalam bisa mengakses pengenal yang ada di lingkup lebih luar, namun tidak berlaku sebaliknya. Lingkup terluar disebut dengan lingkup global (global scope). Dan karena merupakan lingkup terluar maka seluruh pengenal di lingkup global bisa diakses dari seluruh bagian program, bahkan dari yang lingkup paling dalam sekali pun.

Perhatikan contoh kode berikut:

// variabel a di lingkup global
var
a = 1
func f(_ p: Int) -> String {
// variabel s di lingkup lokal dalam fungsi f
let s = a*p
return String(s)
}
// cetak keluaran fungsi f
print(f(3)) // mencetak "3" dari 1*3

Ada 3 bagian akses dalam lingkup sebuah kelompok perintah, secara urutan prioritas akses adalah sebagai berikut:

  1. Akses dalam, adalah pengenal yang ada di dalam kelompok perintah atau segala yang diapit tanda {}. Pada contoh program di atas, penyimpan s berada dalam lingkup ini.
  2. Akses awalan, adalah pengenal yang mengawali kelompok perintah. Jika awalan kelompok perintah adalah fungsi maka seluruh struktur parameter fungsi tersebut ada dalam lingkup ini. Pada contoh program di atas, parameter p berada dalam lingkup ini.
  3. Akses luar, adalah pengenal yang ada di luar kelompok perintah atau segala yang berada di luar perintah fungsi dan kelompok perintah fungsi. Pada contoh program di atas, penyimpan a berada di lingkup ini.

Urutan prioritas akses ini menentukan mana yang dibaca atau dijalankan jika ada beberapa pengenal dengan nama yang sama di lingkup yang berbeda. Contoh program di atas cukup jelas apa ada di lingkup mana karena setiap pengenal (penyimpan, parameter, dan sebagainya) masing-masing mempunyai nama yang berbeda (unik). Sekarang perhatikan contoh program berikut:

// variabel a di lingkup global
var
a = 1
func f(_ a: Int) -> String {
// variabel s di lingkup lokal dalam fungsi f
let a = 2
return String(a)
}
// cetak keluaran fungsi f
print(f(3))

Perhatikan semua penyimpan dan parameter sekarang menggunakan nama yang sama, yaitu a. Penyimpan global a bernilai 1, parameter fungsi a bernilai 3, dan penyimpan lokal a dalam fungsi f bernilai 2. Apa yang dicetak oleh perintah print(f(3))? Apakah 1, 2, atau 3?

Berdasarkan urutan prioritas akses maka yang tercetak adalah 2, karena dari semua nama a di atas, penyimpan lokal a dalam fungsi f berada di prioritas tertinggi. Dalam contoh ini, penyimpan lokal a dalam fungsi f menghalangi akses ke parameter a dan penyimpan global a. Hal ini disebut dengan penghalangan atau shadowing akibat prioritas akses.

Bagaimana cara agar perintah print(f(3)) mencetak parameter a dan bukannya penyimpan lokal a? Cukup ganti nama penyimpan lokal a menjadi — misalnya — b sehingga nama a merujuk pada parameter a. Demikian juga jika ingin mencetak penyimpan global a dari fungsi f, maka cukup ganti nama parameter a menjadi — misalnya — c sehingga nama a merujuk pada penyimpan global a.

Penghalangan ini sebisa mungkin dihindari agar kode program lebih jelas dan lebih mudah dipahami. Namun jika tak bisa dihindari setidaknya kita paham apa yang sebenarnya diakses oleh kode program yang kita tulis. Penghalangan merupakan salah satu sumber kesalahan sederhana namun kadang sulit ditemukan.

Perlu diingat bahwa lingkup yang lebih luar tak bisa mengakses segala yang ada di lingkup yang lebih dalam. Segala yang ada di lingkup yang lebih dalam tak dikenal oleh lingkup yang lebih luar. Contoh:

// variabel a di lingkup global
var
a = 1
func f(_ p: Int) -> String {
// variabel s di lingkup lokal dalam fungsi f
let s = a*p
return String(s)
}
// cetak keluaran fungsi f
print(s) // nama s tak dikenal ⬅︎ salah!

Contoh program di atas menghasilkan kesalahan karena perintah print(s) mencoba membaca pengenal s yang tak dikenal atau berada di luar jangkauan lingkupnya.

Aturan lingkup dan akses tak hanya berlaku untuk fungsi saja, tapi juga berlaku untuk seluruh kelompok perintah (yang diapit tanda {}). Misalnya pada contoh program perulangan berikut:

var i = 1
for i in 1...3 {
let i = 2
print(i)
}

Apa yang akan dicetak perintah print(i) dalam contoh program di atas?

Selanjutnya kita akan membahas tentang Struktur Data.

--

--

Mʀ Bee

is an Apple’s Swift programming language enthusiast.