A first look at Go

I have been curious about the Go programming language for a while. I have been reading a little bit about it and heard other people talk about how they started using Go at some project where they work. I have not had the same opportunity and I have not found the time to look into Go before now. December this year (2019) I decided that I should solve the tasks in KnowIT Christmas calendar in programming languages that I am curious about and not able to use as much as I want.

Getting the environment up and running

To get started with Go programming, it is needed to install Go from their website. I installed it at a Windows 10 machine and that worked right out of the box. At my MacOS Pro machine, I installed the binary and then I had to add one line to ~/.zshrc file.

export PATH=$PATH:/usr/local/go/bin

That was everything I had to do to get the required bits to start playing around with Go. I use Visual Studio Code with Go extension while writing Go code.

Note! In my first look into the Go programming language, I did not look into debugging capabilities and stuff. I just wrote the code and did go build and then executed the executable from the command line.

Hello World!

The first thing I do looking into a new programming language is to make something compile and run. This something is often a very tiny program and most of the time just printing out "Hello World!" to the console. Getting started with Go was no different. First created a folder HelloWorldGo and added an empty file HelloWorld.go.

package main

import "fmt"

func main() {
  fmt.Println("Hello World!")
}

When starting to write Go for the first time, coming from a C# background, I often added semicolons and had to remove them again. I love the simplicity when I do not need to end every line with a semicolon. The import statement import the fmt package that is the base package in Go. The syntax for writing the main function is also pure simple.

Wrote the code, what´s next?

In this first look into Go code, I have been focusing on how it is to write code and how it feels. I want to check out how I can use Go and debug the code sometime later. For now, I have just had the console open and changed the current directory to where I had my Go file. When I needed to compile the code, I just typed go build. That even gave me an error message that I had something wrong in my code or gave me an executable. The output file can be run from the terminal.

Solving a problem using Go

The problem I wanted to solve using Go was door number 9 in KnowIT Christmas calendar. In this exercise, I was given an input files with a lot of numbers (n) on each row. Below is an example of the rules that had to be applied.

n = 45
452 = 2025
2025 kan splittes til a = 20 og b = 25.
a + b = n
20 + 25 = 45.
45 is therefore a Krampusnumber.
n = 100
1002 = 10000
10000 kan splittes til a = 100 og b = 00.
Even a + b = 100 + 0 = 100, 100 is not a Krampusnumber, because a and b can not be 0. Here b = 0, so this is not valid.

My first thought was that I needed to read the input into memory. The first thing I really liked using Go was how I could be working with the errors. Treating error as values. While I was writing the code, Go was complaining if I declared a variable and did not use it. Here I declare a string slice to store the input values into.

func readLines(path string) ([]string, error) {
	file, fileError := os.Open(path)
	if fileError != nil {
		fmt.Println("Error reading file")
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Split(bufio.ScanLines)
	var txtLines []string
	for scanner.Scan() {
		txtLines = append(txtLines, scanner.Text())
	}

	return txtLines, fileError
}

Looking at the signature readLines(path string) ([]string, error) the function has a name readLines. This function takes one string argument and return a tuple containing a string slice and an error.

I created a function that should do the checking if the number was a Krampusnumber or not. Since I needed to check if a number could be split into two other numbers added that together became the original number, I thought I should use recursion.

func krampusSplit(n int64, number int64, splitIndex int) bool {
	numberStr := strconv.FormatInt(number, 10)

	if splitIndex > len(numberStr) {
		return false
	}

	a := numberStr[0:splitIndex]
	b := numberStr[splitIndex:len(numberStr)]

	if aNum, parseAError := strconv.ParseInt(a, 10, 64); parseAError == nil {
		if bNum, parseBError := strconv.ParseInt(b, 10, 64); parseBError == nil {
			if aNum == 0 || bNum == 0 {
				return krampusSplit(n, number, splitIndex+1)
			}
			if aNum+bNum == n {
				return true
			} else {
				if len(numberStr)-1 == splitIndex {
					return false
				}
				return krampusSplit(n, number, splitIndex+1)
			}
		}
	}
	return krampusSplit(n, number, splitIndex+1)
}

func isKrampusNumber(n string) (int64, bool) {
	var number int64
	var nsquared int64
	if v, parseErr := strconv.ParseInt(n, 10, 64); parseErr == nil {
		number = v
		nsquared = v * v
		isKrumpus := krampusSplit(number, nsquared, 1)
		if isKrumpus {
			return number, isKrumpus
		}
	}
	return number, false
}
The formal grammar uses semicolons ";" as terminators in a number of productions. Semicolons

The function isKrampusNumber takes in one number from the list and squares that number and pass the number to the next function that uses recursion to decide if it is a KrampusNumber. While parseing the string to int64, I use ";" to separate to separate two complex statements in the if-statement. Most of the Go code I have been writing so far I could skip the ";".

func sumIntList(list []int64) int64 {
	var sum int64
	for _, input := range list {
		sum = sum + input
	}
	return sum
}

I needed a way to add all the numbers in the slice that I added the KrampusNumbers into, I ended up writing a little function for that. I could not find a built-in way of doing this. In this code, I use "_" in the for-loop. That means that I do not care about that value. If I given a name there, that would have been the index number in the slice.

func main() {
	data, err := readLines("krampus.txt")

	if err != nil {
		fmt.Println("Error reading input file")
	}

	var krumpusNumbers []int64

	for _, input := range data {
		n, isKrampusNumber := isKrampusNumber(input)
		if isKrampusNumber {
			krampusNumbers = append(krumpusNumbers, n)
		}
	}

	sumVal := sumIntList(krampusNumbers)
	fmt.Println("Sum of list", sumVal)
}

The main function was the first function that I wrote and for the most part, it is this function that hold the pieces together. The first line is where I call my readLines function with the name of the input file. Then I check that the error variable is not set before I create a slice of int64 to store the KrampusNumbers. Then I loop through the input values and call the isKrampusNumber function with the number to decide if it is a Krampusnumber. In the end I add all the Krampusnumbers together and print out the sum.

When I look back at the implementation, there are plenty of things that I could have done smarter and better. For instance declaring a slice to store the Krampusnumbers has no value at all. The only thing I am interested in is the sum and I could just had one variable var sum int64 and added each number to that one in the loop.

Conclusion

As said, I could done a smarter and better job when doing the implementation. That was not my goal at all, to get the most efficient Go code, but to just get started and play around with the language. My initial feeling about the Go programming language is that it is a clean and simple to write. It is a language where it is possible to get started fast and be productive quite fast.

The things that I like the most about Go after just getting started is how I can handle errors as values. The next thing is that it is complaining when I declare a variable that is not getting used. Of course the thing that I do not need to include the ";" everywhere!

I look forward to continue my Go journey and discover more about the language. What I specifically look forward to is looking into Channels and trying to build REST-api services.

Cheers and happy coding!

Teis Lindemark

Passionate software developer and beer brewer

Bergen, Norway https://teilin.net