The Startup
Published in

The Startup

Learning Go By Example: Part 1

I strive to be a lifelong learner and enjoy learning new things. I prefer to learn skills by seeing and working with examples. I especially like learning new programming languages and frameworks and have been wanting to pick up Go for quite a while. There are plenty of great resources to learn Go including the wonderful https://tour.golang.org/ that offers a hands on tour of the Go language. There are also several good books on Go including:

These are wonderful resources. I was eager to start coding and used these resources as I dove into the Golang pool.

Why Learn Go?

I know quite a few programming languages why should I learn another one, especially Go? First of all, Go has quite a pedigree. It was created by software engineering legends Brian W. Kerninghan, Rob Pike, and Robert Griesemer. A number of important software projects including Docker and Kubernetes, have been written in Go. Here is a post that makes a compelling case for Go. A better question might be, why not learn Go?

The First Example: Finding Hashtags

My daughter recently started taking her first programming course and her school selected Python to teach students the fundamentals. I am very fond of Python and thought it was a good choice. I eagerly volunteered to be my daughter’s unofficial TA.

I’ve known Python for a long time, but can remember only one time in which I used it professionally. Nevertheless, I “think” in Python. When I write pseudocode, I usually write it in a Python-esque manner. One of my daughter’s first assignments was to write a hashtag finder. The program had to read in tweets and print out all the included hashtags. It was a fairly straightforward exercise:

def main():
while True:
tweet = input("Enter tweet: ")
if tweet.lower() == "quit":
print("Goodbye!")
break

words = tweet.split(" ")
has_hastags = False
for
word in words:
if word[0] == '#':
print("Hashtag: ", word)
has_hastags = True

if not
has_hastags:
print("No hastags")

if __name__ == "__main__":
main()

The code above is very readable except for the somewhat esoteric manner in which main code blocks are defined in Python. Rewriting this example in Go turned out to be a fairly simple exercise:

package main

import "bufio"
import "fmt"
import "os"
import "strings"

func main() {
var reader = bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter tweet: ")
var tweet, _ = reader.ReadString('\n')

if strings.EqualFold(tweet, "quit\n") {
fmt.Println("Goodbye!")
break
}

var words = strings.Split(tweet, " ")
var hasHashTags = false;
for _, word := range words {
if word[0] == '#' {
fmt.Println("Hashtag: ", word)
hasHashTags = true
}
}

if !hasHashTags {
fmt.Println("No hashtags")
}
}
}

This code is also very readable even for a Go newbie.

package main

import "bufio"
import "fmt"
import "os"
import "strings"

The snippet above defines the main code block (a bit simpler than Python) and the required imported packages (i.e. libraries) for the program.

func main() {
var reader = bufio.NewReader(os.Stdin)

This code snippet defines the main function and creates a reader variable that holds a reference to a buffered reader from standard input. The keyword var is used to define variables. We’ll use the reader variable to read a string from the keyboard. This is similar to something we would see in other languages like C or Java. It’s not needed in Python because standard input is implied in Python’s input function. Notice that the NewReader function is from the bufio package which we included in an import statement above.

for {
fmt.Print("Enter tweet: ")
var tweet, _ = reader.ReadString('\n')

if strings.EqualFold(tweet, "quit\n") {
fmt.Println("Goodbye!")
break
}
...

This code snippet defines an infinite loop which is equivalent to the Python while True: loop. We then prompt the user to enter their tweet. The call to the ReadString method is interesting because it returns two values. All Go functions (and methods) are able to return multiple values which can be very useful. The value is the string from standard input and stored in the tweet variable. The second value is the resulting error code. In this case, we use the _ character to ignore it. We could’ve also written var tweet, errorCode := reader.ReadString('\n'), but since we won’t be using the error code, we can avoid a compiler error by ignoring it (the Go compiler doesn’t allow unused variables). The '\n' defines the terminating delimiter of the string. In other words, it returns the string up to, and including the new line delimiter.

The EqualFold function performs a case insensitive comparison of the tweet and the value “quit\n”. Notice that we had to include the trailing newline character in the comparison.

Functions versus methods

I want to make a quick note about the difference between functions and methods. In the snippet above, EqualFold is considered to be a function because is referenced directly from the strings package. ReadString is considered a method because it’s referenced from the object reference contained in the reader variable.

      var words = strings.Split(tweet, " ")
var hasHashTags = false;
for _, word := range words {
if word[0] == '#' {
fmt.Println("Hashtag: ", word)
hasHashTags = true
}
}

if !hasHashTags {
fmt.Println("No hashtags")
}

In the code snippet above, we use the Split function to break up the tweet into separate words that are stored in the array contained in the words variable. We then define a strange looking for loop. We can define an index in the first part of a for loop. Since we won’t be needing one, we use the _ character to ignore it. The for loop iterates through all the words in words array. The word variable contains the word of the current iteration. This variable is only scoped within the for loop and cannot be used outside the loop. If the first byte of the word is a # character, that word is a hashtag and we print it and set the hasHashTags variable to true. After the for loop, if hasHashTags is still false, then we print that we did not find any hashtags.

Refactoring the code

There are few things that we can do to improve this example and make it a better Go program:

package main

import (
"bufio"
"fmt"
"os"
"strings"
)

func main() {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter tweet: ")
tweet, _ := reader.ReadString('\n')

if strings.EqualFold(strings.TrimSpace(tweet),"quit") {
fmt.Print("Exiting...")
break
}

hashtags := findHashTags(tweet)

for _, hashtag := range hashtags {
fmt.Printf("Hashtag: %s\n", hashtag)
}

if len(hashtags) == 0 {
fmt.Print("No hashtags")
}
}
}

func findHashTags(tweet string) []string {
var hashtags[] string
words := strings.Split(tweet, " ")

for _, word := range words {
if word[0] == '#' {
hashtags = append(hashtags, word)
}
}

return hashtags
}

It’s better style to group all the imports into a single parenthesized, “factored” import statement:

import (
"bufio"
"fmt"
"os"
"strings"
)

That makes it a little better to read.

reader := bufio.NewReader(os.Stdin)

We can use the := operator to define and assign variables. This is a shortcut for var reader = bufio.NewReader(os.Stdin).

if strings.EqualFold(strings.TrimSpace(tweet),"quit") {
fmt.Print("Exiting...")
break
}

In this code snippet, we use the TrimSpace function to remove all whitespace from the tweet variable so we can compare to a more simple “quit” value.

hashtags := findHashTags(tweet)

for _, hashtag := range hashtags {
fmt.Printf("Hashtag: %s\n", hashtag)
}

if len(hashtags) == 0 {
fmt.Print("No hashtags")
}

In the code snippet above, we broke out the hashtag code into its own separate function, findHashTags which returns an array with the associated hashtags. The for loop iterates through this array and prints out the hashtags. If the array is empty (its length is zero), we print out a message that no hashtags were found.

The findHashTags function is fairly straightforward:

func findHashTags(tweet string) []string {
var hashtags[] string
words := strings.Split(tweet, " ")

for _, word := range words {
if word[0] == '#' {
hashtags = append(hashtags, word)
}
}

return hashtags
}

The first line defines findHashTags as a function that takes in a string parameter and returns a string array. The function uses array slices to build the return value contained in the hashtags variable. We can view slices as references to arrays. That effectively allows us to treat them as dynamic arrays to which we can append values: hashtags = append(hashtags, word).

Type inference

Go is a statically typed language but it allows for inferred typing. For example, in this snippet:

words := strings.Split(tweet, " ")

the variable words is not explicitly typed. The type is inferred from the function strings.Split. We could’ve also written: var words[] string = strings.Split)tweet, " ").

Summary

This was a fairly simple example, but I hope that it helps start your journey to learning and becoming literate in Go. We’ll continue to post more intricate examples to learn additional Go features and idioms. Here is my second Go post.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store