left-icon

Scala Succinctly®
by Chris Rose

Previous
Chapter

of
A
A
A

CHAPTER 4

Control Structures

Control Structures


Control structures are used to allow certain sections of code to loop or to be executed based on a condition. Programs are normally executed one line at a time from top to bottom. Control structures allow us to change this execution order.

Looking at control structures requires us to first consider arrays, lists, and other data types where control structures are used. I will present control structures first, then collections, but I will be using collections in the code for both chapters.

“If” statements

“If” statements in Scala are similar to those of other C-based languages. We can use “if, else-if, and else” blocks in order to route our code execution based on conditions. One interesting point about Scala “if” statements is that they evaluate to something; in other words, they return a value. We will see this mechanism in a moment, but for now, Code Listing 21 shows a basic example of an If/Else If/Else block.

Code Listing 21: Basic If Blocks

object MainObject {

      def main(args: Array[String]): Unit = {

      

            def x = 100

            def y = 200

            

            if(x < y)

                  println(x + " is smaller than " + y)

                  

            else if(x > y) { // Can use { } for multiple lines of code!

                  println(x + " is greater than " + y)

            }

            

            else // Can finish if/else if with a final else:

                  println(x + " is equal to " + y)

      }

}

“If” conditions work the same way as Java. We begin with an “if,” followed by any number of “else-if” conditions, and end with an optional “else” block. Only a single “if” or “else-if” code block will execute, and if none of the previous “if” or “else-if” blocks execute, the “else” block (if supplied) will execute. We supply a condition after the keyword “if,” then supply a code block for the program to execute when the condition is true. After the “if,” we can supply any number of “else-if” blocks, each with its own condition and code block. Finally, we supply an “else” block at the end.

Code Listing 22 shows an interesting difference between Scala and Java—“if” statements, and indeed the entire If/Else block, actually evaluate to a val. This is particularly important when we are filtering lists using foreach (which we will look at shortly). In Code Listing 22, 100 is less than 200, so the val called resultFromIf will be set to -1.

Code Listing 22: If Blocks Evaluate to Values

object MainObject {

      def main(args: Array[String]): Unit = {

            def x = 100

            def y = 200

            

            // Creating a val from an if block return:

            val resultFromIf = {

                  if(x < y) -1

                  else if(x > y) 1

                  else 0

            }

            println("The result is: " + resultFromIf)

      }

}

For loops

For loops are used to execute a section of code for some specified number of times. Code Listing 23 counts to 10 using a for loop.

Code Listing 23: Counting with a For Loop

object MainObject {

      def main(args: Array[String]) {

            for(i <- 1 to 10) {

                  println(i)

            }

      }

}

The basic syntax for a for loop is for(variable <- range), where range specifies the range over which the for loop iterates. The variable, i in Code Listing 23, is set to 1 first, then the body of the for loop executes. Next, the variable is set to 2, then 3, etc. Each time the variable is incremented, the loop body runs and a new number is printed to the console using println.

The range of the loop is specified as (i <- 1 to 10), which means the variable i will become consecutive whole numbers (Int) from 1 all the way up to and including 10.

We can also iterate through collections using for loops. We will look at this when we cover collection types.

It is important to note that Scala uses different scoping rules than C++ and other languages. If we have a variable called myVariable and we implement a for loop with a counter with the same name, the counter variable will not be the same as the variable defined outside the for loop. Code Listing 24 shows an example of this behavior. The outer myVariable in Code Listing 24 will not change, and the variable that iterates through the loop has the same name, but otherwise it is completely distinct.

Code Listing 24: Variable Scope in For Loops

object MainObject {

def main(args: Array[String]): Unit = {

            var myVariable: Int = 0

            

            // For loop with counter:

            for(myVariable <- 1 to 10) {

                  // Output the for loop's myVariable:

                  println("Value of counter: " + myVariable)

            }

            

            // Output the value of the original myVariable.

            println("Value of local myVariable: " + myVariable)

      

      }

}

until vs. to

In a for loop, if we use the until keyword when specifying our range, Scala will count up to, but not include, the second number. If we use the to keyword, the second number will be included. For example, Code Listing 25 shows exactly the same example as Code Listing 24, except that we have used until in place of to. In Code Listing 24, the loop counts to and includes 10, but when we use until, the program counts up to but does not include the 10. The use of until rather than to is common for iterating through Array and other collection elements because the indices of these collection types are 0-based and a collection with five items would have items numbered 0 until 5 or (0, 1, 2, 3 and 4).

Code Listing 25: Counting to (but Not Including) 10

object MainObject {

      def main(args: Array[String]) {

            // Counts: 1, 2, 3, 4, 5, 6, 7, 8, 9

            for(i <- 1 until 10) {

                  println(i)

            }

      }

}

Multiple range for loops

We can supply multiple ranges for a for loop by separating each range in the for loop with a semicolon. In this case, the right-most variable counts up first, then the next to the left increments, and the right-most counts up again. In this way, the program will iterate through all permutations of the ranges. Code Listing 26 shows a simple example of using two ranges.

Code Listing 26: Using Multiple Ranges

object MainObject {

      def main(args: Array[String]) {

            // Two ranges for loop:

            //    variable called num will count 1 to 10,

            //    while den is set to 1. Then den will

            //    increment, and num will count 1 to 10

            //    again, etc.

            for(den <- 1 to 10; num <- 1 to 10){

                  println(num + " divided by " + den + " is " +

                        (num.toDouble / den.toDouble));

            

            }

      }

}

Code Listing 26 sets the variable den to 1, then it also sets the variable num to 1. It next executes the loop body 10 times, incrementing num each time and printing the results of num/den. When num has reached 10, the loop starts again, but den is incremented. The program will print 100 lines of output to the screen, and it will divide all combinations of the numbers from 1 to 10, starting from 1/1 and working all the way to 10/10.

Tip: Multiple range for loops are particularly important for iterating through multidimensional arrays. (We will look at multidimensional arrays later.) But the method for iterating through a multidimensional array is to set each range of a multiple range for loop to count through a dimension of the array. So, if we have an array with three dimensions with the sizes of the dimensions being 7, 8, and 9, we could use for (x <- 0 until 7; y <- 0 until 8; z <- 0 until 9).

For loop filters

We can add a condition to the ranges we count in a for loop. This will cause the body of the loop to be executed only when the condition is true. Code Listing 27 shows an example of a for loop with a condition—that the program prints out the even numbers between 1 and 100. Using the output from an “if” statement leads to this example.

Code Listing 27: For Loop Conditions

object MainObject {

      def main(args: Array[String]) {

            

            // Print the even numbers using a filter:

            for(i <- 1 to 100 if i % 2 == 0) {

                  println(i)

            }

      

            // Print even numbers without using a filter:

            for(i <- 1 to 50) {

                  println(i * 2)

            }

      }

}

Code Listing 27 contains two examples of how to print the even numbers from 1 to 100. Notice the “if” statement in the middle of the first for loop. If i % 2 == 0, then i is even, and therefore this loop will filter out all the odd numbers. In the second example, we count from 1 to 50 and double the result. This will give the same output and will probably be faster to execute. In this particular example, the second method is preferable because we are iterating through the loop half as many times but we can also use for loop filters when iterating through lists of elements. In the case of iterating through list elements, we would not be able to use the second method to print only the even numbers from the list. We will look at lists again later, but Code Listing 28 shows an example of iterating through a list and filtering out the even numbers.

Code Listing 28: For Loop Filtering List Elements

object MainObject {

      def main(args: Array[String]) {

            

            // Define a list:

            val myNumbers: List[Int] = List(

                  2, 6, 1, 7, 4       

            )

      

            // Filter the even numbers:

            for(i <- myNumbers if(i % 2 == 0)) {

                        println(i + " is even!")

            }           

      }

}

While loops

While loops are used to execute a block of code repeatedly until a certain Boolean condition is false. The syntax for a while loop in Scala is while(condition), where condition is anything that evaluates to true or false, i.e. Boolean. Each time the program encounters the while loop it will evaluate the condition. If the condition evaluates to true, the program will execute the loop body and repeat the while loop's condition check. If the condition evaluates to false, the program will skip the loop body and continue execution after the while loop. Code Listing 29 is a simple number-guessing game that uses a while loop to repeatedly ask the user for a number until the hidden number is guessed.

Note: We use the import to import the scala.io.StdIn.readInt function, which is supplied as a standard part of Scala’s libraries. In the code, we do not actually use the readLine function, which means the import could have read import scala.io.StdIn.readInt, but I left the readLine as an example of importing multiple functions from the same class.

Tip: The Math.random function generates a pseudorandom Double in the range from 0.0 to 1.0. It never generates the number 1.0 itself, but rather all numbers from 0.0 up to 1.0. In order to generate a random Int in the range from 0 to X (not including X), we can use (Math.random * x).toInt. In order to generate a random number from 1 to X (including X), we can use (Math.random * x).toInt + 1.

Code Listing 29: Guess-the-Number Game

import scala.io.StdIn.{readLine, readInt}

object MainObject {

      def main(args: Array[String]): Unit = {

      

            var userAnswer = 0

            var hiddenNumber = (Math.random * 1000).toInt + 1

            

            println("""I'm thinking of a random

number between 1 and 1000, inclusive.""")

            

            // Repeat the game while the user has not

            // guessed the number:

            while(userAnswer != hiddenNumber) {

                  

                  print("Enter a number: ")

                  userAnswer = readInt            // Read an int from the user

                  

                  // Give the user a hint:

                  if(hiddenNumber < userAnswer)

                        println("Lower")

                  else if(hiddenNumber > userAnswer)

                        println("Higher")

            }

            // The user won, print a message and quit.

            println("Yes, you got it, the hidden number was " +

                  hiddenNumber + "!")

      }

}

The program in Code Listing 29 generates a random number from 1 to 1000, and the user must guess the number. Notice the user of the while loop—we are saying that while the user's number is not identical to the hiddenNumber, the program should loop. When the user guesses the hiddenNumber, the variable userAnswer will equal hiddenNumber and the condition of the while loop (userAnswer != hiddenNumber) will be false. The program will stop executing the loop and begin execution after the body of the loop.

Do while loops

Do while loops are similar to while loops, except that the condition is checked at the end, after the loop’s body. This means that do while loops are guaranteed to execute at least once. Code Listing 30 shows the same game as the while loop example, except that here the hidden number can be negative.

Code Listing 30: More Difficult Version of Guess the Number

import scala.io.StdIn.readInt

object MainObject {

      def main(args: Array[String]) {

      

            var userAnswer = 0

            

            // Select a random number from -1000 to 1000

            var hiddenNumber = (Math.random * 2001).toInt -1000

            

            println("""I'm thinking of a random

number between -1000 and 1000, inclusive.""")

            

            // Do while loops guaranteed to execute at least once!

            do {

            print("Enter a number: ")

            userAnswer = readInt      // Read the user's answer

            

            // Give the user a hint:

            if(hiddenNumber < userAnswer)

                  println("Lower")

            else if(hiddenNumber > userAnswer)

                  println("Higher")

            } while(userAnswer != hiddenNumber)

            

            println("Umm... no. Anyway, I'm tired of playing. See ya!")

      }

}

The basic syntax for a do while loop is do { body } while(condition), where body is the body of the loop and where the condition is some value that evaluates to a Boolean. As with the while loop, the do while will continue to execute until the condition becomes false. Then it will drop below the do while and continue execution after the loop.

The reason a do while loop is better suited to this game is because the program initially sets the userAnswer variable to 0. If we use a while loop for the game’s body and the program happens to randomly select the number 0, the program will assume this 0 is the user’s guess and the user will win the game immediately. With a do while, we are guaranteed that the first value we check against our hidden number is actually the user’s input, not just the default value.

Example programs

For the final part of this chapter, we will examine some slightly longer and more complex programs. While we learn the Scala language, we should note that we can already use the basics of the language to create important and interesting programs. The following programs are intended for use in the study of number theory, a field that deals primarily with the characteristics and patterns of whole numbers. These programs are definitely not designed optimally, and there are well-known algorithms that work much faster than those presented here, but these programs are useful for studying prime numbers and patterns with small integers.

Testing if a number is prime

The example in Code Listing 31 contains a simple, brute-force method for testing if a number is prime. The program runs through the integers 1 to 100 using a for loop and prints to the console true or false depending on whether or not the number is prime.

Code Listing 31: Testing Primality 

object MainObject {

      def isPrime(j: Int): Boolean = {

            

            // Base cases:

            if(j < 2) return false

            else if(j == 2 || j == 3) return true

            else if(j % 2 == 0) return false

            

            // Find the highest number we need to check

            var sqrt = Math.sqrt(j)

            

            // First composite to test

            var factor = 3

            

            while(factor <= sqrt) {

                  // If j is divisible by the factor

                  if(j % factor == 0)

                        // Return false

                        return false

                  

                  // Move factor up to the next odd number

                  factor += 2

            }

            

            // If j is not divisible by any factor up to

            // the square root of j, then j is prime!

            return true

            }

      

            def main(args: Array[String]): Unit = {

                  var i = 0

                  for(i <- 1 to 100) {

                        println(i + ": " + isPrime(i))

            }

      }

}

The Goldbach conjecture

The second sample program is designed for use in the study of a famous statement made by Christian Goldbach. Goldbach proposed that every even number greater than 2 could be written as the sum of two primes. Although it appears to be a perfectly simple statement, it has never been proven or disproven, and a proof either way would be an extraordinary event in mathematics and computer science.

A Goldbach partition is two primes that sum to a given integer. For instance, for the number 18 (which is even), a Goldbach partition might be 7 and 11—because 7 and 11 are both primes and they sum to 18. If you are able to find an even number greater than 2 that does not have any Goldbach partitions, you have managed to solve the problem and proven Goldbach was incorrect. Likewise, if you are able to discern some infallible reason that Goldbach’s statement is true for all even numbers greater than 2, you have managed to prove Goldbach correct. At this point, even numbers with hundreds of digits have been checked, and every even number has been found to have one or more Goldbach partitions. Most mathematicians believe the conjecture to be true, but nobody has managed to prove without doubt that Goldbach's conjecture is a fact. The following program outputs all of the Goldbach Partitions for a given number. The user can use the input 0 to exit the program.

Code Listing 32: Goldbach Conjecture Partitions

import scala.io.StdIn.{readLine,readInt}

 

object MainObject {

      def isPrime(j: Int): Boolean = {

            // Base cases:

            if(j < 2) return false

            else if(j == 2 || j == 3) return true

            else if(j % 2 == 0) return false

            // Find the highest number we need to check

            var sqrt = Math.sqrt(j)

            

            // First composite to test

            var factor = 3

            

            while(factor <= sqrt) {

                  // If j is divisible by the factor

                  if(j % factor == 0)

                        // Return false

                        return false

                  // Move factor up to the next odd number

                  factor += 2

                  }

            // If j is not divisible by any factor up to

            // the square root of j, then j is prime!

            return true

      }

      

      // Function prints the Goldbach partitions of

      // a given Int

      def goldbachPartitions(j: Int): Unit = {

            println("Goldbach Partitions for " + j)

            var currentPartition = 2

            while(currentPartition <= j/2) {

                  if(isPrime(currentPartition)

                              && isPrime(j - currentPartition))

                        println("Partition: " + currentPartition + " and " +

                        (j - currentPartition))

                  currentPartition += 1

            }

      }

      

      // Main loops until the user inputs 0

      def main(args: Array[String]): Unit = {

            var input = -1

            while(input != 0) {

      input = scala.io.StdIn.readLine("Input Int (use 0 to exit): ").toInt;

                  if(input != 0)

                        goldbachPartitions(input)

                  else

                        println("Bye!")

            }

      }

}

Note: The Goldbach conjecture is just one example of a mathematical problem that we can already explore using the basics of Scala. There are many such problems and questions in mathematics, and the interested reader should look up the list of unsolved problems in mathematics on Wikipedia: https://en.wikipedia.org/wiki/List_of_unsolved_problems_in_mathematics.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.