golang

What I have learned from Gopher exercises 01 (csv quiz game)

I’m not sure whether this blog post will benefit anyone but me, but I’m trying to put what I have learned from gopher exercises in writing (I’m not even sure whether I’ll be doing the same with the rest of the exercises). and I thought I’ll publish it as a blog post instead if just keeping it in my local drive

The exercise

you can find the exercise here:

https://courses.calhoun.io/lessons/les_goph_01

in a nutshell, we want to build a small command line application that will show quiz questions to the user and expect responses. And then show the user how may answers they got right.

The questions are supposed to be in a simple CSV file. And then we are supposed to add a timer to the quiz so we can limit how long the user could spend on the quiz.

I enjoyed doing this exercise and here are things I’ve learned from it so far:

Opening a CSV file

Since we are supposed to work with small CSV files, we won’t face any issues if we decide to read the content of the whole file at once (as opposed to reading it line by line).

In order to read a CSV file we would need first to open the file like this:

Note that we are using *csvFilename here since we are reading the timer as a flag (returned as a pointer)

and then use the csv package to create a new Reader:

after that we can read the content of the whole file just like this:

 

I ended up creating a new function to read CSV file (even though it is needed just once in this exercise)

 

Using flags

Since the application is supposed to accept arguments/ flags, one for the filename of the quiz file and the other for the timer, we need a way to read these flags.

It turned out reading the flags is really easy with the flag package:

 

note that we are specifying the type of the flag we are reading (flag.String vs flag.Int), and we can also specify a default value (the second argument) as well as a help message that could be shown when we run our program with the --help flag

keep in mind that we need to add the following line after we “read” all the flags, otherwise the flags won’t be parsed, and they will be assigned the default values:

Parsing data to your own type could be really helpful

This wasn’t necessary to solve the exercise, but this will help make your code clean, and easier to deal with different source of data (or at least this is what I understood from the explanation of the instructor)

after we read the CSV file in the previous step, we will end up a slice of slices of strings

like this:

 

we can create a new type (struct) that will represent each question.

 

and then we can create a new function that will parse the lines we got when we read the CSV file and transform them into a slice of questions

 

Note here that when we create the internal slice that will hold the parsed questions, we are creating a slice with a predetermined size as opposed to created a slice without a size and append questions to it on the go

 

The reason behind this, is that we already know the size of the slice and there is no need to make the app change the size (and thus create a new slice?) every time we add a new element

Creating a timer

When we create a timer with time.NewTimer we need to specify the unit of time used as well we the number of units.

For the first one, and since in our case we are using seconds, we can just use this time.Second

But to specify the number of seconds, we can’t just use an int directly but rather we need to pass that int to time.Duration like this:

 

Note that we are using *time here since we are reading the timer as a flag (returned as a pointer)

You can make the app intercept user input with go routines and channels

If our application is supposed to read a user input, and the user doesn’t enter any value, even if our application has a timer/limited by the time, the app will wait until it gets the input from the user.

One way to solve this issue is to use a channel and a go routine as follows:

 

what this go routine is doing is that it will wait for the user input and as soon as it gets it it will push it to a channel

PS: the instructor mentioned something here. Our go routine is an called an anonymous function, but when it uses variables outside the scope of the function (the channel in our example) we call it a closure

How would this help the app intercept the user input?

in this exercise we we using select to wait on multiple channels, the one related to the timer and the one related to the user input. In this case as soon as the timer finishes up, it will send a signal in its channel, and we won’t need to wait for the user input

 

we can use a goto like breaks in go as well

In the previous snippet you might have noticed the use of the problemLoop: label at the beginning as well as this special break command break problemLoop

as you can tell what this break do is to break the loop/code that it is inside the label we mentioned above

solution

here is the solution for this exercise ( it is almost identical to the code you can find it in the link above):

 

%d bloggers like this: