17. Logika boolean secara mendalam

Bab 16 memakai &&, ||, dan ! untuk menggabungkan kondisi. Bab ini melihat lebih dekat apa yang dilakukan operator-operator itu, dan aturan Go yang paling ketat tentang kondisi.

Hanya bool yang boleh masuk ke if

Kondisi harus berupa bool: true atau false, atau sesuatu yang memberikan salah satunya. Selain itu tidak boleh. String, angka, atau nilai kosong tidak dianggap benar atau salah di Go.

Buka exercises/17/01-not-a-bool.go. File ini punya dua pernyataan if, satu dengan string dan satu dengan angka 1. File ini tidak bisa dikompilasi:

# command-line-arguments
exercises\17\01-not-a-bool.go:13:5: non-boolean condition in if statement
exercises\17\01-not-a-bool.go:17:5: non-boolean condition in if statement

Non-boolean berarti "bukan bool". Kolom 5 adalah tempat kondisi dimulai. Ganti kondisinya dengan perbandingan sungguhan, dengan contoh berikutnya sebagai panduan.

Jadi tulis pertanyaannya secara lengkap. Untuk menanyakan apakah string berisi sesuatu, atau apakah angka bukan nol, dibutuhkan perbandingan:

name := "Keiko"
count := 3

if name != "" {
    fmt.Println("Hello, " + name)
}
if count != 0 {
    fmt.Println("The count is not zero.")
}

Output:

Hello, Keiko
The count is not zero.

Ini butuh beberapa karakter lebih banyak, dan sebagai gantinya kondisi menyatakan dengan tepat apa yang diuji. name != "" tidak mungkin salah dibaca. Compiler juga menangkap kesalahannya untukmu, sebelum program berjalan:

level := 1
if level {
    fmt.Println("Has a level.")
}
non-boolean condition in if statement

Perbandingan, sekali lagi

Setiap perbandingan dari Bab 16 memberikan bool. String juga bisa dibandingkan. < dan > membandingkannya menurut urutan kamus, dengan satu catatan: huruf besar berada sebelum huruf kecil.

fmt.Println("apple" == "apple")  // true
fmt.Println("apple" != "banana") // true
fmt.Println("apple" < "banana")  // true
fmt.Println("Zebra" < "apple")   // true

Kedua sisi perbandingan harus punya tipe yang sama. Go tidak mengubah angka menjadi teks untukmu, atau teks menjadi angka:

score := 73
if score == "73" {
    fmt.Println("Seventy-three.")
}
invalid operation: score == "73" (mismatched types int and untyped string)

Konversi dulu dengan strconv dari Bab 14, atau bandingkan dengan angka 73.

&& dan || memberikan bool

&& dan || menerima dua bool dan memberikan satu bool. ! menerima satu bool dan memberikan satu bool. Lengkapnya:

a b a && b a || b !a
true true true true false
true false false true false
false true false true true
false false false false true

Output exercises/17/02-bools.go mencoba beberapa di antaranya:

false
false
true
true
false
true
Can enter: true

Karena jawabannya bool, kamu bisa menyimpannya dalam variabel dengan nama yang baik, lalu memakai variabel itu sebagai kondisi. Variabel bool boleh berdiri sendiri di dalam if, tanpa perbandingan:

level := 12
hasKey := true
canEnter := level >= 10 && hasKey

if canEnter {
    fmt.Println("You can enter the dungeon.")
}
fmt.Println("Can enter:", canEnter)

Output:

You can enter the dungeon.
Can enter: true

Ketika && dan || dicampur, && didahulukan, seperti * didahulukan dari +. Pakai tanda kurung kalau kamu ingin urutan lain, atau kalau tanda kurung membuat barisnya lebih jelas:

fmt.Println(true || false && false)   // true
fmt.Println((true || false) && false) // false

Nilai default

Pekerjaan yang umum: pakai sebuah nilai kalau ada, dan kalau tidak ada, pakai nilai cadangan. || tidak bisa melakukan ini, karena hanya bekerja pada bool:

chosen := ""
move := chosen || "rock"
fmt.Println(move)
invalid operation: operator || not defined on chosen (variable of type string)

Bab 11 menunjukkan bahwa variabel kosong sebenarnya tidak kosong. Ia berisi nilai nol dari tipenya, dan untuk string nilai itu adalah "". Jadi "tidak ada yang dimasukkan" berarti stringnya sama dengan "", dan sebuah if memberikan nilai cadangannya:

typedName := "" // tidak ada yang dimasukkan

name := typedName
if name == "" {
    name = "stranger"
}
fmt.Println("Hello, " + name) // Hello, stranger

Kalau typedName berisi nilai sungguhan, name akan menyimpannya. Mulai dari nilainya, lalu ganti hanya kalau nilainya tidak ada. Panjangnya tiga baris, dan kamu akan sering melihatnya.

Ini bekerja kalau "" tidak mungkin menjadi jawaban sungguhan. Angka lebih sulit, karena 0 bisa berupa nilai sungguhan atau bisa juga hasil strconv.Atoi untuk teks yang salah. Bab 21 menunjukkan cara membedakannya.

Buka exercises/17/03-default.go. chosen diisi "", jadi move memakai nilai cadangan "rock". Isi chosen dengan kata sungguhan lalu jalankan lagi.

Short-circuit: sisi kedua dilewati

&& dan || berhenti begitu jawabannya pasti:

  • a && b: kalau a salah, jawabannya salah, dan b tidak pernah dihitung.
  • a || b: kalau a benar, jawabannya benar, dan b tidak pernah dihitung.

Ini disebut short-circuit evaluation. Dengan ini, pemeriksaan pertama bisa menjaga pemeriksaan kedua, yang hanya berjalan setelah yang pertama lolos:

total := 90
count := 0

// Rata-rata hanya perlu dihitung kalau count bukan nol,
// jadi pemeriksaan count menjaga pembagian.
if count > 0 && total/count > 20 {
    fmt.Println("high average")
} else {
    fmt.Println("no average to show")
}

Output:

no average to show

Karena count > 0 salah, Go tidak pernah menghitung total/count. Go langsung menuju else. Penjaga ini dibutuhkan, karena membagi bilangan bulat dengan nol menghentikan program. Tukar kedua sisinya:

total := 90
count := 0

if total/count > 20 && count > 0 {
    fmt.Println("high average")
}

Sekarang pembagian berjalan lebih dulu, dan program crash dengan:

panic: runtime error: integer divide by zero

Ini jenis crash yang sama dengan error slice di Bab 12: program berhasil dikompilasi, lalu berhenti saat berjalan.

|| menjaga dengan cara yang sama. Di sini pembagian hanya dicapai kalau count bukan nol:

total := 90
count := 0

if count == 0 || total/count < 20 {
    fmt.Println("nothing special")
}

Output:

nothing special

Buka exercises/17/04-short-circuit.go. Jalankan, lalu tukar kedua sisi && dan jalankan lagi untuk melihat crash-nya. Terakhir, kembalikan urutannya dan isi count dengan 3.

! membalik bool

! mengubah true menjadi false dan false menjadi true. Ini berguna untuk bertanya "apakah ini tidak ada?" kalau kamu punya bool:

fmt.Println(!true)  // false
fmt.Println(!false) // true

hasKey := false
if !hasKey {
    fmt.Println("You need a key.")
}

Output:

false
true
You need a key.

Seperti ||, ia hanya bekerja pada bool. Untuk menanyakan hal yang sama tentang string, bandingkan dengan "":

key := ""
if !key {
    fmt.Println("You need a key.")
}
invalid operation: operator ! not defined on key (variable of type string)
key := ""
if key == "" {
    fmt.Println("You need a key.")
}

Output:

You need a key.

PR

Starter PR ada di exercises/17/homework/. Setiap starter bisa langsung dijalankan dan mencetak satu baris pengganti.

Soal 1 — Warna default

Buka exercises/17/homework/01-default-colour.go. fav berisi sebuah warna atau "". Isi colour dengan fav, atau dengan "blue" kalau fav kosong, lalu cetak Your colour is <colour>. Jalankan sekali dengan fav := "" dan sekali dengan warna sungguhan.

Soal 2 — Tulis maksudmu

Buka exercises/17/homework/02-explicit-tests.go. Lima variabel berisi 0, "", false, "hi", dan 1. Untuk masing-masing, cetak nilainya dan hasil pemeriksaan yang menanyakan apakah nilainya berbeda dari nilai nol tipenya, seperti count != 0. Pakai %q untuk string supaya string kosong terlihat. Mana dari kelima variabel itu yang tidak butuh perbandingan?

Soal 3 — Pembagian yang dijaga

Buka exercises/17/homework/03-guarded.go. Ada dua angka: total dan count (yang mungkin 0). Dengan &&, kalau count lebih dari 0 dan total/count di atas 20, cetak rata-ratanya dengan dua angka desimal, sebagai float64; kalau tidak, cetak no high average. Uji dengan count bernilai 0, 3, dan 10.

Tantangan — Nilai pertama yang ada

Buka exercises/17/homework/04-first-value.go. Tiga variabel a, b, c masing-masing berisi string atau "". Dengan switch tanpa nilai, cetak yang pertama tidak kosong, atau none kalau ketiganya kosong. Coba berbagai kombinasi.

Stuck atau sudah selesai? Buka halaman solusi PR.