HomeArchitectureIt is time to Go: our journey into grown-up code part 6

It is time to Go: our journey into grown-up code part 6

This is the sixth article in our series in learning GO, the first four articles can be found here:

  1. It is time to “Go” and learn how to code with Go
  2. It is time to Go: our journey into grown-up code part 2
  3. It is time to Go: our journey into grown-up code part 3
  4. It is time to Go: our journey into grown-up code part 4
  5. It is time to Go: our journey into grown-up code part 5

To date, our journey has covered a lot of ground, and we have slowly built up an understanding of several core features and concepts.  We are truly building firm foundations.

  • The core Variables types
  • Variadic variables
  • Redeclaration
  • Constants
  • inputs
  • Structs
  • Pointers
  • Interfaces
  • Function basics
  • Conditional statements, if statements and switch statements.
  • Loops, breaks, deferment and recover
  • Range
  • Importation of built-in functions (“fmt”, “sync”, “reflect”, “strings”)

At the end of our last article, we extended our loop code with the addition of extra packages, we looked at the “reflect” package that allows us to manipulate objects at runtime this is a very powerful tool.  The next built-in function we introduced was “sync” which is used to provide synchronisation between concurrent goroutines.  We learnt that this is important to prevent a condition known as a “race condition,” we learnt that this is not a good situation as it could result in unpredictable behaviour as two or more goroutine attempt to read the same shared resource.  As a part of this investigation, we focused on the “waitgroup” resource that “sync” provides.  Please review these articles if you are unsure of anything we have covered to date.

What are we looking at today?

To day we will start to look at working on something a little more complex than simply printing out a set of numbers in an array,  today we will look at an array of 2 to 100 and pull out all the prime numbers, so any number that is divisible only by itself or one.  The code should hopefully be starting to look familiar by now.

 

package main
import "fmt"

func main() {
/* local variable definition */
var i, j int
   for i = 2; i < 100; i++ {
      for j = 2; j <= (i/j); j++ {
         if(i%j==0) {
            break; // if factor found, not prime
         }
      }
      if(j > (i/j)) {
         fmt.Printf("%d is prime\n", i);
      }
   }
}

Running this code results in the following output

Go:- Prime Numbers to 100.
Prime Numbers from 2 – 100,

So let’s start to look at the magic.  IN this piece of code we first declare two integers “i” and “j” and instantiate the first loop.

for i = 2; i < 100; i++ {

Note that today our initial value for “i” is “2”, that’s right, you do not have to instantiate an integer at “0”.  We will continue the loop whilst the value of “i” is under “100”, and increment “i” by “1” each loop.  This then leads into the meat and two veg of the loop, which is another loop instantiation.

for j = 2; j <= (i/j); j++ {

Here we instantiate “j” with a value of “2”, however our condition statement is actually a equation “j <=(i/j)”, which means we validate whether ”i” is less than or equal to “j”, and finally we increment “j” by one again.  If our validation is proven, we drop into the if statement

if(i%j==0) {

“if(i%j==0)” this is a conditional statement that checks if the of “i” divided by “j” is equal to “0”. If it is, then the code inside the curly braces {} will be executed. In our case, we will issue the break command and drop out of the inner loop, and restart the loop process as long as “i” is less than 100.

If “i” is devisable by “j” then the number is Prime and will be printed out to standard out as shown by the last if statement that verifies that “i/j” results in j being greater that “i”.

if(j > (i/j)) {
   fmt.Printf("%d is prime\n", i);
}

OK, so we have found Prime Numbers, what about composite numbers, in Mathematics, these are number that are divisible by more than itself or one.  The simplest example is the number 4 as this can be divided by 1,2 and 4.  You may think the logic to find composite numbers is simpler than finding a Prime Number, and looking at the code link below it does seem so.

package main

import "fmt"

func main() {
   for i := 2; i <= 100; i++ {
      for j := 2; j <= i; j++ {
         if i%j == 0 && i != j {
            fmt.Println(i)
            break
         }
      }
   }
}

The code is starting to look very similar, we have, by changing the condition of the second “if” statement completely changed the parameters. Previously our condition was the result of “i/j” in this code we state that “j <= i” this forces the inner loop to process every number between “2” and “100”.

if i%j == 0 && i != j {

Our inner loop checks have two constructs as noted by the “&&”, this construct verifies if the current number in the outer loop “i” is divisible by any number other than “1” and itself (i.e., if it is a composite number).  The first condition “i%j == 0” checks if “i” is divisible by “j”, while the second condition “i != j” ensures that “i” is not equal to “j” (since every number is divisible by 1 and itself).

The rest of the code is well understood, so we will not be explaining it again.  When we run this programme, it results in the following output.

Go- Prime Numbers to 100.
oh look composite numbers.

Now that we have looked at a nested loop, lets delve a little deeper. In the above code we looked at the break statement to escape a loop,  Go has another option, which is called the “continue” statement.  The continue statement in the Go programming language functions similarly to the break statement. However, rather than forcing the termination of the loop it forces the next iteration of the loop to occur, skipping any code in between.

So continuing with the theme of Prime numbers let’s look at calculating all prime numbers from 2-100 again.  But this time using the “continue” statement.

package main
import "fmt"

func main() {
   for i := 2; i <= 100; i++ {
      isPrime := true
      for j := 2; j < i; j++ {
         if i%j == 0 {
            isPrime = false
            continue
         }
      }
      if isPrime {
         fmt.Printf(“%d is a Prime Number\n”,i)
      }
   }
}

Running this code will result in the following output.

go:- Is Prime
everything is Prime, Numbers that is, not the new Soda.

We have introduced a new Boolean variable called “isPrime” and set its value as “True” by default.  We should be starting to understand the details of the condition statements, so we are not going to explain these.  Once the code enters the second loop, if the number is divisible by any number other than 1 or itself it drops into the loop and sets “isPrime” to false and executes the continue statement to restart the loop. If however, the number is divisible by 1 or itself the code drops to the second statement which states that if “isPrime” is “true” then print the string to standard output.  Doing the same with composite numbers, we could simply reverse the Boolean statements, but for clarity we will declare a new Boolean variable “isComposite” this time as “false”, change the variable name in the second loop to “isComposite” and giving it the value of “true”.

package main

import "fmt"

func main() {
   for i := 2; i <= 100; i++ {
      isComposite := false
      for j := 2; j < i; j++ {
         if i%j == 0 {
            isComposite = true
            continue
         }
      }
      if isComposite {
         fmt.Printf(“%d is a Composite Number\n”, i)
      }
   }
}

Finally, we change the “isPrime” statement to “isComposite” in the “Printf” block and run the code, if everything fine and dandy you should receive the following response.

go:- composite numbers
these are not prime, but composite as expected

You are go-ing to Hell

There is a final loop break-out statement, and that is the “goto” statement, this is a method to allow a piece of code to jump to a particular position, it can be used to control the flow of execution.

not all spaghetti is edible, Go code will not taste nice.
not all Spaghetti is edible. Go Code may be a little stringy.

A large subset of coders abhor the “goto” statement in any language, not just Golang, this is because it can lead to very hard-to-follow code, with code jumping all over the place leading to something akin to a bowl of Spaghetti.

So, if it is not recommended to use “goto”, why are we looking at it? Firstly completeness, but secondly if we do not know about the “goto” statement then how are we going to make an informed decision on whether to use it or not?  Also if the “goto” statement was such an anathema, why is it used almost 500 times over almost a hundred files in the Golang standard library.

OK, I am sold, Go has a goto but how does it work?

The way it works is that you can write down a label, which is a custom keyword that the “goto” statement recognises. A label is a string that is created by following the label with a semicolon, in some ways this is similar to a variable. But the value is a position a function that runs some code.,

label:
// Label-specific code

To prevent code getting too complicated GoLang has implemented some safeties in that a label must be created in the same function as the “goto” statement. The label must be used; the compiler will detect if it is not. If you create a variable after a label, all variables used in the label must be defined earlier in the scope.

The best way to understand it is probably to see it; here is a very simple example of how to set a label and then jump to it, skipping any code in between. We’ll make a for loop that runs 10 times, but when I equals 5, we’ll use a goto to break the loop. It is important to note that the label must be created within the same function or the compiler will fail. Again if the label is not used, then the compiler will again fail. In the example, we have named the label into the exit.

package main

import (
   "fmt"
)

func main() {
   fmt.Println("Hello! This is your first 'goto' statement")
   // We create a for loop which runs until i is 10
   for i := 0; i < 10; i++ {
      fmt.Printf("Index: %d\n", i)
      if i == 5 {
         // When i is 5, lets exit by using goto
         goto exit
      }
   }
   fmt.Println("Skip this line here")
   // Create the exit label and insert code that should be executed when triggered
exit:
   fmt.Println("We are now exiting the program")
}

The interesting bit here is the “goto exit” statement

goto exit

Our code will execute the exit statement after iterating 5 times through the loop, this will immediately jump the programme to the named label, in this case “exit:”.  The results are shown below.

Go - our first Goto statement
With this label we know the way out.

OK that is great but how about proving for the guide rails.  In our next piece of code we declare the label in one function, and issue the pointer in another function.

package main

import (
"fmt"
)

func main() {
   fmt.Println("Hello, Reader! Your learning about 'goto' statement")
   // We create a for loop which runs until i is 10
   for i := 0; i < 10; i++ {
      // printInteger will try to goto to exit, which will not work
      printInteger(i)
   }
   fmt.Println("Skip this line here")
      // Create the exit label and insert code that should be executed when triggered
exit:
   fmt.Println("We are now exiting the program")
}
func printInteger(i int) {
   fmt.Printf("Index: %d\n", i)
   if i == 5 {
      // When i is 5, lets exit by using goto
      goto exit
   }
}

Running this code results in the following response.

a world of Go Errors, not too easy then.
This is what happens when you don’t follow the rules

No magic, just errors 😊 and the definitions are relatively sensible.

Go now, Why I want more.

OK what if you need more than one or more “goto” statements? Simple, just define extra labels.  Let’s just for fun program in some redundant code.

package main

import (
   "fmt"
)

func main() {
   fmt.Println("Hello! more 'goto' statement fun")
   // We create a for loop which runs until i is 10
   for i := 0; i < 10; i++ {
      fmt.Printf("Index: %d\n", i)
         switch i {
           case 5:
              goto exit
           case 6:
              goto second
           case 7: // I have to reference third label otherwise compiler wont run
              goto third
           }
       }
fmt.Println("Skip this line here")
// Create the exit label and insert code that should be executed when triggered
exit:
   fmt.Println("We are now exiting the program")
second:
   fmt.Println("This is a second label executed because it comes after exit")
   return
third:
   fmt.Println("this wont print")
}

When you run this code you will receive the following result.

Go - We can have multiple labels
These are labels, but they don’t all work

Now just you wait a minute there, why have we got a line after the “exit:” label?

Remember that the label is not an exit statement, but a pointer to a position in the code, in our case the label ”exit:”,  this means that our code will continue to run from the new entry point and execute all code following.

OK I get that, but why did the third: label not print?

That is simple,  if you look closely at the code contained in the second label:  You can see the introduction of the “return” statement, at first look at this, the name appears to be incongruous with the actual use of the statement.   However, in this case the “return” statement actually means a return to standard output or the terminal rather than a return to a position in the code.

Summary

Once again, we have travelled a long way hopefully you have learnt something new,  I know I have.   We have looked at “nested loops”, “breaks”, “continues” and “goto” labels.  In our next article we will delve deeper into arrays and slices; as we have already mentioned in a previous article these are the building blocks data.  Remember if you are interesting in gaining understanding on a particular function or concept, reach out and ask the question and we will investigate it together.

NEWSLETTER

Receive our top stories directly in your inbox!

Sign up for our Newsletters

spot_img
spot_img

LET'S CONNECT