Table of Contents

05 – Fungsi

Fungsi

Fungsi merupakan aspek penting dalam pemrograman. Definisi fungsi sendiri adalah sekumpulan blok kode yang dibungkus dengan nama tertentu. Penerapan fungsi yang tepat akan menjadikan kode lebih modular dan juga dry (kependekan dari don’t repeat yourself), tak perlu menuliskan banyak kode yang kegunaannya berkali-kali, cukup sekali saja lalu panggil sesuai kebutuhan.

Pada chapter ini kita akan belajar tentang penggunaan fungsi di Go.

Penerapan Fungsi

Sebenarnya tanpa sadar, kita sudah menerapkan fungsi pada pembahasan-pembahasan sebelum ini, yaitu pada fungsi main. Fungsi main merupakan fungsi yang paling utama pada program Go.

Cara membuat fungsi cukup mudah, yaitu dengan menuliskan keyword func, diikuti setelahnya nama fungsi, kurung yang berisikan parameter, dan kurung kurawal untuk membungkus blok kode.

Parameter sendiri adalah variabel yang disisipkan pada saat pemanggilan fungsi.

Silakan lihat dan praktekan kode tentang implementasi fungsi berikut.

				
					package main

import "fmt"
import "strings"

func main() {
    var names = []string{"John", "Wick"}
    printMessage("halo", names)
}

func printMessage(message string, arr []string) {
    var nameString = strings.Join(arr, " ")
    fmt.Println(message, nameString)
}
				
			

Pada kode di atas, sebuah fungsi baru dibuat dengan nama printMessage memiliki 2 buah parameter yaitu string message dan slice string arr.

Fungsi tersebut dipanggil dalam main, dengan disisipkan 2 buah data sebagai parameter, data pertama adalah string “hallo” yang ditampung parameter message, dan parameter ke 2 adalah slice string names yang nilainya ditampung oleh parameter arr.

Di dalam printMessage, nilai arr yang merupakan slice string digabungkan menjadi sebuah string dengan pembatas adalah karakter spasi. Penggabungan slice dapat dilakukan dengan memanfaatkan fungsi strings.Join() (berada di dalam package strings).

				
					go run .
halo John Wick
				
			
Fungsi Dengan Return Value / Nilai Balik

Sebuah fungsi bisa dirancang tidak mengembalikan nilai balik (void), atau bisa mengembalikan suatu nilai. Fungsi yang memiliki nilai kembalian, harus ditentukan tipe data nilai baliknya pada saat deklarasi.

Program berikut merupakan contoh penerapan fungsi yang memiliki return value.

				
					package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().Unix())
    var randomValue int

    randomValue = randomWithRange(2, 10)
    fmt.Println("random number:", randomValue)
    randomValue = randomWithRange(2, 10)
    fmt.Println("random number:", randomValue)
    randomValue = randomWithRange(2, 10)
    fmt.Println("random number:", randomValue)
}

func randomWithRange(min, max int) int {
    var value = rand.Int() % (max - min + 1) + min
    return value
}
				
			

Penjelasan :

  1. rand.Seed(time.Now().Unix()) berada dalam package math/rand, yang harus di-import terlebih dahulu sebelum bisa dimanfaatkan.
  2. Penulisan keyword import untuk banyak package bisa dilakukan dengan dua cara, dengan menuliskannya di tiap package, atau cukup sekali saja
  3. Penggunaan Keyword return Untuk Menghentikan Proses.

Fungsi randomWithRange bertugas untuk generate angka acak sesuai dengan range yang ditentukan, yang kemudian angka tersebut dijadikan nilai kembalian fungsi.

				
					go run .

random number: 4
random number: 10
random number: 4
				
			

Cara menentukan tipe data nilai balik fungsi adalah dengan menuliskan tipe data yang diinginkan setelah kurung parameter. Bisa dilihat pada kode di atas, bahwa int merupakan tipe data nilai balik fungsi randomWithRange(min, max int) int.

Sedangkan cara untuk mengembalikan nilai itu sendiri adalah dengan menggunakan keyword return diikuti data yang ingin dikembalikan. Pada contoh di atas, return value artinya nilai variabel value dijadikan nilai kembalian fungsi.

Eksekusi keyword return akan menjadikan proses dalam blok fungsi berhenti pada saat itu juga. Semua statement setelah keyword tersebut tidak akan dieksekusi.

Fungsi Multiple Return

Umumnya fungsi hanya memiliki satu buah nilai balik saja. Jika ada kebutuhan di mana data yang dikembalikan harus banyak, biasanya digunakanlah tipe seperti map, slice, atau struct sebagai nilai balik.

Go menyediakan kapabilitas bagi programmer untuk membuat fungsi memiliki banyak nilai balik. Pada chapter ini akan dibahas bagaimana penerapannya.

Penerapan Fungsi Multiple Return

Cara membuat fungsi yang memiliki banyak nilai balik tidaklah sulit. Tinggal tulis saja pada saat deklarasi fungsi semua tipe data nilai yang dikembalikan, dan pada keyword return tulis semua data yang ingin dikembalikan. Contoh bisa dilihat pada berikut.

				
					package main

import "fmt"
import "math"

func main() {
	var diameter float64 = 15
	var area, circumference = helpers.Calculate(diameter)

	fmt.Printf("luas lingkaran\t\t: %.2f \n", area)
	fmt.Printf("keliling lingkaran\t: %.2f \n", circumference)
}

func calculate(d float64) (float64, float64) {
    // hitung luas
    var area = math.Pi * math.Pow(d / 2, 2)
    // hitung keliling
    var circumference = math.Pi * d

    // kembalikan 2 nilai
    return area, circumference
}
				
			

Output program :

				
					go run .

luas lingkaran          : 176.71 
keliling lingkaran      : 47.12
				
			

Keunikan lainnya yang jarang ditemui di bahasa lain adalah, di Go variabel yang digunakan sebagai nilai balik bisa didefinisikan di awal.

				
					func calculate(d float64) (area float64, circumference float64) {
    area = math.Pi * math.Pow(d / 2, 2)
    circumference = math.Pi * d

    return
}
				
			
  1. Fungsi math.Pow() digunakan untuk memangkat nilai. math.Pow(2, 3) berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package math.
  2. Fungsi math.Pi adalah konstanta bawaan package math yang merepresentasikan Pi atau 22/7.
Fungsi Variadic

Go mengadopsi konsep variadic function atau pembuatan fungsi dengan parameter sejenis yang tak terbatas. Maksud tak terbatas di sini adalah jumlah parameter yang disisipkan ketika pemanggilan fungsi bisa berapa saja.

Parameter variadic memiliki sifat yang mirip dengan array atau slice. Nilai dari parameter-parameter yang disisipkan bertipe data sama, dan ditampung oleh sebuah variabel saja. Cara pengaksesan tiap datanya juga sama, dengan menggunakan index.

Pada chapter ini kita akan belajar mengenai cara penerapan fungsi variadic.

Penerapan Fungsi Variadic

Deklarasi parameter variadic sama dengan cara deklarasi variabel biasa, pembedanya adalah pada parameter jenis ini ditambahkan tanda titik tiga kali (…) tepat setelah penulisan variabel (sebelum tipe data). Nantinya semua nilai yang disisipkan sebagai parameter akan ditampung oleh variabel tersebut.

Berikut merupakan contoh penerepannya.

				
					package main

import "fmt"

func main() {
    var avg = calculate(2, 4, 3, 5, 4, 3, 3, 5, 5, 3)
    var msg = fmt.Sprintf("Rata-rata : %.2f", avg)
    fmt.Println(msg)
}

func calculate(numbers ...int) float64 {
    var total int = 0
    for _, number := range numbers {
        total += number
    }

    var avg = float64(total) / float64(len(numbers))
    return avg
}
				
			
  1. Pada fungsi calculate(), parameter numbers dideklarasikan dengan disisipkan tanda 3 titik (…), menandakan bahwa numbers adalah sebuah parameter variadic dengan tipe data int.
    func calculate(numbers ...int) float64 {
  2. Pemanggilan fungsi dilakukan seperti biasa, hanya saja jumlah parameter yang disisipkan bisa banyak.
    var avg = calculate(2, 4, 3, 5, 4, 3, 3, 5, 5, 3)
  3. Nilai tiap parameter bisa diakses seperti cara pengaksesan tiap elemen slice. Pada contoh di atas metode yang dipilih adalah for – range.
    for _, number := range numbers {

Output program :

				
					go run .

Rata-rata : 3.70
				
			
Pengisian Parameter Fungsi Variadic Menggunakan Data Slice

Slice bisa digunakan sebagai parameter variadic. Caranya dengan menambahkan tanda titik tiga kali, tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut.

				
					var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
var avg = calculate(numbers...)
var msg = fmt.Sprintf("Rata-rata : %.2f", avg)

fmt.Println(msg)
				
			
Fungsi Dengan Parameter Biasa & Variadic

Parameter variadic bisa dikombinasikan dengan parameter biasa, dengan syarat parameter variadic-nya harus diposisikan di akhir. Contohnya bisa dilihat pada kode berikut.

				
					func main() {
    var hobbies = []string{"sleeping", "eating"}
    yourHobbies("wick", hobbies...)
}
func yourHobbies(name string, hobbies ...string) {
    var hobbiesAsString = strings.Join(hobbies, ", ")

    fmt.Printf("Hello, my name is: %s\n", name)
    fmt.Printf("My hobbies are: %s\n", hobbiesAsString)
}
				
			
Fungsi Closure

Definisi Closure adalah sebuah fungsi yang bisa disimpan dalam variabel. Dengan menerapkan konsep tersebut, kita bisa membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi.

Closure merupakan anonymous function atau fungsi tanpa nama. Biasa dimanfaatkan untuk membungkus suatu proses yang hanya dipakai sekali atau dipakai pada blok tertentu saja.

Closure Disimpan Sebagai Variabel

Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel yang menyimpan closure memiliki sifat seperti fungsi yang disimpannya.

Di bawah ini adalah contoh program sederhana untuk mencari nilai terendah dan tertinggi dari suatu array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel getMinMax.

				
					package main

import "fmt"

func main() {
    var getMinMax = func(n []int) (int, int) {
        var min, max int
        for i, e := range n {
            switch {
            case i == 0:
                max, min = e, e
            case e > max:
                max = e
            case e < min:
                min = e
            }
        }
        return min, max
    }

    var numbers = []int{2, 3, 4, 3, 4, 2, 3}
    var min, max = getMinMax(numbers)
    fmt.Printf("data : %v\nmin  : %v\nmax  : %v\n", numbers, min, max)
}
				
			

Bisa dilihat pada kode di atas bagaimana sebuah closure dibuat dan dipanggil. Sedikit berbeda memang dibanding pembuatan fungsi biasa. Fungsi ditulis tanpa nama, lalu ditampung dalam variabel.

				
					var getMinMax = func(n []int) (int, int) {
    // ...
}
				
			

Cara pemanggilannya, dengan menuliskan nama variabel tersebut sebagai fungsi, seperti pemanggilan fungsi biasa.

				
					var min, max = getMinMax(numbers)
				
			

Output program :

				
					go run .

data : [2 3 4 3 4 2 3]
min  : 2
max  : 4
				
			

Template %v digunakan untuk menampilkan segala jenis data. Bisa array, int, float, bool, dan lainnya.

Immediately-Invoked Function Expression (IIFE)

Closure jenis ini dieksekusi langsung pada saat deklarasinya. Biasa digunakan untuk membungkus proses yang hanya dilakukan sekali, bisa mengembalikan nilai, bisa juga tidak.

Di bawah ini merupakan contoh sederhana penerapan metode IIFE untuk filtering data array.

				
					package main

import "fmt"

func main() {
    var numbers = []int{2, 3, 0, 4, 3, 2, 0, 4, 2, 0, 3}

    var newNumbers = func(min int) []int {
        var r []int
        for _, e := range numbers {
            if e < min {
                continue
            }
            r = append(r, e)
        }
        return r
    }(3)

    fmt.Println("original number :", numbers)
    fmt.Println("filtered number :", newNumbers)
}
				
			

Ciri khas IIFE adalah adanya kurung parameter tepat setelah deklarasi closure berakhir. Jika ada parameter, bisa juga dituliskan dalam kurung parameternya.

 

				
					console.log( 'Code is Poetry' );var newNumbers = func(min int) []int {
    // ...
}(3)
				
			

Penggunaan continue hampir sama seperti menggunakan break. Kita cukup meletakkan keyword continue di tempat dimana kita ingin menghentikan dan melanjutkan perulangan. Bedanya, continue hanya akan menghentikan perulangan saat ini dan melanjutkan perulangan selanjutnya.

				
					if e < min {
    continue
}
r = append(r, e)
				
			

Hasil Output :

				
					go run .

original number : [2 3 0 4 3 2 0 4 2 0 3]
filtered number : [3 4 3 4 3]
				
			

Pada contoh di atas IIFE menghasilkan nilai balik yang kemudian ditampung newNumber. Perlu diperhatikan bahwa yang ditampung adalah nilai kembaliannya bukan body fungsi atau closure.

Closure bisa juga dengan gaya manifest typing, caranya dengan menuliskan skema closure-nya sebagai tipe data. Contoh :

				
					var closure (func (string, int, []string) int)
closure = func (a string, b int, c []string) int {
    // ..
}
				
			
Closure Sebagai Nilai Kembalian

Salah satu keunikan closure lainnya adalah bisa dijadikan sebagai nilai balik fungsi, cukup aneh memang, tapi pada suatu kondisi teknik ini sangat membantu. Di bawah ini disediakan sebuah fungsi bernama findMax(), fungsi ini salah satu nilai kembaliannya berupa closure.

				
					package main

import "fmt"

func findMax(numbers []int, max int) (int, func() []int) {
    var res []int
    for _, e := range numbers {
        if e <= max {
            res = append(res, e)
        }
    }
    return len(res), func() []int {
        return res
    }
}
				
			
  1. Nilai kembalian ke-2 pada fungsi di atas adalah closure dengan skema func() []int. Bisa dilihat di bagian akhir, ada fungsi tanpa nama yang dikembalikan.
  2. Fungsi tanpa nama yang akan dikembalikan boleh disimpan pada variabel terlebih dahulu.

Sedikit tentang fungsi findMax(), fungsi ini digunakan untuk mencari banyaknya angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Nilai kembalian pertama adalah jumlah angkanya. Nilai kembalian kedua berupa closure yang mengembalikan angka-angka yang dicari. Berikut merupakan contoh implementasi fungsi tersebut.

 

				
					func main() {
    var max = 3
    var numbers = []int{2, 3, 0, 4, 3, 2, 0, 4, 2, 0, 3}
    var howMany, getNumbers = findMax(numbers, max)
    var theNumbers = getNumbers()

    fmt.Println("numbers\t:", numbers)
    fmt.Printf("find \t: %d\n\n", max)

    fmt.Println("found \t:", howMany)    // 9
    fmt.Println("value \t:", theNumbers) // [2 3 0 3 2 0 2 0 3]
}
				
			

Output program :

				
					go run .

numbers : [2 3 0 4 3 2 0 4 2 0 3]
find    : 3

found   : 9
value   : [2 3 0 3 2 0 2 0 3]
				
			
Fungsi Sebagai parameter

Setelah pada chapter sebelumnya kita belajar mengenai fungsi yang mengembalikan nilai balik berupa fungsi, kali ini topiknya tidak kalah unik, yaitu fungsi yang digunakan sebagai parameter.

Di Go, fungsi bisa dijadikan sebagai tipe data variabel. Dari situ sangat memungkinkan untuk menjadikannya sebagai parameter juga.

Penerapan Fungsi Sebagai Parameter

Cara membuat parameter fungsi adalah dengan langsung menuliskan skema fungsi nya sebagai tipe data. Contohnya bisa dilihat pada kode berikut.

				
					package main

import "fmt"
import "strings"

func filter(data []string, callback func(string) bool) []string {
	var result []string
	for _, FilterCallback := range data {
		if filtered := callback(FilterCallback); filtered {
			result = append(result, FilterCallback)
		}
	}
	return result
}
				
			

Parameter callback merupakan sebuah closure yang dideklarasikan bertipe func(string) bool. Closure tersebut dipanggil di tiap perulangan dalam fungsi filter().

Fungsi filter() sendiri kita buat untuk filtering data array (yang datanya didapat dari parameter pertama), dengan kondisi filter bisa ditentukan sendiri. Di bawah ini adalah contoh pemanfaatan fungsi tersebut.

				
					func main() {
	var data = []string{"wick", "jason", "ethan"}
	var dataContainsO = filter(data, func(FilterCallback string) bool {
		return strings.Contains(FilterCallback, "o")
	})
	var dataLenght5 = filter(data, func(FilterCallback string) bool {
		return len(FilterCallback) == 5
	})

	fmt.Println("data asli \t\t:", data)
	// data asli : [wick jason ethan]

	fmt.Println("filter ada huruf \"o\"\t:", dataContainsO)
	// filter ada huruf "o" : [jason]

	fmt.Println("filter jumlah huruf \"5\"\t:", dataLenght5)
	// filter jumlah huruf "5" : [jason ethan]
}
				
			

Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi filter() di atas. Berikut merupakan penjelasannya.

  1. Data array (yang didapat dari parameter pertama) akan di-looping.
  2. Di tiap perulangannya, closure callback dipanggil, dengan disisipkan data tiap elemen perulangan sebagai parameter.
  3. Closure callback berisikan kondisi filtering, dengan hasil bertipe bool yang kemudian dijadikan nilai balik dikembalikan.
  4. Di dalam fungsi filter() sendiri, ada proses seleksi kondisi (yang nilainya didapat dari hasil eksekusi closure callback). Ketika kondisinya bernilai true, maka data elemen yang sedang diulang dinyatakan lolos proses filtering.
  5. Data yang lolos ditampung variabel result. Variabel tersebut dijadikan sebagai nilai balik fungsi filter().