Learning objectives
Now that we know a bit more about functions, we are ready to take the next steps and see how Go implements different aspects of the control flow that we learned about in the main text on the level of pseudocode.
For example, consider the following pseudocode function Min2()
that we introduced in the main text to find the minimum of two input integers.
Min2(a, b) if a < b return a else return b
In this lesson, we will explore Go’s syntax for if and else statements.
Set up
Create a new folder called conditionals
in your go/src
directory, and then create a new file within the go/src/conditionals
folder called main.go
, which we will edit in this lesson.
At the top of main.go
, type package main
and create a blank func main()
as follows.
package main func main() { }
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
Taking the minimum of two integers
Just as we used brackets to enclose functions, we will use brackets to enclose if statements. As a result, our Min2()
pseudocode function does not change very much when we convert it to Go: we just need a func
keyword, the parameter type declarations and the output type declaration in the function signature, along with brackets enclosing the function and enclosing the if and else block.
//Min2 takes two integers as input and returns their minimum. func Min2(a, b int) int { if a < b { return a } else { return b } }
Note: Pay close attention to Go’s expectation for bracketing syntax with if statements. For example, we need to ensure that the start of the else statement appears on the same line as the end of the if block by using the notation} else {
.
We will now call Min2()
. Add the following code along with Min2()
to main.go
.
package main import "fmt" func main() { fmt.Println("The minimum of 3 and 4 is", Min2(3, 4)) }
From two cases to three: introducing else if
The power of conditionals is contained in their ability to compare variables and branch based on different conditions. In Min2()
, this comparison is done using a < operator to see which value is larger. The following function WhichIsGreater()
returns 0 if two input integers are equal, 1 if the first input integer is larger, and -1 if the second input integer is larger. (Note that this covers all possible cases.) In Go syntax, when we are comparing two integers for equality, we use double equals (==
), as the single equals symbol is reserved for setting the value of a variable. Because it considers three possible cases, WhichIsGreater()
makes use of an else if
statement before it reaches the else
statement.
//WhichIsGreater takes two integers as input. It returns 0 if two input integers are equal, 1 if the first input integer is larger, and -1 if the second input integer is larger. func WhichIsGreater(x, y int) int { if x == y { // == means testing whether two expressions are equal return 0 } else if x > y { return 1 } else { // we can infer x < y return -1 } }
STOP: Inmain.go
, confirm thatWhichIsGreater()
is working by printing out the values ofWhichIsGreater(3, 5)
,WhichIsGreater(42, 42)
, andWhichIsGreater(-2, -7)
infunc main()
.
A function determining if two integers have the same sign
Let’s consider another example. In the following function SameSign()
, we would like to quickly determine if two input integers x
and y
have the same sign (we will assume that 0 has the same sign as all integers). One way that we could implement SameSign()
would be this would be to have three cases:
x
andy
are both greater than or equal to zero (returntrue
);x
andy
are both less than or equal to zero (returntrue
);- the remaining default case, in which
x
andy
must have opposite sign (returnfalse
).
However, if we consider the product x * y
, then this product is always at least equal to zero in cases 1 and 2 and always negative in case 3. As a result, we can simplify our implementation of SameSign()
to have just two cases, as shown below. In this code, we use the operator >=
to check whether the product x*y
is greater than or equal to zero. (Using >=
instead of ≥
is standard practice across languages since it is easier to type.) If this product is indeed greater than or equal to zero, then we enter the if block and return true
; otherwise, we enter the else block and return false
.
//SameSign takes two integers as input. It returns true if the numbers have the same sign and false otherwise, assuming that 0 has the same sign as all integers. func SameSign(x, y int) bool { if x*y >= 0 { // >= is "greater than or equal" return true } else { return false } }
Technically, we don’t need an else block in the above code, because if the condition x*y >= 0
is not true
, then we will continue past the if block, and we know that we can simply return false
. We can therefore simplify SameSign()
a bit further by removing the else block.
//SameSign takes two integers as input. It returns true if the numbers have the same sign and false otherwise, assuming that 0 has the same sign as all integers. func SameSign(x, y int) bool { if x*y >= 0 { // >= is "greater than or equal" return true } // if we make it here, then we know that x * y < 0 return false }
Note: For the precocious, we can optimizeSameSign()
even further by writing only the single linereturn x*y >= 0
. This is because the expressionx*y >= 0
has the valuetrue
when the product ofx
andy
is at least zero, andfalse
otherwise.
A function taking the positive difference between two integers
Our next function, PositiveDifference()
, again takes two integers, and it returns the absolute value of the difference between the two integers. A simple way to write this function is to consider three cases that are analogous to the three cases in WhichIsGreater()
.
//PositiveDifference takes two integers as input and returns the absolute value of the difference between the two integers. func PositiveDifference(a, b int) int { if a == b { return 0 } else if a > b { return a - b } else { // we can infer that a < b return b-a } }
STOP: How could we implementPositiveDifference()
using only two cases?
Variable scope
Now, say that we instead wanted to introduce a variable c
to store the value that we will eventually return at the end of the function in the cases that a
and b
are not equal.
//PositiveDifference takes two integers as input and returns the absolute value of the difference between the two integers. func PositiveDifference(a, b int) int { if a == b { return 0 } else if a > b { var c int = a-b } else { // we can infer that a < b c = b-a } return c }
STOP: After addingPositiveDifference()
tomain.go
, compile your code. What happens? Why?
When we compile this code, Go throws a compiler error with the feedback undefined: c
, referring to the line c = b-a
of PositiveDifference()
. When we declare a variable, it has a scope (lifetime) that means that the variable exists only inside of the “block” in which it is declared. In our code, the variable c only exists within the following block of PositiveDifference()
.
} else if a > b { var c int = a-b }
To save memory, c
is destroyed at the end of this block. As a result, when we refer to c
later on in the function, there is no such thing as c
because it no longer exists.
Perhaps we could fix this issue with the following workaround, redeclaring c
in the following block; we will use a shorthand declaration using :=
.
//PositiveDifference takes two integers as input and returns the absolute value of the difference between the two integers. func PositiveDifference(a, b int) int { if a == b { return 0 } else if a > b { var c int = a-b } else { // we can infer that a < b c := b-a } return c }
However, if we compile this program, then we will get the same compiler error! This time, we have introduced two separate variables named c
, which are both destroyed at the end of their blocks, so that when we attempt to return c
at the end of the function, neither of these variables exists.
The way to fix this issue is to declare c
outside of any if or else block so that its scope is the entire function, outside of any if block or else block. This is handled by the following updated PositiveDifference()
function, which will now compile.
//PositiveDifference takes two integers as input and returns the absolute value of the difference between the two integers. func PositiveDifference(a, b int) int { var c int // has scope of the entire function if a == b { return 0 } else if a > b { c = a-b } else { // we can infer that a < b c = b-a } return c }
Note: In the previous lesson, we learned that for the types we have thus far worked with, Go handles input parameters using pass by value, creating a copy of any variable passed into a function as input. Our discussion of scope now allows us to remark that the scope of a function’s input parameters is the function itself, with the copied input variables destroyed at the end of the function.
Index of comparison operators
Go (as do many other languages) allows a collection of other conditions for comparisons, which we will list below. We will use these as needed throughout the course.
>
: more than<
: less than>=
: greater or equal to<=
: less than or equal to==
: equal to!=
: not equal to!
: “not”. For example, ifx
is a boolean variable, then!x
evaluates to
whentrue
x
is
and vice-versa.false
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:
Min2()
WhichIsGreater()
SameSign()
PositiveDifference()