Learning objectives
In the previous lesson, we learned how Go implements conditionals via if and else statements. In the main text, our next step was to move on to encounter functions like the pseudocode function Factorial()
reproduced below that incorporates a while loop.
Factorial(n) p ← 1 i ← 1 while i ≤ n p ← p · i i ← i + 1 return p
In this lesson, we will see how Go implements while loops.
Set up
Create a new folder called loops
in your go/src
directory, and then create a new file within the go/src/loops
folder called main.go
, which we will edit in this lesson.
In main.go
, let us add the following starter code that is comparable to our starter code in previous lessons.
package main import ( "fmt" ) func main() { fmt.Println("Loops.") }
Code along video
Beneath the video, we provide a detailed summary of the topics and code covered in the code along.
At the bottom of this page, you will have the opportunity to validate your work via auto-graded assessments that evaluate the functions covered in the code along.
Although we strongly suggest completing the code along on your own, you can find completed code from the code along in our course code repository.
Code along summary
Implementing a factorial function with a while loop
First, let’s implement Factorial()
. We first set two variables p
and i
equal to 1. The variable p
will store the product of integers and is what we will eventually return.
//Factorial takes an integer n as input and returns n! = n*(n-1)*(n-2)*...*2*1 func Factorial(n int) int { p := 1 i := 1 //to be continued return p }
We now encounter a quirk of Go, which is that although it offers support for while loops, it does not have a while
keyword. Instead, we will use the keyword for
. (We will see soon that for
is also used to implement for loops.) Each time through the while loop, we update p
to be equal to the product of the current value of p
and i
, then increase i
by one. As with if statements, we place a while loop in brackets, with the final bracket on its own line.
//Factorial takes an integer n as input and returns n! = n*(n-1)*(n-2)*...*2*1 func Factorial(n int) int { p := 1 i := 1 // Go doesn't have a while keyword, and it uses "for" instead for i <= n { p = p*i i = i+1 } return p }
Note that this implementation of Factorial()
has the exact same structure as our pseudocode function reproduced at the start of the lesson. Regardless of what language we use, we should be able to use the same structure of a while loop increasing a variable of i
by 1 until it reaches n
, with a variable p
constantly updated as the product p*i
.
Let us call Factorial()
by appending the following code to func main()
.
func main() { fmt.Println("Loops.") n := 5 m := Factorial(n) fmt.Println(m) }
Open a terminal window, and navigate into the folder containing main.go
by executing cd go/src/loops
. Then compile your code by executing go build
and run your code by executing ./loops
(Mac) or loops.exe
(Windows). You should see 120
printed.
Handling negative inputs
STOP: Add the linefmt.Println(Factorial(-100))
to the end ofmain.go
. Save, compile, and run your code. What is returned? Why?
One way of handling the issues caused by passing a negative input into Factorial()
is to check if a negative value is given as input using an if statement; if so, we could use a panic()
statement, which will print the error message that it is given as input to the console and then immediately end the program.
//Factorial takes an integer n as input and returns n! = n*(n-1)*(n-2)*...*2*1 func Factorial(n int) int { if n < 0 { // let's handle negative input panic("Error: Negative input given to Factorial()!") } p := 1 i := 1 // Go doesn't have a while keyword, and it uses "for" instead for i <= n { p = p*i i = i+1 } return p }
STOP: Save your code with the updatedFactorial()
function, then compile and run your code from the terminal. You will see that now, instead of printing1
whenfmt.Println(Factorial(-100))
is executed, the program exits with the error message given topanic()
above. You can now safely remove the linefmt.Println(Factorial(-100))
frommain.go
.
Note: You may be wondering why we allow the inputFactorial(0)
. 0! is traditionally considered to be equal to 1 because, for any other non-negative n, n! = (n+1)!/(n+1). In this case, when n is 0, 0! = 1!/1, and therefore 0! is equal to 1.
An infinite loop, and a brief musing on programs terminating
Let’s temporarily comment out the line i=i+1
from Factorial()
, as shown below.
//Factorial takes an integer n as input and returns n! = n*(n-1)*(n-2)*...*2*1 func Factorial(n int) int { if n < 0 { // let's handle negative input panic("Error: Negative input given to Factorial()!") } p := 1 i := 1 // Go doesn't have a while keyword, and it uses "for" instead for i <= n { p = p*i //i = i+1 } return p }
Let’s then compile and run our code using the same func main()
as before, reproduced below.
func main() { fmt.Println("Loops.") n := 5 m := Factorial(n) fmt.Println(m) }
When we run this program in the terminal, we see a blinking cursor. The program is running behind the scenes, but it never prints m
because it is stuck in an infinite loop within the call to Factorial(n)
. program never finishes because it is stuck in an infinite loop!
Note: You can terminate a process in a terminal window using the commandControl+C
.
As we start to code larger projects, we will see that it is normal for a function or for a program to take a few minutes, a few hours, or even a few days to finish. This means that it is difficult to discern whether the code is still running or hung in an infinite loop. You might wonder if it is possible to write code to provide a quality assurance check of whether a given program is going to terminate. It turns out that this question lies at the dark heart of computer science, and we will return to it in a later chapter.
Summing the first n integers
Exercise: Write a functionSumFirstNIntegers()
in Go using a while loop that takes an integern
as input and returns the sum of the firstn
positive integers. As always, we suggest starting on the level of planning in pseudocode.
Our SumFirstNIntegers()
function is shown below. Note that the structure of this function is very similar to the structure of Factorial()
. The only difference is that instead of introducing a variable p
to store our growing product (which is declared to be equal to 1 and is updated via the command p=p*i
each time through the loop), we use a variable sum
(which is declared to be equal to 0 and is updated via the command sum=sum+i
each time through the loop). We have also added a panic()
statement to handle negative inputs.
//SumFirstNIntegers takes an integer n as input and returns the sum of the first n positive integers func SumFirstNIntegers(n int) int { if n < 0 { panic("Error: negative integer given to SumFirstNIntegers") } sum := 0 var i int = 1 for i <= n { sum = sum+i i = i+1 } return sum }
Syntax shortcuts for incrementing or updating a variable
We make an aside to introduce a few syntactical shorthands for changing the values of variables. Incrementing a variable, or adding one to it, is so common that language designers have built in a special notation for it. Instead of i = i+1
for incrementing i, we can use the shorthand i++
. We also have the shorthand i--
to denote i = i-1
.
Note: The popular programming language C++ is an homage to this shorthand incrementation syntax, as C++ was designed as the successor of the earlier programming language C.
The update sum = sum+i
is very common as well. We would like to update a variable (sum
) by adding something (the current value of i
) to the variable’s current value. This statement can be written using the shorthand sum += i
.
Using these two syntax shorthand tricks, we can update SumFirstNIntegers()
as follows.
//SumFirstNIntegers takes an integer n as input and returns the sum of the first n positive integers func SumFirstNIntegers(n int) int { if n < 0 { panic("Error: negative integer given to SumFirstNIntegers") } sum := 0 var i int = 1 for i <= n { sum += i // same as sum = sum+i i++ // same as i = i+1 } return sum }
As you might imagine, Go also supports shorthand variable updates corresponding to subtraction, multiplication, and division, as summarized below.
sum -= i
is equivalent tosum = sum - i
;sum /= i
is equivalent tosum = sum / i
;sum *= i
is equivalent tosum = sum * i
.
If all these shorthand variable updates look alien to you, that is fine! They may take some getting used to, and in the meantime there is nothing wrong with writing i = i+1
or sum = sum + i
.
STOP: Use shorthand variable updates to update two lines ofFactorial()
.
Looking ahead to Euclid’s GCD algorithm
Before continuing, we point out that you now know enough about loops in Go to implement Euclid’s GCD algorithm. The pseudocode for EuclidGCD()
is reproduced below if you would like to give it a shot. Otherwise, we will soon return to show how to implement this algorithm after we discuss how to implement for loops.
EuclidGCD(a,b) while a ≠ b if a > b a ← a − b else b ← b − a return a
Check your work from the code along
We provide autograders in the window below (or via a direct link) allowing you to check your work for the following functions:
Factorial()
SumFirstNIntegers()