Git Commit History: Github

Go Routines

The most interesting part of this exercise was using the go routine. I would like to practice these more, but usually I end up wasting time trying to think of ways to use them.

I understood that I needed a go routine so my timer could work in the “background” and interrupt the quiz immediately upon timeout, but I got things a bit backwards in the beginning, and I neglected to use channels.

After looking at the solution, I was able to correct things and make the code much clearer.

My first attempt was pretty ugly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
go func() {

    limit := *limitSeconds

    for range time.Tick(1 * time.Second) {
        limit--

        if limit <= 0 {
            timesup = true
            log.Println("Time's up!")
            break
        }
    }
}()

The idea was to just set a flag that would be checked in the problem loop. I’ve since learned that sharing memory like this is a big nono.

Pausing for User Input

It seems there are at least a couple ways to do this.

I haven’t written many programs for the command line before, so perhaps the way to do this is more intuitive for others.

My first try:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

  // Read the input to start
  buf := bufio.NewReader(os.Stdin)

  fmt.Println("You will have " + strconv.Itoa(*limitSeconds) + " seconds to complete the quiz.")
  fmt.Println("Hit enter when you're ready to start...")

  // ReadBytes until \n which is "enter".
  // because \n is our delimeter, we have to trim
  // that off our answer to do a comparison with the
  // expected answer.
  _, err = buf.ReadBytes('\n')

Notice the comment: “// because \n is our delimeter, we have to trim // that off our answer to do a comparison…”

That made things a bit ugly, so I like this approach better:

1
2
// Hold until ENTER
fmt.Scanf("\n")

and to capture text input until ENTER:

1
2
3
var answer string
// accepts a single string and then ENTER
fmt.Scanf("%s\n", &answer)

Timer

I first used time.Tick

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

import (
  "time"
)

func main() {
  for range time.Tick(1 * time.Second) {
    // stuff...
  }
}

but I should have used:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
  "fmt"
  "time"
)

func main() {

  timer := time.NewTimer(time.Duration(1) * time.Second)

  problemsLoop:
      for i, problem := range problems {

        // stuff...

        select {
        case <-timer.C:
            fmt.Println("Time's up!")
            break problemsLoop
        }
      }
}

so it seems to me that a great pattern to get comfortable with would be this:

Play Link

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
	"fmt"
	"time"
)

func main() {

	timer := time.NewTimer(time.Duration(1) * time.Second)

	vv := []string{"One", "Two", "Three"}

problemsLoop:
	for _, v := range vv {

		myChan := make(chan string)

		go func() {
			myChan <- v
		}()

		select {
		case <-timer.C:
			fmt.Println("Time's up!")
			break problemsLoop
		case value := <-myChan:
			// do something with that value
			fmt.Println(value)
		}
	}
}