diff --git a/content/2-instalasi-golang.md b/content/2-instalasi-golang.md
index 0e4feb93a..82025dc35 100644
--- a/content/2-instalasi-golang.md
+++ b/content/2-instalasi-golang.md
@@ -10,7 +10,7 @@ URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung und
## A.2.1. Instalasi Go *Stable*
-#### • Instalasi Go di Windows
+#### ◉ Instalasi Go di Windows
1. Download terlebih dahulu *installer*-nya di [https://golang.org/dl/](https://golang.org/dl/). Pilih *installer* untuk sistem operasi Windows sesuai jenis bit yang digunakan.
@@ -26,7 +26,7 @@ URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung und
> Sering terjadi, command `go version` tidak bisa dijalankan meskipun instalasi sukses. Solusinya bisa dengan restart CMD (tutup CMD, kemudian buka lagi). Setelah itu coba jalankan ulang command di atas.
-#### • Instalasi Go di MacOS
+#### ◉ Instalasi Go di MacOS
Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh/).
@@ -57,7 +57,7 @@ Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh
5. Jika output adalah sama dengan versi Go yang ter-*install*, menandakan proses instalasi berhasil.
-#### • Instalasi Go di Linux
+#### ◉ Instalasi Go di Linux
1. Unduh arsip *installer* dari [https://golang.org/dl/](https://golang.org/dl/), pilih installer untuk Linux yang sesuai dengan jenis bit komputer anda. Proses download bisa dilakukan lewat CLI, menggunakan `wget` atau `curl`.
diff --git a/content/A-buffered-channel.md b/content/A-buffered-channel.md
index 9a1af3024..b5e16bf82 100644
--- a/content/A-buffered-channel.md
+++ b/content/A-buffered-channel.md
@@ -59,7 +59,7 @@ Pengiriman data indeks ke 0, 1, 2 dan 3 akan berjalan secara asynchronous, hal i
Karena pengiriman dan penerimaan data via buffered channel terjadi tidak selalu synchronous (tergantung jumlah buffer-nya), maka ada kemungkinan dimana eksekusi program selesai namun tidak semua data diterima via channel `messages`. Karena alasan ini pada bagian akhir ditambahkan statement `time.Sleep(1 * time.Second)` agar ada jeda 1 detik sebelum program selesai.
-#### • Fungsi `time.Sleep()`
+#### ◉ Fungsi `time.Sleep()`
Fungsi ini digunakan untuk menambahkan delay sebelum statement berikutnya dieksekusi. Durasi delay ditentukan oleh parameter, misal `1 * time.Second` maka durasi delay adalah 1 detik.
diff --git a/content/A-channel-range-close.md b/content/A-channel-range-close.md
index 7615cd947..9ce0ee6a7 100644
--- a/content/A-channel-range-close.md
+++ b/content/A-channel-range-close.md
@@ -48,11 +48,11 @@ Setelah 20 data yang dikirim sukses diterima, channel `ch` di-non-aktifkan denga
![Penerapan for-range-close pada channel](images/A_channel_range_close_1_for_range_close.png)
----
+## A.34.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai channel.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.34.1.1. Channel Direction
+#### ◉ Channel Direction
Go mendesain API channel untuk mendukung level akses channel, apakah hanya sebagai penerima, pengirim, atau penerima sekaligus pengirim. Konsep ini disebut dengan **channel direction**.
diff --git a/content/A-channel.md b/content/A-channel.md
index 1f5836199..583ada27a 100644
--- a/content/A-channel.md
+++ b/content/A-channel.md
@@ -131,11 +131,11 @@ Passing data bertipe channel lewat parameter sifatnya **pass by reference**, yan
![Parameter channel](images/A_channel_3_channel_param.png)
----
+## A.32.3. Penjelasan tambahan
-Berikut merupakan penjelasan tambahan untuk kode di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
+#### ◉ Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
Data slice yang baru di inisialisasi bisa langsung di-iterasi, caranya mudah dengan menuliskannya langsung setelah keyword `range`.
@@ -145,7 +145,7 @@ for _, each := range []string{"wick", "hunt", "bourne"} {
}
```
-#### • Eksekusi Goroutine Pada IIFE
+#### ◉ Eksekusi Goroutine Pada IIFE
Eksekusi goroutine tidak harus pada fungsi atau closure yang sudah terdefinisi. Sebuah IIFE juga bisa dijalankan sebagai goroutine baru. Caranya dengan langsung menambahkan keyword `go` pada waktu deklarasi-eksekusi IIFE-nya.
diff --git a/content/A-concurrency-pipeline.md b/content/A-concurrency-pipeline.md
index 4aaab1039..d842a7649 100644
--- a/content/A-concurrency-pipeline.md
+++ b/content/A-concurrency-pipeline.md
@@ -260,7 +260,7 @@ Pada bagian ini kita akan re-write ulang program 2, isinya masih sama persis kal
Kenapa kita pecah, karena ketiga proses tersebut bisa dijalankan bersama secara konkuren, dalam artian misalnya ketika `file1` sudah selesai dibaca, perhitungan md5sum-nya bisa dijalankan secara bersama dengan pembacaan `file2`. Begitu juga untuk proses rename-nya, misalnya, proses rename `file24` bisa dijalnkan secara konkuren bersamaan dengan proses hitung md5sum `file22` dan bersamaan dengan proses baca `file28`.
-#### • Basis Kode Program
+#### ◉ Basis Kode Program
Mungkin agar lebih terlihat perbandingannya nanti di akhir, kita siapkan file terpisah saja untuk program ini. Siapkan file baru bernama `3-find-md5-sum-of-file-then-rename-it-concurrently.go`.
@@ -291,7 +291,7 @@ type FileInfo struct {
Kurang lebih sama seperti sebelumnya, hanya saja ada beberapa packages lain yg di-import dan ada struct `FileInfo`. Struct ini digunakan sebagai metadata tiap file. Karena nantinya proses read file, md5sum, dan rename file akan dipecah menjadi 3 goroutine berbeda, maka perlu ada metadata untuk mempermudah tracking file, agar nanti ketika dapat md5 sum nya tidak salah simpan, dan ketika rename tidak salah file.
-#### • Pipeline 1: Baca File
+#### ◉ Pipeline 1: Baca File
Siapkan fungsi main, lalu panggil fungsi `readFiles()`.
@@ -359,7 +359,7 @@ Mengenai channel `chanOut` sendiri, akan di-close ketika dipastikan **semua file
Ok lanjut, karena di sini ada channel yang digunakan sebagai media pengiriman data (`FileInfo`), maka juga harus ada penerima data channel-nya dong. Yups.
-#### • Pipeline 2: MD5 Hash Konten File
+#### ◉ Pipeline 2: MD5 Hash Konten File
Tepat di bawah pipeline 1, tambahkan pemanggilan fungsi `getSum()` sebanyak 3x, bisa lebih banyak sih sebenarnya, bebas. Kemudian jadikan nilai balik pemanggilan fungsi tersebut sebagai variadic argument pemanggilan fungsi `mergeChanFileInfo()`.
@@ -447,7 +447,7 @@ Secara garis besar, pada fungsi ini terjadi beberapa proses:
* Channel `chanOut` ini dijadikan sebagai nilai balik fungsi.
* Di situ kita gunakan `sync.WaitGroup` untuk kontrol goroutine. Kita akan tunggu hingga semua channel input adalah closed, setelah itu barulah kita close channel `chanOut` ini.
-#### • Pipeline 3: Rename file
+#### ◉ Pipeline 3: Rename file
Tambahkan statement pipeline ketiga, yaitu pemanggilan fungsi Fan-out `rename()`, lalu panggil fungsi Fan-in `mergeChanFileInfo()` untuk multiplex channel kembalian fungsi `rename()`.
@@ -493,7 +493,7 @@ Bisa dilihat di atas kita rename file asli yang informasi path-nya ada di `FileI
Setelah semua file berhasil di-rename, maka channel `chanOut` di-close.
-#### • Pipeline 4 / Output
+#### ◉ Pipeline 4 / Output
Serangkaian proses yang sudah kita setup punya ketergantungan tinggi satu sama lain, dan eksekusinya harus berurutan meskipun *concurrently*. Ini secara langsung juga mempermudah kita dalam mengolah output hasil pipeline. Kita cukup fokus ke channel hasil Fan-in yang paling terakhir, yaitu channel `chanRename`.
diff --git a/content/A-data-type-conversion.md b/content/A-data-type-conversion.md
index 1bfef034c..135d4f3ba 100644
--- a/content/A-data-type-conversion.md
+++ b/content/A-data-type-conversion.md
@@ -6,7 +6,7 @@ Di beberapa chapter sebelum ini kita telah menerapkan beberapa cara konversi dat
Package `strconv` berisi banyak fungsi yang sangat membantu kita untuk melakukan konversi. Berikut merupakan beberapa fungsi yang dalam package tersebut.
-#### • Fungsi `strconv.Atoi()`
+#### ◉ Fungsi `strconv.Atoi()`
Fungsi ini digunakan untuk konversi data dari tipe `string` ke `int`. `strconv.Atoi()` menghasilkan 2 buah nilai kembalian, yaitu hasil konversi dan `error` (jika konversi sukses, maka `error` berisi `nil`).
@@ -26,7 +26,7 @@ func main() {
}
```
-#### • Fungsi `strconv.Itoa()`
+#### ◉ Fungsi `strconv.Itoa()`
Merupakan kebalikan dari `strconv.Atoi`, berguna untuk konversi `int` ke `string`.
@@ -37,7 +37,7 @@ var str = strconv.Itoa(num)
fmt.Println(str) // "124"
```
-#### • Fungsi `strconv.ParseInt()`
+#### ◉ Fungsi `strconv.ParseInt()`
Digunakan untuk konversi `string` berbentuk numerik dengan basis tertentu ke tipe numerik non-desimal dengan lebar data bisa ditentukan.
@@ -63,7 +63,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatInt()`
+#### ◉ Fungsi `strconv.FormatInt()`
Berguna untuk konversi data numerik `int64` ke `string` dengan basis numerik bisa ditentukan sendiri.
@@ -74,7 +74,7 @@ var str = strconv.FormatInt(num, 8)
fmt.Println(str) // 30
```
-#### • Fungsi `strconv.ParseFloat()`
+#### ◉ Fungsi `strconv.ParseFloat()`
Digunakan untuk konversi `string` ke numerik desimal dengan lebar data bisa ditentukan.
@@ -89,7 +89,7 @@ if err == nil {
Pada contoh di atas, string `"24.12"` dikonversi ke float dengan lebar tipe data `float32`. Hasil konversi `strconv.ParseFloat` adalah sesuai dengan standar [IEEE Standard for Floating-Point Arithmetic](https://en.wikipedia.org/wiki/IEEE_floating_point).
-#### • Fungsi `strconv.FormatFloat()`
+#### ◉ Fungsi `strconv.FormatFloat()`
Berguna untuk konversi data bertipe `float64` ke `string` dengan format eksponen, lebar digit desimal, dan lebar tipe data bisa ditentukan.
@@ -113,7 +113,7 @@ Ada beberapa format eksponen yang bisa digunakan. Detailnya bisa dilihat di tabe
| `g` | Akan menggunakan format eksponen `e` untuk eksponen besar dan `f` untuk selainnya |
| `G` | Akan menggunakan format eksponen `E` untuk eksponen besar dan `f` untuk selainnya |
-#### • Fungsi `strconv.ParseBool()`
+#### ◉ Fungsi `strconv.ParseBool()`
Digunakan untuk konversi `string` ke `bool`.
@@ -126,7 +126,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatBool()`
+#### ◉ Fungsi `strconv.FormatBool()`
Digunakan untuk konversi `bool` ke `string`.
diff --git a/content/A-defer-exit.md b/content/A-defer-exit.md
index f659f9177..bb719c471 100644
--- a/content/A-defer-exit.md
+++ b/content/A-defer-exit.md
@@ -19,7 +19,7 @@ func main() {
}
```
-Output:
+Output program:
![Penerapan `defer`](images/A_defer_exit_1_defer.png)
@@ -45,7 +45,7 @@ func orderSomeFood(menu string) {
}
```
-Output:
+Output program:
![Penerapan `defer` dengan `return`](images/A_defer_exit_2_defer_return.png)
@@ -68,7 +68,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
@@ -95,7 +95,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
diff --git a/content/A-error-panic-recover.md b/content/A-error-panic-recover.md
index 8a61870be..dca1c4ff4 100644
--- a/content/A-error-panic-recover.md
+++ b/content/A-error-panic-recover.md
@@ -165,7 +165,7 @@ func main() {
}
```
-Output:
+Output program:
![Handle panic menggunakan recover](images/A_error_panic_recover_4_recover.png)
diff --git a/content/A-file.md b/content/A-file.md
index 2ab152a7b..58b386141 100644
--- a/content/A-file.md
+++ b/content/A-file.md
@@ -1,12 +1,12 @@
# A.50. File
-Ada beberapa cara yang bisa digunakan untuk operasi file di Go. Pada chapter ini kita akan mempelajari teknik yang paling dasar, yaitu dengan memanfaatkan `os.File`.
+Pada chapter ini kita akan belajar beberapa teknik operasi file yang paling dasar.
## A.50.1. Membuat File Baru
-Pembuatan file di Go sangatlah mudah, cukup dengan memanggil fungsi `os.Create()` lalu memasukkan path file yang ingin dibuat sebagai parameter. Jika ternyata file yang akan dibuat sudah ada, maka akan ditimpa. Bisa memanfaatkan `os.IsNotExist()` untuk mendeteksi apakah file sudah dibuat atau belum.
+Pembuatan file di Go sangat mudah, dilakukan dengan memanfaatkan fungsi `os.Create()` disertai path file sebagai argument pemanggilan fungsi. Jika ternyata file yang akan dibuat sudah ada duluan, maka operasi `os.Create()` akan menimpa file yang sudah ada dengan file baru. Untuk menghindari penimpaan file, gunakan fungsi `os.IsNotExist()` untuk mendeteksi apakah file yang ingin dibuat sudah ada atau belum.
-Berikut merupakan contoh pembuatan file.
+Contoh program operasi pembuatan file:
```go
package main
@@ -43,17 +43,17 @@ func main() {
}
```
-Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai parameter fungsi `os.IsNotExist()`, untuk mendeteksi apakah file yang akan dibuat sudah ada. Jika belum ada, maka fungsi tersebut akan mengembalikan nilai `true`.
+Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai argument pemanggilan fungsi `os.IsNotExist()`, untuk mengetahui apakah file yang akan dibuat sudah ada. Jika rupanya file belum ada ada, maka fungsi tersebut akan mengembalikan nilai `true`.
-Fungsi `os.Create()` digunakan untuk membuat file pada path tertentu. Fungsi ini mengembalikan objek `*os.File` dari file yang bersangkutan. File yang baru terbuat statusnya adalah otomatis **open**, maka dari itu perlu untuk di-**close** menggunakan method `file.Close()` setelah file tidak digunakan lagi.
+Fungsi `os.Create()` ini mengembalikan objek bertipe `*os.File`. File yang baru dibuat, statusnya adalah otomatis **open**. Setelah operasi file selesai, file harus di-**close** menggunakan method `file.Close()`.
-Membiarkan file terbuka ketika sudah tak lagi digunakan bukan hal yang baik, karena efeknya ke memory dan akses ke file itu sendiri, file akan di-lock sehingga tidak bisa digunakan oleh proses lain selama status file masih open atau belum di-close.
+Membiarkan file terbuka ketika sudah tak lagi digunakan adalah tidak baik, karena ada efek ke memory dan akses ke file itu sendiri, file menjadi terkunci/locked, membuatnya tidak bisa diakses oleh proses lain selama status file statusnya masih **open** dan belum di-close.
![Membuat file baru](images/A_file_1_create.png)
## A.50.2. Mengedit Isi File
-Untuk mengedit file, yang perlu dilakukan pertama adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk pengisian data. Terakhir panggil method `Sync()` untuk menyimpan perubahan.
+Untuk mengedit file, yang pertama perlu dilakukan adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk penulisan data. Di akhir, panggil method `Sync()` untuk menyimpan perubahan.
```go
func writeFile() {
@@ -80,13 +80,13 @@ func main() {
}
```
-Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file akan disimpan dengan dipanggilnya `Sync()`.
+Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file menjadi tersimpan dengan adanya pemanggilan method `Sync()`.
![Mengedit file](images/A_file_2_write.png)
## A.50.3. Membaca Isi File
-File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Setelah itu, gunakan method `Read()` dengan parameter adalah variabel, yang di mana hasil proses baca akan disimpan ke variabel tersebut.
+File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Dari object file kembalian fungsi tersebut, gunakan method `Read()` dengan disertai argument berupa variabel yang akan menampung data hasil operasi baca.
```go
// tambahkan di bagian import package io
@@ -120,21 +120,21 @@ func main() {
}
```
-Pada kode di atas `os.OpenFile()` digunakan untuk membuka file. Fungsi tersebut memiliki beberapa parameter.
+Fungsi `os.OpenFile()` dalam pemanggilannya memerlukan beberapa argument parameter untuk di-isi:
1. Parameter pertama adalah path file yang akan dibuka.
2. Parameter kedua adalah level akses. `os.O_RDONLY` maksudnya adalah **read only**.
3. Parameter ketiga adalah permission file-nya.
-Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen 1024. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file akan dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
+Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen `1024`. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
-Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error tersebut adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
+Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
![Membaca isi file](images/A_file_3_read.png)
## A.50.4. Menghapus File
-Cara menghapus file sangatlah mudah, cukup panggil fungsi `os.Remove()`, masukan path file yang ingin dihapus sebagai parameter.
+Operasi menghapus file dilakukan via fungsi `os.Remove()`. Panggil fungsi tersebut, kemudian isi path dari file yang ingin dihapus sebagai argument fungsi.
```go
func deleteFile() {
diff --git a/content/A-fungsi-closure.md b/content/A-fungsi-closure.md
index 4c60405ec..b8e6ab54b 100644
--- a/content/A-fungsi-closure.md
+++ b/content/A-fungsi-closure.md
@@ -1,12 +1,12 @@
# A.21. 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.
+Definisi **Closure** adalah suatu *anonymous function* (atau fungsi tanpa nama) yang disimpan dalam variabel. Dengan adanya closure, kita bisa mendesain beberapa hal diantaranya seperti: membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi. Closure biasa dimanfaatkan untuk membungkus suatu proses yang hanya dijalankan sekali saja atau hanya dipakai pada blok tertentu saja.
## A.21.1. 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`.
+Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel closure memiliki sifat seperti fungsi yang disimpannya.
+
+Di bawah ini adalah contoh program sederhana yang menerapkan closure untuk pencarian nilai terendah dan tertinggi dari data array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel `getMinMax`.
```go
package main
@@ -35,8 +35,7 @@ func main() {
}
```
-
-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.
+Bisa dilihat pada kode di atas bagaimana cara deklarasi closure dan cara pemanggilannya. Sedikit berbeda memang dibanding pembuatan fungsi biasa, pada closure fungsi ditulis tanpa memiliki nama lalu ditampung ke variabel.
```go
var getMinMax = func(n []int) (int, int) {
@@ -44,7 +43,7 @@ var getMinMax = func(n []int) (int, int) {
}
```
-Cara pemanggilannya, dengan menuliskan nama variabel tersebut sebagai fungsi, seperti pemanggilan fungsi biasa.
+Cara pemanggilan closure adalah dengan memperlakukan variabel closure seperti fungsi, dituliskan seperti pemanggilan fungsi.
```go
var min, max = getMinMax(numbers)
@@ -54,25 +53,25 @@ Output program:
![Penerapan closure](images/A_fungsi_closure_1_closure.png)
----
+## A.21.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai kode di atas
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.21.1.1. Penggunaan Template String `%v`
+#### ◉ Penggunaan Template String `%v`
-Template `%v` digunakan untuk menampilkan segala jenis data. Bisa array, int, float, bool, dan lainnya.
+Template `%v` digunakan untuk menampilkan data tanpa melihat tipe datanya. Jadi bisa digunakan untuk menampilkan data array, int, float, bool, dan lainnya. Bisa dilihat di contoh statement, data bertipe array dan numerik ditampilkan menggunakan `%v`.
```go
fmt.Printf("data : %v\nmin : %v\nmax : %v\n", numbers, min, max)
```
-Bisa dilihat pada statement di atas, data bertipe array dan numerik ditampilkan menggunakan `%v`. Template ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Sangat tepat jika digunakan pada data bertipe `interface{}` yang nantinya akan di bahas pada chapter [A.27. Interface](/A-interface.html).
+Template `%v` ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Biasa digunakan untuk keperluan debugging, misalnya untuk menampilkan data bertipe `any` atau `interface{}`.
----
+> Pembahasan mengenai tipe data `any` atau `interface{}` ada di chapter [A.27. Interface](/A-interface.html)
-## A.21.2. Immediately-Invoked Function Expression (IIFE)
+#### ◉ 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.
+Closure jenis IIFE ini eksekusinya adalah langsung saat deklarasi. Teknik ini biasa diterapkan untuk membungkus proses yang hanya dilakukan sekali. IIFE bisa memiliki nilai balik atau bisa juga tidak.
Di bawah ini merupakan contoh sederhana penerapan metode IIFE untuk filtering data array.
@@ -104,7 +103,7 @@ Output program:
![Penerapan IIFE](images/A_fungsi_closure_2_iife.png)
-Ciri khas IIFE adalah adanya kurung parameter tepat setelah deklarasi closure berakhir. Jika ada parameter, bisa juga dituliskan dalam kurung parameternya.
+Ciri khas dari penulisan IIFE adalah adanya tanda kurung parameter yang ditulis di akhir deklarasi closure. Jika IIFE memiliki parameter, maka argument-nya juga ditulis. Contoh:
```go
var newNumbers = func(min int) []int {
@@ -112,13 +111,13 @@ var newNumbers = func(min int) []int {
}(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**.
+Di contoh sederhana di atas, IIFE menghasilkan nilai balik yang ditampung variabel `newNumber`. Perlu diperhatikan bahwa yang ditampung adalah **nilai kembaliannya** bukan body fungsi atau **closure**-nya.
> 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 {
// ..
}
## A.21.3. 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.
+Salah satu keunikan lain dari closure adalah: closure bisa dijadikan sebagai nilai balik fungsi. Cukup aneh, tapi pada kondisi tertentu teknik ini sangat berguna. Sebagai contoh, di bawah ini dideklarasikan sebuah fungsi bernama `findMax()` yang salah satu nilai kembaliannya adalah berupa closure.
```go
package main
@@ -148,7 +147,12 @@ return len(res), func() []int {
> Fungsi tanpa nama yang akan dikembalikan boleh disimpan pada variabel terlebih dahulu. Contohnya:var getNumbers = func() []int {
return res
}
return len(res), getNumbers
-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.
+Tentang fungsi `findMax()` sendiri, fungsi ini dibuat untuk mempermudah pencarian angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Fungsi ini mengembalikan dua buah nilai balik:
+
+- Nilai balik pertama adalah jumlah angkanya.
+- Nilai balik kedua berupa closure yang mengembalikan angka-angka yang dicari.
+
+Berikut merupakan contoh implementasi fungsi tersebut:
```go
func main() {
diff --git a/content/A-fungsi-multiple-return.md b/content/A-fungsi-multiple-return.md
index 796983568..2ac5893f0 100644
--- a/content/A-fungsi-multiple-return.md
+++ b/content/A-fungsi-multiple-return.md
@@ -1,12 +1,10 @@
# A.19. 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.
+Di Go, suatu fungsi bisa saja mengembalikan nilai belik lebih dari 1 buah. Teknik ini bisa menjadi alternatif selain menggunakan tipe data kolektif seperti `map`, slice, atau `struct` sebagai nilai balik. Pada chapter ini kita akan belajar penerapannya.
-Go menyediakan kapabilitas bagi programmer untuk membuat fungsi memiliki banyak nilai balik. Pada chapter ini akan dibahas bagaimana penerapannya.
+## A.19.1. Penerapan Fungsi Multiple Return
-## A.19.1 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.
+Cara membuat fungsi agar memiliki banyak nilai balik tidaklah sulit, caranya pada saat deklarasi fungsi, tulis semua tipe data nilai balik yang ingin dikembalikan. Kemudian dalam body fungsi, pada penggunaan keyword `return`, tulis semua data yang ingin dikembalikan. Contoh:
```go
package main
@@ -25,7 +23,7 @@ func calculate(d float64) (float64, float64) {
}
```
-Fungsi `calculate()` di atas menerima satu buah parameter (`diameter`) yang digunakan dalam proses perhitungan. Di dalam fungsi tersebut ada 2 hal yang dihitung, yaitu nilai **luas** dan **keliling**. Kedua nilai tersebut kemudian dijadikan sebagai return value fungsi.
+Fungsi `calculate()` di atas memiliki satu buah parameter yaitu `d` (diameter). Di dalam fungsi terdapat operasi perhitungan nilai **luas** dan **keliling** dari nilai `d`. Kedua hasilnya kemudian dijadikan sebagai return value.
Cara pendefinisian banyak nilai balik bisa dilihat pada kode di atas, langsung tulis tipe data semua nilai balik dipisah tanda koma, lalu ditambahkan kurung di antaranya.
@@ -39,7 +37,7 @@ Tak lupa di bagian penulisan keyword `return` harus dituliskan juga semua data y
return area, circumference
```
-Implementasi dari fungsi `calculate()` di atas, bisa dilihat pada kode berikut.
+Sekarang, coba panggil fungsi `calculate()` yang sudah dibuat untuk mencari nilai luas dan keliling dari suatu diameter.
```go
func main() {
@@ -55,13 +53,13 @@ Output program:
![Penerapan teknik multiple return](images/A_fungsi_multiple_return_1_multiple_return.png)
-Karena fungsi tersebut memiliki banyak nilai balik, maka pada pemanggilannya harus disiapkan juga banyak variabel untuk menampung nilai kembalian yang ada (sesuai jumlah nilai balik fungsi).
+Fungsi `calculate()` memiliki banyak nilai balik, maka dalam pemanggilannya harus disiapkan juga sejumlah variabel untuk menampung nilai balik fungsi (sesuai dengan jumlah nilai balik yang dideklarasikan).
```go
var area, circumference = calculate(diameter)
```
-## A.19.2 Fungsi Dengan Predefined Return Value
+## A.19.2. Fungsi Dengan Predefined Return Value
Keunikan lainnya yang jarang ditemui di bahasa lain adalah, di Go variabel yang digunakan sebagai nilai balik bisa didefinisikan di awal.
@@ -84,15 +82,15 @@ Fungsi dideklarasikan memiliki 2 buah tipe data, dan variabel yang nantinya dija
Karena variabel nilai balik sudah ditentukan di awal, untuk mengembalikan nilai cukup dengan memanggil `return` tanpa perlu diikuti variabel apapun. Nilai terakhir `area` dan `circumference` sebelum pemanggilan keyword `return` adalah hasil dari fungsi di atas.
----
+## A.19.3. Penjelasan tambahan
-Ada beberapa hal baru dari kode di atas yang perlu dibahas, seperti `math.Pow()` dan `math.Pi`. Berikut adalah penjelasannya.
+Ada beberapa hal baru dari kode di atas yang perlu dibahas, diantaranya `math.Pow()` dan `math.Pi`.
-#### • Penggunaan Fungsi `math.Pow()`
+#### ◉ Penggunaan Fungsi `math.Pow()`
-Fungsi `math.Pow()` digunakan untuk memangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
+Fungsi `math.Pow()` digunakan untuk operasi pangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
-#### • Penggunaan Konstanta `math.Pi`
+#### ◉ Penggunaan Konstanta `math.Pi`
`math.Pi` adalah konstanta bawaan `package math` yang merepresentasikan **Pi** atau **22/7**.
diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md
index 8eacd0a12..0e27fde4d 100644
--- a/content/A-fungsi-sebagai-parameter.md
+++ b/content/A-fungsi-sebagai-parameter.md
@@ -1,8 +1,8 @@
# A.22. 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.
+Pada chapter sebelumnya kita telah belajar tentang fungsi yang mengembalikan nilai balik berupa fungsi. Kali ini topiknya tidak kalah unik, yaitu tentang fungsi yang memiliki parameter sebuah fungsi.
-Di Go, fungsi bisa dijadikan sebagai tipe data variabel. Dari situ sangat memungkinkan untuk menjadikannya sebagai parameter juga.
+Di Go, fungsi bisa dijadikan sebagai tipe data variabel, maka sangat memungkinkan untuk menjadikannya sebagai parameter.
## A.22.1. Penerapan Fungsi Sebagai Parameter
@@ -50,7 +50,7 @@ func main() {
}
```
-Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut merupakan penjelasannya.
+Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut adalah 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.
@@ -62,15 +62,17 @@ Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di
Pada `dataContainsO`, parameter kedua fungsi `filter()` berisikan statement untuk deteksi apakah terdapat substring `"o"` di dalam nilai variabel `each` (yang merupakan data tiap elemen), jika iya, maka kondisi filter bernilai `true`, dan sebaliknya.
-pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
+Pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
-Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat membantu proses pembuatan aplikasi.
+Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat berguna.
## A.22.2. Alias Skema Closure
-Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Pada fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
+Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Di fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
-Pada fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama. Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
+Untuk fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias dalam pendefinisiannya, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama, maka kita tidak perlu menuliskan skema panjang fungsi tersebut berulang-ulang.
+
+Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
```go
type FilterCallback func(string) bool
@@ -82,11 +84,11 @@ func filter(data []string, callback FilterCallback) []string {
Skema `func(string) bool` diubah menjadi tipe dengan nama `FilterCallback`. Tipe tersebut kemudian digunakan sebagai tipe data parameter `callback`.
----
+## A.22.3. Penjelasan tambahan
Di bawah ini merupakan penjelasan tambahan mengenai fungsi `strings.Contains()`.
-## A.22.2.1. Penggunaan Fungsi `string.Contains()`
+#### ◉ Penggunaan Fungsi `string.Contains()`
Inti dari fungsi ini adalah untuk deteksi apakah sebuah substring adalah bagian dari string, jika iya maka akan bernilai `true`, dan sebaliknya. Contoh penggunaannya:
diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md
index 75e54f00a..7263e9926 100644
--- a/content/A-fungsi-variadic.md
+++ b/content/A-fungsi-variadic.md
@@ -1,16 +1,16 @@
# A.20. 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.
+Go mengadopsi konsep **variadic function** atau pembuatan fungsi dengan parameter bisa menampung nilai sejenis yang tidak terbatas jumlahnya.
-Parameter variadic memiliki sifat yang mirip dengan 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.
+Parameter variadic memiliki sifat yang mirip dengan slice, yaitu nilai dari parameter-parameter yang disisipkan bertipe data sama, dan kesemuanya cukup ditampung oleh satu variabel saja. Cara pengaksesan tiap nilai juga mirip, yaitu dengan menggunakan index.
Pada chapter ini kita akan belajar mengenai cara penerapan fungsi variadic.
## A.20.1. 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.
+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.
+Contoh program:
```go
package main
@@ -56,19 +56,21 @@ Nilai tiap parameter bisa diakses seperti cara pengaksesan tiap elemen slice. Pa
for _, number := range numbers {
```
----
+## A.20.2. Penjelasan tambahan
-Berikut merupakan penjelasan tambahan dari kode yang telah kita tulis.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `fmt.Sprintf()`
+#### ◉ Penggunaan Fungsi `fmt.Sprintf()`
-Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada kasus di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
+Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada case di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
Selain `fmt.Sprintf()`, ada juga `fmt.Sprint()` dan `fmt.Sprintln()`.
-#### • Penggunaan Fungsi `float64()`
+#### ◉ Penggunaan Fungsi `float64()`
+
+Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) menandakan bahwa digunakan untuk keperluan **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-casting.
-Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) berguna untuk **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-cast. Dan cara penerapannya juga sama, cukup panggil sebagai fungsi, lalu masukan data yang ingin dikonversi sebagai parameter.
+Cara penerapan casting: panggil saja tipe data yang diingunkan seperti pemanggilan fungsi, lalu masukan data yang ingin dikonversi sebagai argument pemanggilan fungsi tersebut.
Pada contoh di atas, variabel `total` yang tipenya adalah `int`, dikonversi menjadi `float64`, begitu juga `len(numbers)` yang menghasilkan `int` dikonversi ke `float64`.
@@ -76,11 +78,9 @@ Variabel `avg` perlu dijadikan `float64` karena penghitungan rata-rata lebih ser
Operasi bilangan (perkalian, pembagian, dan lainnya) di Go hanya bisa dilakukan jika tipe datanya sejenis. Maka dari itulah perlu adanya casting ke tipe `float64` pada tiap operand.
----
-
-## A.20.2. Pengisian Parameter Fungsi Variadic Menggunakan Data Slice
+## A.20.3. 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.
+Slice bisa digunakan sebagai argument pada fungsi variadic. Caranya penerapannya: tulis saja nama variabel tapi disertai dengan tanda titik tiga kali, dituliskan tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut:
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -90,9 +90,9 @@ var msg = fmt.Sprintf("Rata-rata : %.2f", avg)
fmt.Println(msg)
```
-Pada kode di atas, variabel `numbers` yang merupakan slice int, disisipkan ke fungsi `calculate()` sebagai parameter variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna ketika sebuah data slice ingin difungsikan sebagai parameter variadic.
+Pada kode di atas, variabel `numbers` bertipe data slice int, disisipkan pada pemanggilan fungsi `calculate()` sebagai argument parameter fungsi variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna pada case dimana sebuah data slice perlu untuk digunakan sebagai argument parameter variadic.
-Perhatikan juga kode berikut ini. Intinya adalah sama, hanya caranya yang berbeda.
+Agar lebih jelas, perhatikan 2 kode berikut. Intinya sama, hanya cara penulisannya yang berbeda.
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -105,7 +105,7 @@ var avg = calculate(2, 4, 3, 5, 4, 3, 3, 5, 5, 3)
Pada deklarasi parameter fungsi variadic, tanda 3 titik (`...`) dituliskan sebelum tipe data parameter. Sedangkan pada pemanggilan fungsi dengan menyisipkan parameter array, tanda tersebut dituliskan di belakang variabelnya.
-## A.20.3. Fungsi Dengan Parameter Biasa & Variadic
+## A.20.4. 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.
@@ -123,7 +123,7 @@ func yourHobbies(name string, hobbies ...string) {
Nilai parameter pertama fungsi `yourHobbies()` akan ditampung oleh `name`, sedangkan nilai parameter kedua dan seterusnya akan ditampung oleh `hobbies` sebagai slice.
-Cara pemanggilannya masih sama seperi pada fungsi biasa.
+Cara pemanggilannya masih sama seperi pada fungsi biasa, contoh:
```go
func main() {
@@ -131,7 +131,7 @@ func main() {
}
```
-Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali.
+Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali seperti ini:
```go
func main() {
diff --git a/content/A-goroutine.md b/content/A-goroutine.md
index 653650da0..78e919798 100644
--- a/content/A-goroutine.md
+++ b/content/A-goroutine.md
@@ -51,17 +51,17 @@ Bisa dilihat di output, tulisan `"halo"` dan `"apa kabar"` bermunculan selang-se
Pada gambar di atas, program dieksekusi 2 kali. Hasil eksekusi pertama berbeda dengan kedua, penyebabnya adalah karena kita menggunakan 2 prosesor. Goroutine mana yang dieksekusi terlebih dahulu tergantung kedua prosesor tersebut.
----
+## A.30.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan tentang beberapa fungsi yang baru kita pelajari di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.30.1.1. Penggunaan Fungsi `runtime.GOMAXPROCS()`
+#### ◉ Penggunaan Fungsi `runtime.GOMAXPROCS()`
Fungsi ini digunakan untuk menentukan jumlah core atau processor yang digunakan dalam eksekusi program.
Jumlah yang diinputkan secara otomatis akan disesuaikan dengan jumlah asli *logical processor* yang ada. Jika jumlahnya lebih, maka dianggap menggunakan sejumlah prosesor yang ada.
-## A.30.1.2. Penggunaan Fungsi `fmt.Scanln()`
+#### ◉ Penggunaan Fungsi `fmt.Scanln()`
Fungsi ini akan meng-capture semua karakter sebelum user menekan tombol enter, lalu menyimpannya pada variabel.
diff --git a/content/A-json.md b/content/A-json.md
index 72ce09b14..bcf51a652 100644
--- a/content/A-json.md
+++ b/content/A-json.md
@@ -125,7 +125,7 @@ var jsonString = string(jsonData)
fmt.Println(jsonString)
```
-Output:
+Output program:
![Encode data ke JSON](images/A_json_2_encode.png)
diff --git a/content/A-konstanta.md b/content/A-konstanta.md
index 52c3a60e5..fdd7756fe 100644
--- a/content/A-konstanta.md
+++ b/content/A-konstanta.md
@@ -20,7 +20,7 @@ const lastName = "wick"
fmt.Print("nice to meet you ", lastName, "!\n")
```
-#### • Penggunaan Fungsi `fmt.Print()`
+#### ◉ Penggunaan Fungsi `fmt.Print()`
Fungsi ini memiliki peran yang sama seperti fungsi `fmt.Println()`, pembedanya fungsi `fmt.Print()` tidak menghasilkan baris baru di akhir outputnya.
diff --git a/content/A-method.md b/content/A-method.md
index 491676ade..e449763ca 100644
--- a/content/A-method.md
+++ b/content/A-method.md
@@ -44,7 +44,7 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method](images/A_method_1_method.png)
@@ -106,7 +106,7 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method pointer](images/A_method_2_method_pointer.png)
@@ -124,11 +124,11 @@ var s2 = &student{"ethan hunt", 22}
s2.sayHello()
```
----
+## A.25.3. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai beberapa hal pada chapter ini.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `strings.Split()`
+#### ◉ Penggunaan Fungsi `strings.Split()`
Pada chapter ini ada fungsi baru yang kita gunakan: `strings.Split()`. Fungsi ini berguna untuk memisah string menggunakan pemisah yang ditentukan sendiri. Hasilnya adalah array berisikan kumpulan substring.
diff --git a/content/A-pipeline-context-cancellation.md b/content/A-pipeline-context-cancellation.md
index 442227033..e904ca11e 100644
--- a/content/A-pipeline-context-cancellation.md
+++ b/content/A-pipeline-context-cancellation.md
@@ -19,7 +19,7 @@ Jadi kurang lebih akan ada dua result:
Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program versi *concurrent* tanpa *cancellation*. Bisa langsung copy-paste, atau tulis dari awal dengan mengikut tutorial ini secara keseluruhan. Untuk penjelasan detail program versi sekuensial silakan merujuk ke chapter sebelumnya saja, di sini kita tulis langsung agar bisa cepat dimulai bagian program konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.61-pipeline-cancellation-context")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
```go
type FileInfo struct {
@@ -51,7 +51,7 @@ type FileInfo struct {
}
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -65,7 +65,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -82,7 +82,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -111,7 +111,7 @@ func generateFiles() {
log.Printf("%d/%d of total files created", counterSuccess, counterTotal)
}
```
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
```go
func generateFileIndexes() <-chan FileInfo {
@@ -131,7 +131,7 @@ func generateFileIndexes() <-chan FileInfo {
}
```
-#### • Fungsi `createFiles()`
+#### ◉ Fungsi `createFiles()`
```go
func createFiles(chanIn <-chan FileInfo, numberOfWorkers int) <-chan FileInfo {
@@ -179,7 +179,7 @@ Hasil eksekusi program:
Ok, sekarang kita akan refactor kode tersebut, kita tambahkan mekanisme *cancellation* menggunakan `context.Context` API. Silakan duplikasi file program, lalu ikuti petunjuk berikut.
-#### • Import package `context`
+#### ◉ Import package `context`
Tambahkan package `context` dalam block import packages.
@@ -191,7 +191,7 @@ import (
)
```
-#### • Tambahkan definisi konstanta timeout
+#### ◉ Tambahkan definisi konstanta timeout
Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angka timeout untuk keperluan testing.
@@ -199,7 +199,7 @@ Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angk
const timeoutDuration = 3 * time.Second
```
-#### • Penerapan context di fungsi `main()`
+#### ◉ Penerapan context di fungsi `main()`
Pada fungsi main, lakukan sedikit perubahan. Yang sebelumnya ada statement berikut:
@@ -253,7 +253,7 @@ Jadi pada contoh yang kita tulis di atas, kurang lebih yang akan dilakukan adala
* Fungsi `generateFilesWithContext()` dipanggil dengan disisipkan object context.
* Callback `context.CancelFunc` dipanggil secara deferred. Ini merupakan idiomatic Go dalam penerapan context. Meskipun context sudah punya timeout atau deadline dan kita tidak perlu meng-*cancel* context secara manual, sangat dianjurkan untuk tetap memanggil callback `cancel()` tersebut secara deferred.
-#### • Modifikasi fungsi `generateFiles()`
+#### ◉ Modifikasi fungsi `generateFiles()`
Isi dari fungsi `generateFiles()` kita ubah menjadi pemanggilan fungsi `generateFilesWithContext()` dengan parameter context kosong.
@@ -319,7 +319,7 @@ Nah jadi lewat seleksi kondisi 2 case di atas, kita bisa dengan mudah mengidenti
Selain beberapa hal yang sudah saya sampaikan, ada *minor changes* lainnya, yaitu pada pemanggilan fungsi `generateFileIndexes()` dan `createFiles()` ditambahkan argument context.
-#### • Penambahan context pada fungsi `generateFiles()`
+#### ◉ Penambahan context pada fungsi `generateFiles()`
Kenapa ini perlu? karena **meski eksekusi fungsi `generateFilesWithContext()` otomatis di stop ketika cancelled, proses di dalamnya akan tetap berjalan jika tidak di-*handle* dengan baik *cancellation*-nya.**
@@ -355,7 +355,7 @@ Dibanding sebelumnya, perbedaannya adalah ada *channel selection*. Jadi di bagia
* Jika ada notif cancel paksa, maka case pertama akan terpenuhi, dan perulangan di-`break`.
* Selebihnya, pengiriman jobs akan berlangsung seperti normalnya.
-#### • Penambahan context pada fungsi `createFiles()`
+#### ◉ Penambahan context pada fungsi `createFiles()`
Hal yang sama (cancel di level sub prosees) juga perlu diterapkan pada `createFiles()`, karena jika tidak, maka proses pembuatan file akan tetap berjalan sesuai dengan jumlah jobs yang dikirim meskipun sudah di-cancel secara paksa.
diff --git a/content/A-properti-public-dan-private.md b/content/A-properti-public-dan-private.md
index 20c6a62f7..87d28e37c 100644
--- a/content/A-properti-public-dan-private.md
+++ b/content/A-properti-public-dan-private.md
@@ -176,7 +176,7 @@ fmt.Println("name ", s1.Name)
fmt.Println("grade", s1.grade)
```
-Output:
+Output program:
![Error lain muncul saat menjalankan program](images/A_properti_public_private_4_error.png)
diff --git a/content/A-simplified-fan-in-fan-out-pipeline.md b/content/A-simplified-fan-in-fan-out-pipeline.md
index c01ada997..63da60969 100644
--- a/content/A-simplified-fan-in-fan-out-pipeline.md
+++ b/content/A-simplified-fan-in-fan-out-pipeline.md
@@ -20,7 +20,7 @@ Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program
Siapkan folder project baru, isinya satu buah file `1-generate-dummy-files-sequentially.go`.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -54,7 +54,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -71,7 +71,7 @@ func randomString(length int) string {
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -103,7 +103,7 @@ Kita lanjut dulu saja. Berikut adalah output jika program di atas di-run.
Sekarang saya buat file program `2-generate-dummy-files-concurrently.go` yang isinya adalah sama yaitu untuk keperluan generate dummy files, tapi pembuatannya dilakukan secara konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
Import beberapa hal pada file baru ini, lalu definisikan beberapa variabel juga.
@@ -126,7 +126,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
Kita perlu siapkan struct baru bernama `FileInfo`, struct ini digunakan sebagai skema payload data ketika dikirimkan via channel dari goroutine jobs ke goroutine worker.
@@ -143,7 +143,7 @@ type FileInfo struct {
* Property `WorkerIndex` digunakan sebagai penanda worker mana yang akan melakukan operasi pembuatan file tersebut.
* Property `Err` default isinya kosong. Nantinya akan diisi dengan objek error ketika ada error saat pembuatan file.
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -157,7 +157,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -173,7 +173,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -215,7 +215,7 @@ Fungsi `createFiles()` di sini merupakan fungsi **Fan-out Fan-in** karena meneri
Fungsi `createFiles()` menghasilkan channel yang isinya merupakan result dari operasi tiap-tiap jobs. Dari data yang dilewatkan via channel tersebut akan ketahuan misal ada error atau tidak saat pembuatan files. Channel tersebut kemudian di-loop lalu ditampilkan tiap-tiap result-nya.
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
Fungsi ini merupakan fungsi Fan-out distribusi jobs. Di dalamnya dilakukan perulangan sejumlah `totalFile`, kemudian data tiap index digunakan untuk pembentukan filename lalu dikirim ke channel outputnya.
@@ -239,7 +239,7 @@ func generateFileIndexes() <-chan FileInfo {
Setelah dipastikan semua job terkirim, kita close channel output `chanOut` tersebut.
-#### • Fungsi `dispatchWorkers()`
+#### ◉ Fungsi `dispatchWorkers()`
Bagian ini merupakan yang paling butuh *effort* untuk dipahami. Jadi fungsi `createFiles()` seperti yang sudah saja jelaskan secara singkat di atas, fungsi ini merupakan fungsi gabungan Fan-out (menerima channel output dari pipeline sebelumnya) dan juga Fan-in (menjalankan beberapa worker untuk memproses channel output dari pipeline sebelumnya, lalu output masing-masing worker yang juga merupakan channel - langsung di merge jadi satu channel saja).
@@ -315,13 +315,13 @@ Semoga cukup jelas ya. Kelebihan metode ini ini salah satunya adalah kita bisa d
Saya akan coba jalankan program pertama dan kedua, lalu mari kita lihat perbedaannya.
-#### • Program Generate Dummy File *Sequentially*
+#### ◉ Program Generate Dummy File *Sequentially*
![Generate dummy files sequentially](images/A_simplified_fan_in_fan_out_pipeline_2_benchmark.png)
Testing di awal chapter ini hasilnya butuh sekitar **19 detik** untuk menyelesaikan generate dummy files sebanyak 3000 secara sekuensial. Tapi kali ini lebih lambat, yaitu **23 detik** dan ini wajar, karena di tiap operasi kita munculkan log ke stdout (via `log.Println()`).
-#### • Program Generate Dummy File *Concurrently*
+#### ◉ Program Generate Dummy File *Concurrently*
![Generate dummy files concurrently](images/A_simplified_fan_in_fan_out_pipeline_3_concurrent.png)
diff --git a/content/A-sql.md b/content/A-sql.md
index dd3f0b1fd..a87811434 100644
--- a/content/A-sql.md
+++ b/content/A-sql.md
@@ -174,7 +174,7 @@ func main() {
}
```
-Output:
+Output program:
![Membaca data dari database server](images/A_sql_2_sql_query.png)
diff --git a/content/A-variabel.md b/content/A-variabel.md
index 39e017488..ff265bcc7 100644
--- a/content/A-variabel.md
+++ b/content/A-variabel.md
@@ -51,7 +51,7 @@ var firstName string = "john"
Nilai variabel bisa di-isi langsung pada saat deklarasi variabel.
-#### • Penggunaan Fungsi `fmt.Printf()`
+#### ◉ Penggunaan Fungsi `fmt.Printf()`
Fungsi ini digunakan untuk menampilkan output dalam bentuk tertentu. Kegunaannya sama seperti fungsi `fmt.Println()`, hanya saja struktur outputnya didefinisikan di awal.
diff --git a/content/B-golang-web-hello-world.md b/content/B-golang-web-hello-world.md
index 9f0b213df..6acbd481a 100644
--- a/content/B-golang-web-hello-world.md
+++ b/content/B-golang-web-hello-world.md
@@ -69,7 +69,7 @@ Cek pada browser rute yang sudah dibuat, output akan muncul.
Berikut merupakan penjelasan detail per-bagian program yang telah kita buat dari contoh di atas.
-#### • Penggunaan `http.HandleFunc()`
+#### ◉ Penggunaan `http.HandleFunc()`
Fungsi ini digunakan untuk **routing**, menentukan aksi dari sebuah url tertentu ketika diakses (di sini url tersebut kita sebut sebagai rute/route). Rute dituliskan dalam `string` sebagai parameter pertama, dan aksi-nya sendiri dibungkus dalam fungsi (bisa berupa closure) yang ditempatkan pada parameter kedua (kita sebut sebagai handler).
@@ -83,7 +83,7 @@ Ketika rute-rute tersebut diakses lewat browser, outpunya adalah isi-handler dar
> Pada contoh di atas, ketika rute yang tidak terdaftar diakses, secara otomatis handler rute `/` akan terpanggil.
-#### • Penjelasan Mengenai **Handler**
+#### ◉ Penjelasan Mengenai **Handler**
Route handler atau handler atau parameter kedua fungsi `http.HandleFunc()`, adalah sebuah fungsi dengan ber-skema `func (ResponseWriter, *Request)`.
@@ -105,7 +105,7 @@ Pada contoh program yang telah kita buat, handler `Index()` memunculkan text `"W
Sebuah handler bisa dipergunakan pada banyak rute, bisa dilihat pada di atas handler `Index()` digunakan pada rute `/` dan `/index`.
-#### • Penggunaan `http.ListenAndServe()`
+#### ◉ Penggunaan `http.ListenAndServe()`
Fungsi ini digunakan untuk membuat web server baru. Pada contoh yang telah dibuat, web server di-*start* pada port `9000` (bisa dituliskan dalam bentuk `localhost:9000` atau cukup `:9000` saja).
diff --git a/content/B-http-basic-auth.md b/content/B-http-basic-auth.md
index 91d0f47ab..5350c63e3 100644
--- a/content/B-http-basic-auth.md
+++ b/content/B-http-basic-auth.md
@@ -149,7 +149,7 @@ func init() {
Selanjutnya, kita perlu menyiapkan beberapa fungsi yg digunakan pada `main.go`, yaitu `Auth()` dan `AllowOnlyGET()`.
-#### • Fungsi `Auth()`
+#### ◉ Fungsi `Auth()`
Buka `middleware.go`, siapkan fungsi `Auth()`.
@@ -188,7 +188,7 @@ Fungsi `r.BasicAuth()` mengembalikan 3 informasi:
Jika basic auth request tidak valid, maka tampilkan pesan error sebagai nilai balik. Sedangkan jika basic auth adalah valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang ada di aplikasi kita.
-#### • Fungsi `AllowOnlyGET()`
+#### ◉ Fungsi `AllowOnlyGET()`
Fungsi ini bertugas untuk memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka akan dianggap invalid request.
diff --git a/content/B-simple-configuration.md b/content/B-simple-configuration.md
index 8516fea7e..223e19d31 100644
--- a/content/B-simple-configuration.md
+++ b/content/B-simple-configuration.md
@@ -202,11 +202,11 @@ Coba ubah konfigurasi pada `config.json` nilai `log.verbose` menjadi `false`. La
Ok, kita telah selesai belajar tentang cara membuat file konfigurasi yang mudah dibaca dan praktis. Namun penerapan kontrol konfigurasi dengan metode ini kurang dianjurkan karena beberapa hal:
-#### • Tidak mendukung komentar
+#### ◉ Tidak mendukung komentar
Komentar sangat penting karena untuk aplikasi besar yang konfigurasi item-nya sangat banyak - akan susah untuk dipahami. Sebenarnya perihal ini bisa di-*resolve* menggunakan jenis konfigurasi lain seperti `YAML`, `.env`, atau lainnya.
-#### • Nilai konfigurasi harus diketahui di awal
+#### ◉ Nilai konfigurasi harus diketahui di awal
Kita harus tau semua value tiap-tiap konfigurasi terlebih dahulu, dan dituliskan ke file, sebelum aplikasi di-up. Dari sini akan sangat susah jika misal ada beberapa konfigurasi yang kita tidak tau nilainya tapi tau cara pengambilannya.
@@ -214,19 +214,19 @@ Contohnya pada beberapa kasus, seperti di AWS, database server yang di-setup sec
Dengan ini akan sangat susah jika kita harus cari terlebih dahulu value konfigurasi tersebut untuk kemudian dituliskan ke file. Memakan waktu dan kurang baik dari banyak sisi.
-#### • Tidak terpusat
+#### ◉ Tidak terpusat
Dalam pengembangan aplikasi, banyak konfigurasi yang nilai-nya akan didapat lewat jalan lain, seperti *environment variables* atau *command arguments*.
Akan lebih mudah jika hanya ada satu sumber konfigurasi saja untuk dijadikan acuan.
-#### • Statis (tidak dinamis)
+#### ◉ Statis (tidak dinamis)
Konfigurasi umumnya dibaca hanya jika diperlukan. Penulisan konfigurasi dalam file membuat proses pembacaan file harus dilakukan di awal, haru kemudian kita bisa ambil nilai konfigurasi dari data yang sudah ada di memori.
Hal tersebut memiliki beberapa konsekuensi, untuk aplikasi yang di-manage secara automated, sangat mungkin adanya perubahan nilai konfigurasi. Dari sini berarti pembacaan konfigurasi file tidak boleh hanya dilakukan di awal saja. Tapi juga tidak boleh dilakukan di setiap waktu, karena membaca file itu ada *cost*-nya dari prespektif I/O.
-#### • Solusi
+#### ◉ Solusi
Kita akan membahas solusi dari beberapa masalah di atas pada chapter terpisah, yaitu [Best Practice Configuration Menggunakan Environment Variable
](/C-best-practice-configuration-env-var)
diff --git a/content/B-template-functions.md b/content/B-template-functions.md
index b80d8e9ec..86b59b70d 100644
--- a/content/B-template-functions.md
+++ b/content/B-template-functions.md
@@ -168,7 +168,7 @@ Cara penggunannya juga masih sama.