20. Pola perulangan

Hampir setiap perulangan adalah salah satu dari beberapa bentuk. Beri nama bentuk-bentuk itu dan kamu berhenti menciptakan setiap perulangan dari nol, lalu mengambil yang cocok. Bab ini membahas enam, masing-masing melakukan satu pekerjaan kecil. Akumulasi, hitung, cari, dan tanda memakai rentang angka di sini, dan bentuknya bekerja pada apa pun yang bisa kamu telusuri. Validasi input dan sentinel membaca dari keyboard.

1. Akumulasi: membangun hasil

Pegang sebuah variabel dan ubah setiap putaran. Total berjalan adalah contoh klasiknya:

sum := 0
for i := 1; i <= 10; i++ {
    sum += i
}
fmt.Println(sum) // 55

Bentuknya selalu: atur nilai awal sebelum perulangan, ubah di dalam, pakai sesudahnya. Bentuk ini juga membangun string (mulai dari "", sambung setiap potongan dengan +=) dan mencari nilai terbesar (mulai dari nilai rendah, naikkan setiap kali melihat yang lebih besar).

2. Hitung: berapa yang cocok?

Penghitung adalah akumulator yang naik satu, tetapi hanya kalau sebuah pemeriksaan lolos. "Berapa angka dari 1 sampai 30 yang habis dibagi 3?"

hits := 0
for i := 1; i <= 30; i++ {
    if i%3 == 0 {
        hits++
    }
}
fmt.Println(hits) // 10

if di dalam perulangan adalah pemeriksaannya; hits++ adalah penghitungannya.

3. Cari: temukan yang pertama cocok lalu berhenti

Kalau kamu hanya butuh nilai pertama yang cocok, break begitu menemukannya. Tidak ada gunanya melanjutkan:

first := -1
for i := 1; i <= 100; i++ {
    if i*i > 50 {
        first = i
        break
    }
}
fmt.Println(first) // 8

Jawabannya 8, karena 8 × 8 = 64 adalah kuadrat pertama di atas 50.

first butuh nilai awal, dan int di Go selalu berisi angka. Jadi pilih angka yang tidak mungkin menjadi jawaban sungguhan. -1 bisa dipakai di sini karena pencarian mulai dari 1, dan itu jawaban "tidak ditemukan" yang sama dengan yang diberikan strings.Index di Bab 14. Kalau tidak ada yang cocok, first tetap -1, dan kamu bisa mengujinya dengan if first == -1.

4. Tanda: apakah ada putaran yang cocok?

Terkadang kamu ingin jawaban ya atau tidak, bukan nilainya: "apakah ada angka dari 1 sampai 20 yang habis dibagi 7?" Simpan sebuah bool yang mulai dari false dan berubah menjadi true begitu kamu melihat yang cocok:

found := false
for i := 1; i <= 20; i++ {
    if i%7 == 0 {
        found = true
    }
}
fmt.Println(found) // true

Program mencetak true karena 7 dan 14 ada dalam rentang. Kalau kamu hanya peduli apakah ada, break saat tandanya berubah, dan pola ini menjadi pencarian. Tanda dan cari adalah pola yang bersaudara.

Buka exercises/20/01-patterns.go. File ini mengakumulasi jumlah 1 sampai 20. Tambahkan pola hitung di bawahnya untuk mencetak berapa angka genap dalam rentang itu.

5. Validasi input: bertanya sampai jawabannya baik

Bab 18 menunjukkan bahwa for kosong dengan break berjalan setidaknya sekali. Itu cocok untuk pertanyaan: tanya, periksa, dan tanya lagi kalau jawabannya tidak baik. Ini adalah exercises/20/02-validate.go, di dalam main:

reader := bufio.NewReader(os.Stdin)

var level int
for {
    fmt.Print("Level (1 to 99): ")
    text, _ := reader.ReadString('\n')
    level, _ = strconv.Atoi(strings.TrimSpace(text))
    if level >= 1 && level <= 99 {
        break
    }
    fmt.Println("That is not a level from 1 to 99.")
}

fmt.Println("Level set to", level)

Contoh sesi:

Level (1 to 99): abc
That is not a level from 1 to 99.
Level (1 to 99): 150
That is not a level from 1 to 99.
Level (1 to 99): 12
Level set to 12

Ada tiga hal di sini:

  • level dideklarasikan sebelum perulangan, supaya masih ada setelah perulangan. Di dalamnya, level, _ = memakai = biasa untuk mengubahnya.
  • text, _ := boleh di dalam perulangan, walaupun berjalan berkali-kali. Setiap putaran adalah jalannya blok yang baru, jadi setiap putaran mendeklarasikan text yang baru.
  • abc tertangkap, tetapi bukan karena Go melaporkannya. strconv.Atoi memberikan 0, seperti peringatan Bab 15, dan 0 berada di luar rentang. Jadi rentanglah yang menangkap teks yang salah. Kalau 0 adalah jawaban yang sah, pemeriksaan ini tidak bisa membedakannya dari teks yang salah. Bab 21 menunjukkan cara membaca laporan error dari Atoi, dan itu memperbaikinya.

Perulangan validasi hanya berakhir dengan jawaban yang baik. Kalau input habis, misalnya ketika input datang dari file dan bukan dari keyboard, setiap pembacaan berikutnya memberikan "", angkanya 0, dan perulangan mencetak pesannya selamanya. Hentikan dengan Ctrl + C. Membaca nilai kedua dari ReadString, yang dijelaskan Bab 21, adalah cara program mengetahui hal ini.

6. Sentinel: membaca sampai nilai khusus

Sering kali kamu tidak tahu berapa banyak nilai yang akan datang. Sentinel adalah input khusus yang berarti "sudah semua". Program ini membaca nilai sampai kata done, lalu mencetak rata-ratanya. Ini adalah exercises/20/03-sentinel.go, di dalam main:

reader := bufio.NewReader(os.Stdin)

total := 0
count := 0

for {
    fmt.Print("Score from 1 to 100, or done: ")
    text, _ := reader.ReadString('\n')
    answer := strings.TrimSpace(text)
    if answer == "done" {
        break
    }

    score, _ := strconv.Atoi(answer)
    if score < 1 || score > 100 {
        fmt.Println("Skipped.")
        continue
    }

    total += score
    count++
}

if count > 0 {
    average := float64(total) / float64(count)
    fmt.Printf("%d scores, average %.2f\n", count, average)
} else {
    fmt.Println("No scores entered.")
}

Contoh sesi:

Score from 1 to 100, or done: 80
Score from 1 to 100, or done: abc
Skipped.
Score from 1 to 100, or done: 91
Score from 1 to 100, or done: 150
Skipped.
Score from 1 to 100, or done: 74
Score from 1 to 100, or done: done
3 scores, average 81.67

Perulangan ini memakai pola yang sudah kamu kenal: mengakumulasi total dan menghitung dengan count. Tiga hal lagi:

  • Sentinel diperiksa sebelum konversi, karena ia berupa teks. Ini juga menghindari jebakan: dengan angka seperti 0 sebagai sentinel, teks yang salah akan mengakhiri perulangan, karena teks yang salah juga memberikan 0.
  • continue dari Bab 18 melewati sisa putaran, jadi nilai yang salah tidak ditambahkan atau dihitung.
  • count > 0 menjaga pembagian, seperti di Bab 17.

Rata-rata adalah tempat tipe Go berpengaruh. total dan count adalah int, jadi total / count adalah pembagian bilangan bulat dan membuang pecahannya. Konversi keduanya ke float64 dulu, seperti di Bab 13:

total := 245
count := 3
fmt.Println(total / count)                          // 81
fmt.Printf("%.2f\n", float64(total)/float64(count)) // 81.67

Mencampur pola

Program nyata menggabungkan pola-pola ini. Satu kali jalan bisa mengakumulasi total, menghitung berapa yang cocok dengan suatu aturan, dan menandai apakah sesuatu yang khusus muncul, semuanya sekaligus:

total := 0
evens := 0
hasMultipleOf5 := false

for i := 1; i <= 10; i++ {
    total += i // akumulasi
    if i%2 == 0 {
        evens++ // hitung
    }
    if i%5 == 0 {
        hasMultipleOf5 = true // tanda
    }
}

fmt.Println("total", total)
fmt.Println("evens", evens)
fmt.Println("multiple of 5?", hasMultipleOf5)

Output:

total 55
evens 5
multiple of 5? true

Satu perulangan, tiga pola, tiga jawaban.

PR

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

Soal 1 — Faktorial

Buka exercises/20/homework/01-product.go. Dengan pola akumulasi, kalikan angka 1 sampai 5 dan cetak hasilnya. (Petunjuk: hasil kali mulai dari 1, bukan 0, karena apa pun dikali nol adalah nol.)

Soal 2 — Hitung kelipatan

Buka exercises/20/homework/02-count-multiples.go. Dengan pola hitung, cetak berapa angka dari 1 sampai 50 yang habis dibagi 7.

Soal 3 — Kuadrat besar pertama

Buka exercises/20/homework/03-first-big.go. Gunakan pola cari untuk menemukan dan mencetak angka pertama yang kuadratnya di atas 200, dengan memeriksa dari 1 ke atas dan berhenti dengan break.

Tantangan — Statistik rentang dalam satu jalan

Buka exercises/20/homework/04-stats.go. Dalam satu perulangan atas 1 sampai 20, hitung dan cetak tiga hal: total (akumulasi), berapa yang genap (hitung), dan apakah ada angka yang habis dibagi 9 (tanda).

Stuck atau sudah selesai? Buka halaman solusi PR.