introduction to programming with go

33
Introduction to programming with Go Youri Ackx Jun 16, 2019

Upload: others

Post on 01-Mar-2022

9 views

Category:

Documents


0 download

TRANSCRIPT

Introduction to programming with Go

Youri Ackx

Jun 16, 2019

CONTENTS:

1 Introduction 1

2 Basics 32.1 Abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.2 First program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.3 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.4 Conditional statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.5 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.6 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.7 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.8 Arithmetic operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.9 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.10 Logical operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.11 Practical case - Leap year . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.12 Read user input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.13 String formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.14 Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222.15 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.16 Vocabulary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3 Appendix 273.1 About Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.2 Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4 Indices and tables 29

i

ii

CHAPTER

ONE

INTRODUCTION

This book is about programming, algorithms and data structures. It is not a book on Go. A tutorial dedicated toa language will generally give you recipies to solve a problem, without necessarily discuss the underlying algorithm.For instance, it would give you instructions on how to sort a list using a library or a built-in function, and that wouldhelp you meet that specific need. But it would probably not discuss how sorting actually works.

Of course, Go will be used heavily throughout this book, and will inevitably taint the way concepts are explained. Inthe end, you will however reach the same goal write programs but you will follow a somewhat longer, deeper path.What you will learn here, you will be able to transfer to other programming languages like Python, Java or C, to someextent at least.

We will cover a single programming paradigm: procedural programming. Object orientation for instance if not inthe scope of this book.

Go may be seen as a peculiar choice for learning. We discuss the reasons of this choice and its merits in appendix.You will also find some background on that great language, and practical information to install it on your machine.

We will take a non-formal, intuitive and practical approach to programming, heavily based on exercises of increas-ing complexity, adding the minimum amount of theory necessary as we progress to solve them.

For beginners, the first part lays the foundation of programming. Variables, loops, conditional statements and func-tions found in most languages will be presented. At this stage, you will be able to solve simple problems, like checkingif a paper fits in an enveloppe.

We will continue with more advanced data structures. Slices and maps are the bread and butter of Go programs. Fromthere, you can already go a long way and write useful programs.

We will extend these data structures to form lists, linked lists, queues, stack and trees to mention the most commonones. We will read data from files and manipulate them. At this intermediate level, we will talk about recursion,sorting, backtracking, space and time complexity, finite automatons. We will solve both fun and classical academicproblems like the eight queens problem, the towers of Hanoi, play blackjack, or implement the classical Conway’sgame of life.

1

Introduction to programming with Go

2 Chapter 1. Introduction

CHAPTER

TWO

BASICS

2.1 Abstraction

In order to write programs, we need abstractions.

Consider a hard disk drive. It is an enclosure containing rapidly rotating disks or platters, each of which is coatedwith magnetic material in order to store data. Arms with magnetic heads move above the platters in order to read (andwrite) the data that compose an image, a music or a poem.

You could read the poem and display it on screen by accessing the hard drive platter and communicating with the CPUdirectly, but the task would be incredibly difficult to achieve. You would have to repeat it for every single file youwould want to read, and for multiple disk geometries.

Maybe the drive can be seen as cyclinders and sectors that form some logical organization. Which they do in reality.This is still not the abstraction you are looking for - unless you are writing specific parts of an operating system, or ahard drive controller, in which case it is. Let us assume we have a more casual purpose.

Now consider the following program fragment:

3

Introduction to programming with Go

poem, err := ioutil.ReadFile("poem.txt")defer poem.Close()if e != nil {

panic(e)}fmt.Print(string(poem))

There are many things that require an explanation. What is err or panic for instance. All in due time. For now, wehave abstracted the process of retrieving the information on the disk platters. In fact, the abstraction is layered: the Gocompiler provides you with a first abstraction from the operating system, which in turn abstracts you for the processor,the memory and the hard disk. This is what we are looking for: a mean to express ideas and concepts while hidingthe underlying details. It is still nice to know how a hard drive or an operating system work though. But suffice tosay it is much more efficient and confortable to rely on the work done by others to access a storage medium and focuson our program.

Note: Hard drives are progressively getting replaced by solid state drives that have no mechaninal moving parts.Moving parts or not, you still want to be abstracted from the bare medium. Our program above will work, no matterthe underlying physical media.

2.2 First program

To get acquainted with a programming language, it is customary to write a program that displays a friendly message.For the first chapters, you do not necessarily need to have Go installed on your computer. If you have an internetconnection, you can simply enter an execute your program in the Go Playground.

package main

import ("fmt"

)

func main() {fmt.Println("Hello, playground")

}

This simple program already raises several questions. What is a package, why is there an import with "fmt" init? All in due time. For now, remember that Println prints a line on the screen. The term “print” and its variationis found in many programming languages, more often than “display”. main is the program entry point. It is where thefirst instructions will be executed.

This program is found by default when you open the the playground, so you don’t even have to copy-paste. Mark thispage, as this program will be the skeleton for many exercises we will solve in the next chapters.

2.2.1 A paper and a pen

. . . is all you need to write a program. For instance, let’s play hide and seek. But that, we mean to write pseudo-codethat describes a game:

count from 20 to 0while buddy not found:

seek buddy

4 Chapter 2. Basics

Introduction to programming with Go

Pseudo-code: Pseudo-code is an informal high-level description of the operating principle of a computer programor other algorithm. It uses the structural conventions of a normal programming language, but is intended forhuman reading rather than machine reading [Wikipedia].

It is helpful to describe an algorithm coarsly. But we can take the same algorithm and get into more low level details,for instance when we countdown:

counter <- 20while counter is not 0:

counter <- counter - 1

Same algorithm, different levels. The latter is more formal than the first. Both have their use. In both cases, we haveleft many things on the side. For instance, if your buddy is too well hidden, the game will go on forever as you keepsearching for them endlessly. A compiled program will gleefully execute the sequences of instructions you entered,regardless of their implications, provided that they are syntactically correct, and no matter the aberration they produce.

Paper and pen are fine, but they are not very motivating. A programming language also gives you more feedback andinteractions than paper. That is why we will rely on Go. But pseudo-code on paper or on a drawing board remains akey tool before jumping to your keyboard.

2.3 Variables

A variable is a storage place in memory. It has a name that identifies it. For instance, we could perform the followingsequence of instructions:

h <- 'hello'a <- 1b <- 2sum <- a + b + 4

The variable h contains the string of characters hello. a contains the value 1 and b is 2. Now sum is the sum of a,b and 4, that is 7. The equivalent in Go is to declare the variables, and to assign them a value.

func main() {h := "hello"a := 1b := 2sum := a + b + 4.5

}

Notice the := sign. Assigning variables does not do much. If you were to enter this code snippet in the programmain method, the program would execute but nothing would be displayed. Of course, we can print the results. We canmodify our program so that it displays the sum of three numbers.

fmt.Println(sum)

Would result unsurprisingly in the following output:

7.5

As its name implies, a variable can vary. Or more precisely, the value it holds can vary.

a := 7a = a + 2

2.3. Variables 5

Introduction to programming with Go

After the second instructions, a would hold the value 9.

As opposed to variables, we can define constants whose values cannot change once they are set.

const a := 5a = 6 // won't compile

Exercises

1. Write a program that initializes a variable 𝑛 , then displays its cube 𝑛3 .

2. Write a program that initializes three variables with a numerical value and displays their average.

Do not be daunted by this next exercise. By decomposing the probel in simple steps, you can figure out a solution onlywith variables and simple arithmetic operations.

3. In a factory, three ingredients are mixed to form a cake. The weight (in grams) of each ingredient isknown. We want to know their total weight, as a first approximation. But to be more accurate, one wouldtake into account spillage due to the fabrication process. Loss is known to be comprised between 3% and4.5% of the total weight of the three ingredients. Write a program that: a. Assign the weight of the 3ingredients, in grams, to three variables v1, v2 and v3. For example: 600, 150 and 475. b. Display theirtotal weight in grams, e.g. 1025. c. Display their total wight in kilograms and grams, e.g. 1kg 25g d.Display the minimum and the maximum actual weight, in grams, taking splillage into account.

2.4 Conditional statements

Programs perform different computations or actions depending on whether a programmer-specified condition evaluatesto true or false via a mechanism called conditional statement. We can express this in pseudo-code:

if condition:action1action2

else:action3action4

Conditions are expressed with the word if, for instance, if a > 10. Notice the indent, as it is not accidental. Theaction1 and action2 will only be executed if the condition is met, a.k.a if it evaluates to true. Otherwise,action3 and action4 will be executed. You can have any number of instructions in the “if” and in the “else”block. Let’s see how it translates in Go:

a := 10b := 5if isDivisible(a, b) {

fmt.Printf("%d is divisible by %d\n", a, b)} else {

fmt.Printf("%d is not divisible by %d\n", a, b)}

Go uses curly braces { and } to define a block of action. In this case, each block contains a single statement. The elsepart is not mandatory.

Here is a more practical example:

6 Chapter 2. Basics

Introduction to programming with Go

if no coffee:exit housebuy coffeeenter house

pour coffeedrink coffee

We will pour the cofee and drink it whether we had some left in the first place. The condition if no coffeeapplies to the block represented by the 3 instructions below it, indented. So we will exit house, buy coffee and enterthe house only if there is no coffee left. The two last actions pour coffee and drink coffee are outside the “if” block.

Coffee is easy. Let’s combine it with bread.

if no bread or no coffee:exit houseif no bread:

buy breadif no cofee:

buy cofeeenter house

slide breadpour coffeeeat breaddrink coffee

Exercises

1. Describe loosely the pseudo-code above.

2. What happens is there is coffee, but no bread?

3. In what cases do we exit the house?

Several blocks can be constructed:

a := 10if a > 10 {

fmt.Printf("%d is greater than 10\n", a)} else if a < 0 {

fmt.Printf("%d is less than 0\n", a)} else {

fmt.Printf("%d is between 0 and 10\n", a)}

Blocks can also be nested:

a := 1000if a < 0 {

fmt.Printf("%d is negative\n", a)} else {

if a > 100 {fmt.Printf("%d is a large number\n", a)

}}

2.4. Conditional statements 7

Introduction to programming with Go

2.5 Loops

What we described before in pseudo-code with the “while” block is a loop repeating an action or a set of actions whilea condition is met (or sometimes, a negative condition, e.g. ” while countdown is not 0”). A program often combinesloops and conditional statements.

if no bread:exit housetimer <- 10 minuteswhile timer > 0

search bakeryif barkery found:

buy breadenter house

if bread:slide breadeat bread

In this fictive example, we have put a limit of 10 minutes on the “search bakery” action. We have casually used a“while” loop to express this intent. This means we may not find a bakery, so the action “buy bread” can only happenif a bakery is found. Should we forget to check that we actually found a bakery, we would attempt to buy bread out ofthin air, and the program would terminate abnormally a.k.a. it would crash.

Same thing before we slice and eat bread: we must make sure we have the bread! Indeed, there’s am execution path inthis program where we started without bread, went outside, searched in vain for a bakery we did not find, so that wedid not buy bread and came home empty, leaving us without bread to slice or eat.

In Go, a loop is performed using the for instruction. The following program count downs from 10 to 0.

n := 10for n >= 0 {

fmt.Println(n)n = n - 1

}

On each step (called iteration), the program checks if the condition n >= 0 holds. If it does, it executes the instruc-tions in the “for” block, that is it prints the current value of the counter and decrement it.

A general form of a for statement is the following:

"for" [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] Block

where

• InitStmt is an initialization statement, performed once before starting the loop;

• Condition is the loop condition that must hold true to continue to iterate, for instance i < 10;

• PostStmt is a statement executed after each execution of the loop. Typically we increment a counter.

For example, the following program will compute the sum of all integers from 0 to 10 excluded, and print it:

sum := 0for i := 0; i < 10; i++ {

sum += i}fmt.Println(sum)

• Init statetment is i := 0. It is executed once;

8 Chapter 2. Basics

Introduction to programming with Go

• End condition is i < 10, aka we will perform the loop while i is less than 10;

• After each execution of the block, i is increased by 1 (incremented) with the instruction i++, a shortcut for i= i + 1.

Note

There are additional forms of loops. We will come back to them later, once we have the proper datastructures to iterateover.

Exercises

1. Write a program that displays even numbers for 1 to 10.

2.6 Functions

Suppose we want to display the sum of the squares of any two numbers.

𝑓(𝑎, 𝑏) → 𝑎2 + 𝑏2

𝑊𝑒𝑐𝑜𝑢𝑙𝑑𝑑𝑜𝑡ℎ𝑒𝑓𝑜𝑙𝑙𝑜𝑤𝑖𝑛𝑔 :

func main() {a := 2b := 3fmt.Println(a*a + b*b)a := 5b := 4fmt.Println(a*a + b*b)

}

You can see we are repeating ourselves. Instead of writing the same formula over and over again, we could writea function. We have already used functions, namely the one called main. It is easy to create our own calledsumOfSquares:

func sumOfSquares(a, b int) int {return a*a + b*b

}

We say sumOfSquares is a function that take takes two parameters a and b. They are of type int, which standsfor integer, and it returns another integer. That is fine, as our arguments 2 and 3 are integer values. The functionreturns another integer, that is the value that was computed. We can invoke it and print its output:

func sumOfSquares(a, b int) int {return a*a + b*b

}

func main() {fmt.Println(sumOfSquares(2, 3))fmt.Println(sumOfSquares(5, 4))

}

With a predictable result:

2.6. Functions 9

Introduction to programming with Go

1341

Exercises

1. Try to call sumOfSquares with one or two negative arguments. Does it work as expected?

2. Try to replace 2 by "hello" (with double quotes). What happens?

3. What if you attempt to pass a very large number to the function? Try to call it with 1000000000, 1 asarguments.

4. Write a function volume that, given a box dimensions, computes its volume.

5. Write a function withVAT that accepts an amount VAT excluded and a VAT rate, and returns the correspondingamount VAT included.

2.7 Data types

We have manipulated one sort of numbers called integers or int. Our function sumOfSquares does not acceptother types,

func sumOfSquares(a, b int) int {return a*a + b*b

}

sumOfSquares("hello", 2)

If we try to call the function with "hello" as a first argument, Go won’t even run our program. The compiler willanalyze it and rightfully complain:

cannot use "hello" (type string) as type int in argument to sumOfSquares

It will not go any better if we use a rational (non-integer) number:

a := 2.5b := 2sumOfSquares(a, b)

cannot use a (type float64) as type int in argument to sumOfSquares

Next to the int type, we have now discovered string and float64 data types. There are actually many morepredeclared types in Go. There is a granularity to integer types ranging from 8 to 64 bits representation. An 8 bitinteger can hold 256 different values.

Note

A bit can represent 2 values (0 and 1). Two bits can represent 4 values (00, 01, 10 and 11). In general, n bits canrepresent 2𝑛 distinct values. So with 8 bits, we have 28 or 256 distinct values that can range from 0 to 255 if we don’tcare about negative values. In Go, that corresponds to an unsigned 8 bits integer or uint8. Or we can span our rangeto both negative and positive numbers, going from -128 to 127 included with a signed 8 bits integer or int8.

By combining signed and unsigned integers with 8, 16, 32 and 64 bits, we obtain the following integer datatypes:

10 Chapter 2. Basics

Introduction to programming with Go

uint8 the set of all unsigned 8-bit integers (0 to 255)uint16 the set of all unsigned 16-bit integers (0 to 65535)uint32 the set of all unsigned 32-bit integers (0 to 4294967295)uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8 the set of all signed 8-bit integers (-128 to 127)int16 the set of all signed 16-bit integers (-32768 to 32767)int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)int64 the set of all signed 64-bit integers (-9223372036854775808 to→˓9223372036854775807)

Type int alone is an odd duck, as it will be either 32 or 64 bits, depending on your platform.

The situation is slightly simpler with “non integers non complex” numbers, as there are only two types float32 andfloat64 IEEE-754 32-bit floating-point representations. You can represent very large and very small numbers (inabsolute value) but. . . with a finite precision. This is not a Go-specific limitation as floating-point aritmethic is verycommon among programming languages.

Warning: Never use a floating point values to manipulate financial amounts, in whatever language.

2.7.1 Overflow

If you attempt to create a numeric value that is outside of the range that can be represented with a given number ofdigits, an overflow will occur. Typically, the result will “wrap around” the maximum. That condition will be mostlikely unanticipated, leading to an incorrect or undefined behavior of your program. This can have dire consequences.

On June 4th, 1996, an Ariane 5 rocket bursted into flames 39 seconds after liftoff. The explosion was caused by abuffer overflow when a program tried to stuff a 64-bit number into a 16-bit space. It’s estimated that the explosionresulted in a loss of US$370m. Fortunatelly there was no crew on board.

Sometimes, the result is less harmful. In 2014, a popular video clip caused the Youtube view counter to overflow,forcing Google to switch from 32 to 64 bits integer. A number of views greater than 2 billions had not been anticipated.

The consequences were far greater in the former case than in the latter. Depending on your context, you may need tobe extremelly wary, or you can afford to remain casual.

Often we can reason about a datatype that is safe for our purpose. An application dealing with small amounts islikely to be confortable with int32. The numbers supported by uint64 seem to go even beyond a banker’s wildestdreams, although today sky-high numbers (whatever the definition you give them) in the financial world cast a doubton the safest assumptions. However, imagine you manipulate cents rather than units in order to avoid dealing withdecimal numbers. You would multiply every number by 100. Or even by 10000 to safely manipulate 4 digits. Andsuppose you do computations on a currency like japanese yen currently at 1 JPY for 0,008 EUR, leading to furthermultiply values by about 1000. How safe is your initial assumption now?

Fret not however, as Go has dedicated implementations for big numbers for instance. But they come at the cost ofconvenience, readability and performance. That is why they are not your go-to solution.

2.7.2 Abstraction vs low level

Why not simply manipulate “integers”? Why “floating point arithmetic”? After all, we mentionned the importanceto abstract ourselves from the underlyting platform. Some languages only expose “integers” and “decimals”, but itcomes with a substantial performance cost. Go integers types are closer to the actual machine representation. That isa trade-off the languages authors decided to do, based on the intent and purposes of the language. From a teaching

2.7. Data types 11

Introduction to programming with Go

perspective, this design choice gets a bit into our way. On the bright side, as far as learning goes, you are confrontedwith some underlying details, and can get a better grasp at them.

Exercise

1. Write a program to find out what is the size of int on your computer.

2. Modify sumOfSquares so that it accepts rational numbers as parameters.

2.8 Arithmetic operators

2.8.1 Standard operators

Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standardarithmetic operators (+, -, *, /) apply to integer and floating-point.

+ sum integers, floats, complex values, strings- difference integers, floats, complex values

* product integers, floats, complex values/ quotient integers, floats, complex values% remainder integers

You are familiar with the standard arithmetic operators. However the remainder operator may be new to you. If wedivide 7 by 2, the result will be 3, and the remainder will be 1, because 7 == 2*3 + 1.

7 / 23

7 % 21

It gets tricky with negative numbers; refer to the Go documentation and experiment if you want to know more.

Hint: A number a is divisible by b if the reminder to of division a/b is 0.

2.8.2 Concatenation

The + also applies to strings: "Hello, " + "world!".

Exercises

1. Write a function isDivible that takes two numbers as arguments and checks if the first is divisible by thesecond.

2. Write a function concatenate that returns the result of the concatenation of two strings passed as arguments.

12 Chapter 2. Basics

Introduction to programming with Go

2.8.3 Bitwise logical operators

& bitwise AND integers| bitwise OR integers^ bitwise XOR integers&^ bit clear (AND NOT) integers

Although the operators work on integers, let’s first work on single bits. Let’s have two of them: p and q.

• p&q is true if both p and q are true. It is false otherwise.

• p|q is true if either p or q is true, or both are true. It is false otherwise.

• p|q is true if either p or q is true, but not both. It is false otherwise.

• p&^q is true if p is true and q is not. It is false otherwise.

To put it differently, given two possible values 1 and 0 for each, we can construct the following table or truth:

p q p&q p|q p^q p&^q1 1 1 1 0 00 1 0 1 1 01 0 0 1 1 10 0 0 0 0 0

Now that you understand how binary operators work on single bits, it is easy to see how they operate on integers onceyou represent the integer in their binary form.

Given 12 and 10, represented in binary, one above the other:

1 1 0 01 0 1 0

We can perform the bitwise operations on each pair of bits taken vertically. We will say that two bits “have the sameplace” if they have the same significance or weight, that is, if they both are on the same vertical imaginary line.

Bitwise AND &will look for bits with the same significance (same place) only to keep bits that are “1” in both integers:

1 1 0 01 0 1 0

& 1 0 0 0

The binary result 1000 is 8 in decimal.

Bitwise OR will keep bits that are “1” at the same place in either integers, or in both:

1 1 0 01 0 1 0

| 1 1 1 0

The binary result 1110 is 14 in decimal.

Bitwise XOR will keep bits that are “1” at the same place in either integers, but not in both:

1 1 0 01 0 1 0

^ 0 1 1 0

Bitwise AND NOT will keep bits that are “1” for the first bit and “0” for the second:

2.8. Arithmetic operators 13

Introduction to programming with Go

1 1 0 01 0 1 0

^ 0 1 0 0

2.8.4 Shift

<< left shift integer << unsigned integer>> right shift integer >> unsigned integer

This is simply a matter of moving bits to the left (<<) or to the right (>>) as specified number of times.

5<<2 means:

• Take the binary representation of 5: 101

• Move the bits to the left, two times, filling with 0 on the right as you go: 10100

• The result is 10100 in binary or 20 in decimal.

5>>2 means:

• Take the binary representation of 5: 101

• Move the bits to the right, two times, dropping the bit on the right each time: 10 then 1

• The result is 1 in binary or 1 in decimal.

Hint: An interresting property appears:

• Everytime you shift left, you multiply by 2.

• Everytime you shift right, you divide by 2.

Attempting to shift a negative number of times results in an error.

Exercises

3. What is the quotient of the division of 22 by 4? And the reminder?

4. Find two numbers greater than zero p and q so that p&q is 0.

5. Find q so that given any p, p&q is 0.

6. Multiply 10 by 4 without using any standard arithmetic operator.

2.9 Expressions

2.9.1 Examples

An easy way to start programming is to have a look at expressions. "Hello, playground" we used in out firstprogram is an expression. 42 is another one. 3+4 a third. The basic arithmetic operations can be performed with +, -,* and /. You can perform complex operations, with parenthesis if you need. The * and / operators take precendenceover + and -.

We can go further. All of the following are expressions:

14 Chapter 2. Basics

Introduction to programming with Go

sumOfSquare(2, 3)sumOfSquare(2 * 4, 3 * 2)sumOfSquare(sumOfSquare(2, 3), 3 * 2)7 + 242

2.9.2 Definitions

Expressions in Go are formally defined in the language specification. In the expression 7+2, + is the operand. 7 and2 are its operators. The operator’s arity is 2.

Expression: An expression specifies the computation of a value by applying operators and functions to operands.

Operand: Operands denote the elementary values in an expression.

Binary operator: An operator that applies to two operands. Its artity is 2.

Unary operator: An operator that applies to one operand. The expression -7 has an operator - (negate) with oneoperand 7. Its arity is 1. In this case, the operator - (of arity 1) is not to be confused with the minus mathematicaloperator (of arity 2).

2.9.3 Evaluation

Let’s take a non trivial yet simple case and evaluate 3+4. The expression is evaluated to 7 after the following steps:

3 + 47

Our sumOfSquare with two integers will be evaluated as follows:

sumOfSquare(2, 3)(2 * 2) + (3 * 3)4 + (3 * 3)4 + 913

Note: Notice the use of parenthesis. Although mathematically not mandatory in this case, as multiplation takesprecendence over addition, they denote a group to be evaluated regardless of arithmetic precedence considerations.

In order to evaluate sumOfSquare(2 * 4, 3 * 2), 2*4 and 3*2 must be evaluated first. Then we fall back onthe case of sumOfSquare with two integers as parameters we already know.

sumOfSquare(2 * 4, 3 * 2)sumOfSquare(8, 3 * 2)sumOfSquare(8, 6)(8 * 8) + (6 * 6)64 + 36100

Expression evaluation will come in handy at a later stage, when we examine the complexity of an algorithm.

2.9. Expressions 15

Introduction to programming with Go

2.9.4 Boolean expressions

A Boolean expression is a logical statement that is either true or false.

The expression 3 < 5 is evaluated as true while 2 < 0 is evaluated as false. You can assign a boolean expres-sion to a variable, for instance a = 3 < 5. In that case, a will be evaluated to true.

You can test two values for equality with == as in 3 == 3 which of course is true. The double equal is used toavoid confusion with a variable assignment, as in a = 3. Which can be confusing in itself. Actually, most languagesuse == to perform equality comparisons, for historical reasons. If you want to check that two expressions are different,as in “”, you would use !=, like so: 3 != 5 which evaluates to true as 3 is not equal to 5.

Math Go Meaning> > Greater than

>= Greater or equal< < Less than

<= Less than or equal= == Equal

!= Not equal

Although it would be pretty useless, for illustration purpose, we can write a function isGreaterThan that checksif its first argument is greater than its second.

func isGreaterThan(a int, b int) bool {return a > b

}

a := 3

With for instance the following evaluation steps:

isGreaterThan(5, 3)5 > 3true

2.10 Logical operators

2.10.1 Conjonction (AND)

A boolean expression does not have to live in isolation. Consider we want to answer the following question:

Is 2 less than 5 and is 3 less than 10?

The answer would be “yes”, or true, because indeed, 2 is less than 5, and 3 is less than 10 ; both are true.

That would translate into the following expression with its evaluation:

2 < 5 and 3 < 10true and truetrue

Suppose we would slightly change our initial question to become:

Is 2 greater than 5 and is 3 less than 10?

16 Chapter 2. Basics

Introduction to programming with Go

You can convience yourself the answer is “no” or false, because although 3 is still less than 10, 2 is not greater than5. The first condition fails, so the whole statement no longer holds. If we decompose, we would have:

2 > 5 and 3 < 10false and truefalse

Go does not use “and” as we do in English. Instead, it has an operator &&. Again, this is for historical reason and itfeels very natural to seasoned programmers. Some languages simply use and, that becomes a reserved keyword.

Given “p and q”, written p && q in Go or 𝑝∧ 𝑞 in logic notation, with two boolean possibilies true and false forp and for q, we can construct the following table of truth for the and logical operation (noted ):

p q p qtrue true truetrue false falsefalse true falsefalse false false

2.10.2 Disjunction (OR)

Replace “and” seen above by “or”, we want to test if any of two expressions is true - or if both a true at the same time.We want to test “p or q”, written p || q in Go or 𝑝 ∨ 𝑞 in logic notation. What does our table of truth become?

p q p qtrue true truetrue false truefalse true truefalse false false

There is only one case where p || q is false: when both p and q are false.

2.10.3 Negation (NOT)

Also called logical complement, it is an operation that takes a proposition p to another proposition “not p”, written ¬𝑝in logic or !p in Go.

Intuitively, it turns true into false and vice-versa.

p ¬ptrue falsefalse true

2.10.4 Shortcut evaluation

In logic and in math, the “AND” and the “OR” operations are commutative, meaning you can swap p and q in ourexamples.

𝑝 ∧ 𝑞 ≡ 𝑞 ∧ 𝑝

𝑝 ∨ 𝑞 ≡ 𝑞 ∨ 𝑝

2.10. Logical operators 17

Introduction to programming with Go

In Go, as in many programming languages, logical expressions are evaluated left to right and are tested for possible“short-circuit” evaluation using the following rules:

• (some falsy expression) && expr is short-circuit evaluated to the falsy expression

• (some truthy expression) || expr is short-circuit evaluated to the truthy expression.

Short circuit means that expr above is not evaluated. Therefore any side effect they might have will not occur.Suppose we want to evaluate an expressions using a new function divide:

func divide(a, b int) int {return a / b

}

fmt.Println((divide(5, 2) == 10) && (divide(5, 1) == 10))

Actually, Go will only evaluate the left part (divide(5, 2) == 10) to false. Remember our table of truth: ifany part of the && expression is false, the resulting evaluation for the whole expression is bound to be false aswell. So why waste CPU cycles to evaluate (3 < 10), as any other outcome is out of sight?

Exercise

1. Modify the function divide to prove only the left expression was evaluated.

Consider this revisited example:

fmt.Println((divide(5, 2) == 10) && (divide(5, 0) == 10))

The expression will evaluate to false, although there is an error waiting to happen on the right side. Now turn theexpression around:

fmt.Println((divide(5, 0) == 10) && (divide(5, 2) == 10))

This will produce an error!

panic: runtime error: integer divide by zero

Because of shortcut evaluation, the two expressions p&&q and q&&p are therefore not equivalent in Go if p or qproduces a side-effect, such as generating an runtime error. The same mechanism occurs with ||. This time, if theleft side is true, Go will be satisfied and won’t bother evaluating the right side.

Hint: This mechanism is certainly not limited to Go. You will encounter it in most if not all commonly usedprogramming languages.

2.11 Practical case - Leap year

In the Gregorian calendar (used in most parts of the world), a leap year is a year containing one additional day addedto keep the calendar year synchronized with the astronomical.

It is also referred to as an intercalary year or a bissextile year. A leap day (February 29) is added to the calendar in leapyears as a corrective measure, because the Earth does not orbit the sun in precisely 365 days. The rule to determine ifa given year is a leap year is the following:

18 Chapter 2. Basics

Introduction to programming with Go

Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100,but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700,1800, and 1900 are not leap years, but the years 1600 and 2000 are.

Take a moment to write down some pseudocode to determine if a year is a leap year or a common year. What wouldcheck for first? How would you construct your if/else blocks?

• if (year is not divisible by 4) then (it is a common year)

• else if (year is not divisible by 100) then (it is a leap year)

• else if (year is not divisible by 400) then (it is a common year)

• else (it is a leap year)

Now think about significant examples and convince yourself that this algorithm yields the correct output for each ofyour input by evaluating all applicable condition. What years would you take? Testing with 2021 for the “not divisibleby 4” case is a fine example. Testing again with 2023 would not bring anything more as you would be testing thesame case (“not divisible by 4”). A good test set covers all cases exactly once.

For instance, a function isLeapYear(year: int) bool could be tested with the following values:

Branch to test Example Expected output(year is not divisible by 4) 2021 false(year is not divisible by 100) 2020 true(year is not divisible by 400) 2100 false(it is a leap year) (end) 2000 true

The algorithm could be translated to the following Go function:

func isLeapYear(year int) bool {if year%4 != 0 {

return false} else if year%100 != 0 {

return true} else if year%400 != 0 {

return false} else {

return true}

}

Experiment with the test values proposed above, or with your own.

Rather than using conditional statements, we could rely on the concepts explained in the Logical operators to write aboolean expression. The rules to determine a leap year can be rephrased as follows:

A year is a leap year if it is a multiple of 4 and not multiple of 100, or if it is a multiple of 400.

Notice the punctuation and translate it correctly using the logical operators AND and OR:

For the year to be leap, it is either (a multiple of 4 and not multiple of 100) or it is (a multiple of 400).

That can be translated into Go:

func isLeapYear(year int) bool {return (year%4 == 0 && year%100 != 0) || (year%400 == 0)

}

Extra parenthesis were used to highlight the scope of the logical operators.

2.11. Practical case - Leap year 19

Introduction to programming with Go

Which version should you prefer? Probably the most readable one. Which one is the most readable? It is debatable.For such short code, a seasoned programmer may find the second version more compact and easy to read. Beyonda few conditions, it may become difficult or counterproductive to avoid the conditioanl if/else statements just for thesake of it. As in many things about programming, you will learn that there are often no clear-cut answers when itcomes to design and style questions. In any case, try to remain consistant and to follow the language idioms when theyapply.

Trivia

1. The Earth rotation speed around its axis is not constant. It is also globally slowing down.

2. The Gregorian calendar was adopted by Pope Gregory XIII in 1582 and correct inaccuraries found in the Juliancalendat

3. The current rules lead to 1 day deviation per 3,030 years, which is in practice reduced to 1 day per 7,700 yearsdue to the precession of the equinoxes.

4. Occasionally, a leap second is introduced and stretches the length of the day. These unconspicuous changes arenot introduced according to a predetermined schedule as the variability of our planet’s rotation is not entierlypredictable.

2.12 Read user input

User input can be read from the keyboard using functions from the os and the bufio packages.

package main

import ("bufio""fmt""os"

)

func main() {fmt.Print("What is your name? ")reader := bufio.NewReader(os.Stdin)text, _ := reader.ReadString('\n')fmt.Println("Hello " + text)

}

os.Stdin represents the standard input, namely the keyboard (or equivalent device), if stdin has not been redi-rected from another source. The mechanism is to obtain a “reader” from “stdin”, and from that reader, we read a string.A string is terminated by a new line represented by \n (the user presses Enter). ReadString may return an errorbut for the sake of simplicity we simply discard it with _.

Warning:

1. The code above won’t work from the playground. The program will run but it will not accept any user input.

2. It is not a good practice to simply ignore possible errors. However, in the context of this book, we can agreeit is acceptable.

There are other ways to read user input; the following relies on fmt.

20 Chapter 2. Basics

Introduction to programming with Go

package main

import ("fmt"

)

func main() {fmt.Print("What is your name? ")var name stringfmt.Scan(&name)fmt.Println("Hello " + name)

}

The first method is more flexible and robust, the second is more straightforward and lenient. For instance, try to readan int but type in some letters instead and find out what happens.

Hint: Make sure you acquire good habits by not mixing user input, computation and printing in the same function.For instance, if we want to read two numbers, compute their sum, then display the sum, we should not read the userinput or print the output in the function that computes the sum.

This advice applies even if the logic is trivial, as it it is often the case in this chapter where we deal with the basics.

package main

import ("fmt"

)

func computeSurface(width, height int) int {return width * height

}

func main() {fmt.Print("Enter width ")var width intfmt.Scan(&width)fmt.Print("Enter height ")var height intfmt.Scan(&height)surface := computeSurface(width, height)fmt.Printf("Surface: %d\n", surface)

}

2.13 String formatting

Package fmt offers formatting functions analogous to C’s printf and scanf. The format “verbs” are derived fromC’. Suppose you execute the following code:

fmt.Println(1.0/3)

The output would be:

2.13. String formatting 21

Introduction to programming with Go

0.3333333333333333

It is convenient to be able to round to a certain number of digits, while keeping the original arbitrary precision of thevariable. This can be achieved as follows:

fmt.Printf("%.2f", 1.0/3)

Which would display:

0.33

Notice we’ve used Printf (print formatted) instead of Println (print line). There is something missing however.If we were to attempt to print the value of a second expression, it would be displayed on the same line as the first. Weneed to explicitely add a new line at the end, with the \n symbol.

fmt.Printf("%.2f\n", 1.0/3)

Go offers a large amount of formatting options as you can read in the documentation.

Exercises

1. Write a program that reads two numbers entered from the keyboard, than display them and their sums, with 4decimals, by aligning these numbers as when performing a written addition.

2. Write a program that reads an amount VAT excluded and displays it VAT included. We set the VAT to 20%.Output must be rounded to 2 decimals.

23.4500+ 3.4345---------

26.8845

2.14 Random

Random numbers can be generated using package fmt. It is a pseudo-random number generators, because numbersare not actually purely random, they come from a predetermined sequence.

Here is a example that generate a “random” number. Run this program several times. Since the seed is always thesame (42), it is little surprise that the generated random number will always be the same.

package main

import ("fmt""math/rand"

)

func main() {rand.Seed(42)fmt.Println(rand.Intn(10))

}

This is not very useful. In practice, you will need to set the seed to something different, that changes over time. Thesystem time would be a perfect candidate. Replace the seed with the following (import the "time" package):

22 Chapter 2. Basics

Introduction to programming with Go

rand.Seed(time.Now().UnixNano())

This time, you will get different results after several executions (if you get the same result 5 times in a row, considerchanging your computer).

Now what if you want to get random numbers not from 0 to n, but from m to n? You simply generate a random numberfrom 0 to m-n then add m to the result.

package main

import ("fmt""math/rand""time"

)

func random(min int, max int) int {return rand.Intn(max-min) + min

}

func main() {rand.Seed(time.Now().UnixNano())fmt.Println(random(10, 20))

}

This little routing will come in handy to solve some of the exercises to come.

Hint: Generating true random numbers is not as easy as it seems. Sensitive systems may rely on dedicated hardwareon top of the “crypto/rand” package.

2.15 Exercices

2.15.1 Part 1

The following summary exercices combines a bit of everything we have seen so far: functions, variables, expressions,conditional statements and loops. You don’t have to resolve all of them if you don’t feel like it; however you areadvised to a least test your skills on some of them, and be confident you could solve the others as well.

To avoid recompiling your program over and over, some data will be introduced on the keyboard.

1. Write a function that computes the division of two numbers. Call this function from a program that displays theresult with 4 digit precision.

2. Write a function that computes the surface of a rectangle, given its width and height. Write a second function thatcomputes its permiter. Write a program that reads a rectangle dimensions and prints its surface and perimeter.

3. Write a program that reads two numbers and displays their sum and the product. The output must be to the 4thdecimal. The program terminates when the user enters 0 as the first number.

4. Write a function that takes two numbers as arguments and checks that one and only one of the following condi-tions is met: (i) one of the two numbers is divisible by the other; (ii) their product is greater than 10 times theirsum.

5. Write a program that reads 3 numbers representing the dimensions of a triangle and that checks if: (i) thistriangle is possible; (ii) it is equilateral; (iii) it is isoscele; (iv) it is a rectangle triangle.

2.15. Exercices 23

Introduction to programming with Go

6. Write a function that takes 3 numeric arguments and checks exactly two of them are equal.

7. Write a function that takes 3 integer arguments and checks that 2 of them are equal, a third (any of them) beingstrictly greater than the two equal, and a fourth being strictly smaller than the two equal.

4 6 6 9 true5 4 5 7 true8 4 1 4 true1 2 4 5 false3 3 4 4 false3 3 3 4 false2 7 7 4 false9 7 7 8 false

2.15.2 Part 2

Exercising is good for learning. The problems in this section are of equal difficulty than the previous one. They arehowever a bit more involved. A bit more fun too.

Do not be discouraged if a single exercise takes you one hour to complete, or if you can’t achieve it the first time. Thisis perfectly normal for beginners. Keep trying and keep practicing!

1. Read integer numbers from the keyboard. We stop when the user enters 0. The program displays the sum ofpositive numbers, and the sum of negative numbers.

2. Each student has taken several tests. Write a program that reads their scores (the input stops when a negativenumber is entered), and displays: (i) the average score (ii) the highest score (iii) the lowest score (iv) the averageof all scores after you have eliminated the highest and the lowest score.

3. Write a function that takes an integer n as a parameter, and prints successively the following patterns. Theexamples shown below are given for n equal to 4. You must work line by line, as a printer would.

a) **** b) * c) **** d) ******** ** *** ******* *** ** ****** **** * *

4. Write a function that takes two integers n and lines as parameters, and prints the following triangles:

For n=7 and lines=4

78 9 8

9 0 1 0 90 1 2 3 2 1 0

For n=2 and lines=5

23 4 3

4 5 6 5 45 6 7 8 7 6 5

6 7 8 9 0 9 8 7 6

5. Write a program that reads a positive integer from the keyboard, and displays the a triangle (example given ifthe input is 4):

24 Chapter 2. Basics

Introduction to programming with Go

** *

* ********

6. Write a program that reads a sequence of integers from the keyboard, and that computes the number of raisesand decreases. The input stop when 0 is entered. Example: 4 6 8 2 -1 -5 0 will count 2 raises and 3decreases. 4 6 4 8 9 0 will yield 3 and 1 respectively.

2.15.3 Multiplation table training

1. Write a program that chooses randomly two integers from 1 to 10 included, and ask the user to find their product.If the user makes a mistake, the program should display an error message and ask the same problem again, untilthe right answer is provided (at that time the program will terminate).

2. Modify the program so that it proposes a new multiplication after the previous one is done.

3. Extend the program to give at most 3 attempts for a given question. If the user has not found the right answerafter 3 attempts, the program gives it out and proceeds with the next question.

4. Make the program stop when the user gives a total of 5 correct answers. Do not forget to congratulate them ontheir progress.

5. Same as above, but an answer is valid only if given on the first try.

6. Same as above, but this time the user has to provide 5 correct answers in a row for the program to terminate.

Trivia

Small programs like the one above were the rage in the 1980’s. They were actually used by younger students to helpthem memorize their tables. A modern variant could be in the form of a smartphone application, and would probablyinclude some fancy visual effects. The basic logic however remains unchanged though the years.

2.16 Vocabulary

While we said we would take a practical approach, a bit a vocabulary won’t hurt as long as it has a practical context.

2.16.1 Computer programming

Computer programming is the process of designing and building an executable computer program for accomplishinga specific computing task [Wikipedia]. This task can be printing a report, sending an email, operating a robotic arm,cheating on emission testing, or displaying pixels on a screen.

Programs are written once, but read many times. They are meant to be read by humans first, and by machinesincidentally. In order to create programs readable by others (or simply by yourself later), we follow certain principlesto ensure readability. In large projets, cruft is easily accumulated, one line at the time, leading to systems that arefragile, difficult to maintain and to reason about.

2.16.2 Bugs

An error in a program is called a bug. It has been estimated that a bug appears every 1,000 lines of code [TAN]. Noall of them have the same severity. Its manifestation can be a slightly unclear message on the screen, or an abrupt

2.16. Vocabulary 25

Introduction to programming with Go

termination (abnormal end or abend; a crash if you prefer). The consequences of a bug can vary greatly, from mildannoyance like a missing extra topping on a pizza, to loss of business and money, for instance with the failure tocomplete a shopping transaction, or the explosion of a rocket. In the worst cases, is can result in human harm. Wemust write programs and design systems to be confident that they will perform correctly, according to the context.

2.16.3 Algorithm and data structures

An algorithm is a computable set of steps to achieve a desired result [NIST]. A data structure is a way to organizeinformation. Algorithms and data structures for programs.

2.16.4 Software and application

Program, softwware and application are often used interchangeably. To make a distinction, software is an all-encompassing term that covers programs and is used in constrast with hardware. An application is a program orgroup of programs that is designed for the user.

There, now we have common ground.

26 Chapter 2. Basics

CHAPTER

THREE

APPENDIX

3.1 About Go

Before we roll up our sleeves, it is important to understand why the language was developed. Let’s have a look at aquote [video] from Rob Pike, one of its creators.

“Go was conceived as an answer to some of the problems we were seeing developing software infrastruc-ture at Google. The computing landscape today is almost unrelated to the environment in the languagesbeing used, mostly C++, Java and Python have been created. The problems introduced by multicore pro-cessors, networked systems, massive computation clusters,. and the web programming model were beingworked around rather than addressed head-on.”

“Moreover, the scale has changed: todays’s server programs comprise ten of millions of lines of code, areworked on by hundred or even thousands of programmers, and are updated literally every day. To makematters worse, build time has stretched to many minutes, even hours, on large compilation clusters”.

The language creators all have major contributions to our field. Robert Griesemer has worked on the V8 Javascriptengine and on the Java VM hotspot; Rob Pike was on the Unix team at the Bell Lab and created the first windowingsystem for Unix; Ken Thompson designed and implemented the original Unix. Thompson and Pike the co-creator ofUTF-8.

Go has a very simple programming model, something that can fill in your head easily. They purposefully avoidedcomplex and advanced constructions. You won’t find generics, or even exceptions, which can be surprising at first.They have a “less is better” approach and they are extremely wary not to add unnecessary features to the language. Asa result, you can learn Go in only a few days.

Some of the cruft accumulated by older languages has been removed, like the need to specifically declare a data type,although a modern compiler should not need you to do so. Back then Java did not have type inference for instance,although now it does.

Go also strongly favours certain idioms, and that’s an excellent thing to achieve higher consistency, and to avoidendless debates on personal tastes. As a result, most of the Go projects look pretty alike code-wise. You just do it theGo way so to speak.

It is a garbage collected language, so you don’t have manage memory allocation yourself.

It is built with concurrent programming in mind, with its coroutines and channels. Testing also has a key place withdedicated constructs.

Go compiles to native code and allows cross compilation to another platform. So for instance one can create aexecutable for GNU/Linux on a macOS machine.

The tools are well designed and well thought, and so is the language. There is a decent amount of libraries available.The memory footprint is extremely small. It is very capable to handle heavy loads.

27

Introduction to programming with Go

It was designed to run “server-side” programs, and although solutions exist to plug Go to a GUI, its sweet spot is onthe server side. Youtube relies amongst other things on a Google project called vitess, and vitess is written in Go. Thisgives you an indication that Go delivers.

From a career perspective, Go is also is a sensible choice.

3.2 Credits

• Hard drive, Wikipedia, licensed under CC BY-SA 3.0.

• Gopher mascot by Takuya Ueda, licensed under the Creative Commons 3.0 Attributions license.

28 Chapter 3. Appendix

CHAPTER

FOUR

INDICES AND TABLES

• genindex

• modindex

• search

29