Chapter 3

Scala Basics

Now that we have explored the tools that we will be using, it is time to put them to use and begin our journey learning how to program with the Scala language. We will spend most of this chapter working in the REPL environment and learning some of the basics of Scala and programming in general. Toward the end we will write our first little script to solve a problem and run that as well to see how basic programs function.

3.1 Expressions, Types, and Basic Math

All programming languages are built from certain fundamental parts. In English you put together words into phrases and then combine phrases into sentences. These sentences can be put together to make paragraphs. To help you understand programming, we will make analogies between standard English and programming languages. These analogies are not perfect. You cannot push them too far. However, they should help you to organize your thinking early in the process. Later on, when your understanding of programming is more mature, you can dispense with these analogies as you will be able to think about programming languages in their own terms.

The most smallest piece of a programming language that has meaning is called a token. A token is like a word or punctuation mark in English. They are the smallest pieces with any meaning. If you break up a token, you change the meaning of that piece just like breaking up a word is likely to result in something that is no longer a word and does not have any meaning at all. Indeed, many of the tokens in Scala are words. Other tokens are symbols like punctuation. At the end of the last chapter we looked at a line of code like the following.

println("Hello World!");

This line contains a number of tokens: println, (, "Hello World!", ), and ;.

When you think of putting words together, you probably think of building sentences with them. A sentence is a grouping of words that stands on its own in written English. The equivalent of a sentence in Scala, and most programming languages, is the statement. A statement is a complete and coherent instruction that we can give the computer. When you are entering “commands” into the REPL, they are processed as full statements. If you enter something that is not a complete statement in the REPL, instead of the normal prompt, you will get a vertical bar on the next line telling you that you need to continue the statement. The command listed above is a complete statement, which is why it worked the way it did.

Note that this statement ends with a semicolon. In English you are used to ending sentences with a period, question mark, or exclamation point. Scala follows many other programming languages in that semicolons denote the end of a statement. Scala also does something called semicolon inference. Put simply, if a line ends in such a way that a semicolon makes sense, Scala will put one there for you. As a result of this, our print statement will work just as well without the semicolon.

println("Hello World!")

You should try entering this into the REPL to verify that it works. Thanks to the semicolon inference in Scala, we will very rarely have to put semicolons in our code. One of the few times they will really be needed is when we want to put two statements on a single line for formatting reasons.

While you probably think of building sentences from words in English, the reality is that you put words together into phrases and then join phrases into sentences. The equivalent of a phrase in Scala is the expressions. Expressions have a far more significant impact on programming languages than phrases have in English, or at the least programmers need to be more cognizant of expressions than English writers have to be of phrases. An expression is a group of tokens in the language that has a value and a type. Just like some phrases are made from a single word, some tokens represent things that have values on their own and as such, they are expressions themselves. The most basic of these are what are called literals. Our sample line was not only a statement, without the semicolon it was also an expression. In Scala, any valid expression can be used as a statement, but some statements are not expressions. The “Hello World!” part of our full statement was also an expression. It is something called a string literal, which we will learn more about later in this chapter.

Let us take a bit of time to explore these concepts in the REPL. Run the Scala command without any arguments. This will put you in the REPL with a prompt of scala>. In the last chapter we typed in a line that told Scala to print something. This was made from more than one token so we want to start simpler here. Type in a whole number, like 5, followed by a semicolon and hit Enter. You should see something like this:

scala> 5;
res0: Int = 5

The first line is what you typed in at the prompt. The second line is what the Scala REPL printed out as a response. Recall that REPL stands for Read-Evaluate-Print Loop. When you type something in, the REPL reads what you typed, then evaluates it and prints the result. The term loop implies that this happens over and over. After printing the result, you should have been given a new prompt.

So what does this second line mean? The REPL evaluated the statement that you input. In this case, the statement is just an expression followed by a semicolon and the REPL was printing out the value of the expression you entered. As was mentioned above, the REPL needs you to type in full statements so that it can evaluate it. In this case, we typed in a very simple statement that has an expression called a numeric literal followed by a semicolon. This semicolon will be inferred if you do not add it in. We will take advantage of that and leave them out of statements below.

The end of the output line gives us the value of the expression which is, unsurprisingly, 5. What about the stuff before that? What does res0: Int mean? The res0 part is a name. It is short for “result0”. When you type in an expression as a statement in the Scala REPL as we did here, it does not just evaluate it, it gives it a name so that you can refer back to it later. The name res0 is now associated with the value 5 in this run of the REPL. We will come back to this later. For now we want to focus on the other part of the line :Int. Colons are used in Scala to separate things from their types. We will see a lot more of this through the book, but what matters most to us now is the type, Int. This is the type name that Scala uses for basic numeric integers. You can try typing in a few other integer values to see what happens with them. Most of the time the results will not be all that interesting, but if you push things far enough you might get a surprise or two.

What happens if you type in a number that is not an integer? For example, what if you type in 5.6? Try it and you should get something like this:

scala> 5.6
res1: Double = 5.6

We have a different name now because this is a new result. We also get a different type. Instead of Int, Scala now tells us that the type is Double. In short, Double is the type that Scala uses by default for any non-integer numeric values. Even if a value technically is an integer, if it includes a decimal point, Scala will interpret it to be a Double. You can type in 5.0 to see this in action. Try typing in some other numeric values that should have a Double as the type. See what happens. Once again, the results should be fairly mundane unless you push far enough.

So far all of the expressions we have typed in have been single tokens. Now we will build some more complex expressions. We will begin by doing basic mathematical operations. Try typing in “5+6”.

scala> 5+6
res2: Int = 11

This line involves three topics. Each character in this case is a separate token. If you space things out, it will not change the result. However, if you use a number with multiple digits, all the digits together are a single token and inserting spaces does change the meaning.

There should not be anything too surprising about the result of 5+6. We get back a value of 11 and it has a type of Int. Try the other basic arithmetic operations of -, *, and /. You’ll notice that you keep getting back values of type Int. This makes sense for addition, subtraction, and multiplication. However, the result of 5/2 might surprise you a little bit. You normally think of this expression has having the value of 2.5 which would be a Double. However, if you ask Scala for the result of 5/2 it will tell you the value is the Int 2. Why is this and what happened to the 0.5? When both operands are of type Int, Scala keeps everything as Ints. In the case of division, the decimal answer you might expect is truncated and the fractional part is thrown away. Note that it is not rounded, but truncated. Why is this? It is because in integer arithmetic, the value of 5/2 is not 2.5. It is 2r1. That is to say that when you divide five by two, you get two groups of two with one remainder. At some point in your elementary education, when you first learned about division, this is how you were actually told to think about it. At that time you only had integers to work with so this is what made sense.

Scala is just doing what you did when you first learned division. It is giving you the whole number part of the quotient with the fractional part removed. This fractional part is normally expressed as a remainder. There is another operation called modulo that is represented by the percent sign that gives us the remainder after division. Here we can see it in action.

scala> 5%2
res3: Int = 1

The modulo operator is used quite a bit in computing because it is rather handy for expressing certain ideas. You should take some time to re-familiarize yourself with it. You might be tempted to say that this would be your first time dealing with it, but in reality, this is exactly how you did division yourself before you learned about decimal notation for fractions.

What if you really wanted 2.5 for the division? Well, 2.5 in Scala is a Double. We can get this by doing division on Doubles.

scala> 5.0/2.0
res4: Double = 2.5

All of our basic numeric operations work for Doubles as well. Play around with them some and see how they work. You can also build larger expressions. Put in multiple operators, and use some parentheses.

One last thing before we move on to other types is to see what happens when you combine a Double and an Int in an expression. Consider this example:

scala> 5.0/2
res5: Double = 2.5

Here we have a Double divided by an Int. The result is a Double. When you combine numeric values in expressions, Scala will change one to match the other. The choice of which one to change is fairly simple. It changes the one that is more restrictive to the one that is less restrictive. In this case, anything that is an Int is also a Double, but not all values that are Doubles are Ints. So the logical path is to make the Int into a Double and do the operation that way.

3.2 Objects and Methods

One of the features of the Scala language is that all the values in Scala are objects. The term object in reference to programming means something that combines data and the functionality on that data. In Scala we refer to the things that an object knows how to do as methods. The normal syntax for calling a method on an object is to follow the object by a period (which we normally read as “dot”) and the name of the method. Some methods need extra information, which we called arguments. If a method needs arguments then those are put after the method name in parentheses.

In many programming languages, numeric literals like the ones we have used so far would not be objects. They would be simple values, called primitives, that we could not call methods on. In Scala though, even the most basic literals are treated as objects in our program and we can therefore call methods on them. An example of when we might do this is when we need to convert one type to another. In the sample below we convert the Double value 5.6 into an Int by calling the toInt method. In this simple context we would generally just use an Int literal, but there will be situations we encounter later on where we are given values that are Doubles and we need to convert them to Ints. We will be able to do that with the toInt method.

scala> 5.6.toInt
res6: Int = 5

One thing you should note about this example is that converting a Double to an Int does not round. Instead, this operation performs a truncation. Any fractional part of the number is cut off and only the whole integer is left.

We saw at the beginning of this chapter that Scala is flexible when it comes to the requirement of putting semicolons at the end of statements. Scala will infer a semicolon at the end of a line if one makes sense. This type of behavior makes code easier to write and allows it to flow better. In Scala, the dot between an object and a method is also optional. So we can write the following instead:

scala> 5.6 toInt
res7: Int = 5

Even the parentheses on the arguments to a method are optional assuming that there is only one argument. This type of flexibility makes certain parts of Scala more coherent and provides the programmer with significant flexibility. Though you did not realize it, you were using this fact in the last section. To see this, go into Scala and type “5.” then press Tab. The Scala REPL has tab completion just like the command-line, so what you see is a list of all the methods that could be called on the Int. It should look something like the following.

scala> 5.

!=

#

%

&

*

+

/

<

<<

<=

==

<

<=

<<

>>>

^

asInstanceOf

equals

hashCode

isInstanceOf

toByte

toChar

toDouble

toFloat

toInt

toLong

toShort

toString

unary_+

unary_−

unary_~

|

We will not go into any detail on most of these here, but some you have already seen and used. We just finished using toInt on a Double. We can call toDouble on an Int as well. The things that might stand out though are the basic math operations that were used in the previous section. The +, , *, /, and % we used above are nothing more than methods on the Int type. The expression 5+6 is really 5 .+ (6) to Scala. In fact, you can type this into Scala and see that you get the same result.

scala> 5 .+ (6)
res8: Int = 11

The space between the 5 and the . is required here because without it Scala thinks you want a Double. Instead of spacing this out, you can put the 5 in parentheses.

So when you type in 5+6, Scala sees a call to the method + on the object 5 with one argument of 6. We get to use the short form simply because Scala allows both the dot and the parentheses to be optional in cases like this.

3.3 Other Basic Types

Not everything in Scala is a number. There are other non-numeric types in Scala which also have literals. We will start simple and move up in complexity. Perhaps the simplest type in Scala is the Boolean type. Objects of the Boolean type are either true or false and those are also valid literals for Booleans.

scala> true
res9: Boolean = true
 
scala> false
res10: Boolean = false

We will see a lot more on Booleans and what we can do with them in chapter 4 when we introduce Boolean logic.

Another type that is not explicitly numeric is the Char type. This type is used to represent single characters. We can make character literals by placing the character inside of single quotes like we see here.

scala> ’a’
res11: Char = a

The way that computers work, all character data is really numbers and different numbers correspond to different characters. We can find out what numeric value is associated with a given character by using the toInt method. As you can see from the line below, the lowercase “a” has a numeric value of 97.

scala> ’a’.toInt
res11: Int = 97

Because characters have numeric values associated with them, we can also do math with them. When we do this, Scala will convert the character to its numeric value as an Int and then do the math with the Int. The result will be an Int, as seen in this example.

scala> ’a’+1
res12: Int = 98

In the last section you might have noticed that the Int type has a method called toChar. We can use that to get back from an integer value to a character. You can see from the following example that when you add 1 to ’a’ you get the logical result of ’b’.

scala> (’a’+1).toChar
res13: Char = b

An object of the Char type can only be a single character. If you try to put more than one character inside of single quotes you will get an error. It is also an error to try to make a Char with empty single quotes. However, there are lots of situations when you want to be able to represent many characters, or even zero characters. This includes words, sentences, and many other things. For this there is a different type called a String. String literals are formed by putting zero or more characters inside of double quotes like we see in this example.

scala> "Scala is a programming language"
res14: java.lang.String = Scala is a programming language

Notice that the type is listed as java.lang.String. Scala integrates closely with Java and uses some of the Java library elements in standard code. It also allows you to freely call code from the Java libraries. The type String has a full name of java.lang.String, but Scala will automatically provide the java.lang part so we will generally leave it out.

Certain operations that look like mathematical operations are supported for Strings. For example, when you use + with Strings, it does string concatenation. That is to say it gives back a new string that is the combined characters of the two that are being put together as shown here:

scala> "abc"+"def"
res15: java.lang.String = abcdef

This type of operation works with other types as well. This example shows what happens when we concatenate a String with an Int. The Int is converted to a String, using the toString method, and normal string concatenation is performed.

scala> "abc"+123
res16: java.lang.String = abc123

This works whether the String is the first or second argument of the +.

scala> 123+"abc"
res17: java.lang.String = 123abc

In addition to concatenation, you can multiply a string by an integer and you will get back a new string that has the original string repeated the specified number of times.

scala> "abc"*6
res18: String = abcabcabcabcabcabc

This can be helpful for things such as padding values with the proper number of spaces to make a string a specific length. You can do this by “multiplying” the string " " by the number of spaces you need.

There are other types that are worth noting before we move on. One is the type Unit. The Unit type in Scala basically represents a non-value or a meaningless value. The equivalent in many other languages is called void. We have actually seen an example of code that uses Unit. The first program we saw in chapter 2 used a function called println. When we called println Scala did something, but did not give us back a value. This is what happens when we type in an expression that gives us back a value of Unit in the REPL.

The last type in Scala that we will deal with here is a grouping of different types called a tuple. A tuple is a sequence of a specified number of specific types. It is basically, a collection of values that is strict about how many and what type of values it has. We can make tuples in Scala by simply putting values in parentheses and separating them with commas as seen in the following examples.

scala> (5,6,7)
res19: (Int, Int, Int) = (5,6,7)
 
scala> ("book",200)
res20: (java.lang.String, Int) = (book,200)
 
scala> (5.7,8,’f’,"a string")
res21: (Double, Int, Char, java.lang.String) = (5.7,8,f,a string)

The tuples in Scala provide a simple way of dealing with multiple values in a single package and they will come up occasionally through the book. Note that the way we express a tuple type in Scala is to put the types of the values of the tuple in parentheses with commas between them, just like we do with the values to make a tuple object.

Tuples with only two elements can have special meanings in some parts of Scala. For that reason, there is an alternate syntax you can use to define these. If you put the token -> between two values, it will produce a 2-tuple with those values. Consider the following example.

scala> 3 -> "three"
res22: (Int, java.lang.String) = (3,three)

The -> will only produce tuples with two elements though. If you try using it with more than two elements you can get interesting results.

scala> 4 -> 5 -> 6
res23: ((Int, Int), Int) = ((4,5),6)

So if you want tuples with more than two elements, stick with the parentheses and comma notation.

Once you have a tuple, there are two ways to get things out of them. The first is to use methods named _1, _2, _3, etc. So using res21 from above we can do the following.

scala> res21._1
res24: Double = 5.7
 
scala> res21._3
res25: Char = f

The challenge with this method is that method names like _1 are not very informative and can make code difficult to read. We will see an alternative approach in section 3.7 that requires a bit more typing, but can produce more readable code.

3.4 Back to the Numbers

Depending on how much you played around with the topics in section 3.1 you might or might not have found some interesting surprises where things behaved in ways that you were not expecting. Consider the following:

scala> 1500000000+1500000000
res9: Int = -1294967296

Mathematicians would consider this to be an error. It is actually a reflection of the way that numbers are implemented on computers. The details of this implementation can impact how your programs work, so it is worth taking a bit of time to discuss it.

At a fundamental level, all information on computers is represented with numbers. We saw this with the characters being numbers. On modern computers all these numbers are represented in binary, or base two. The electronics in the computer alternate between two states that represent 1 and 0 or on and off. Collections of these represent numbers. A single value of either a 0 or a 1 is called a bit. It is a single digit in a binary number. The term byte refers to a grouping of 8 bits, which can represent 256 different numbers. In Scala these will be between -128 and 127. To understand this, we need to do a little review of how binary numbers work.

You have likely spent your life working with decimal numbers, or base ten. In this system, there are ten possible values for each digit: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9. Digits in different positions represent different power of ten. So the number 365 is really 3*102+6*101+5*100. There is nothing particularly unique about the base ten other than perhaps it relates well to the number of digits on human hands. You can just as well use other bases in which case you need an appropriate number of symbols for each digit and each position represents a power of that base.

Binary uses a base of two. For this we only need two different digits: 0 and 1. This is convenient on computers where the electronics can efficiently represent two states. The different positions represent powers of two: 1, 2, 4, 8, 16, 32, ... So the number 110101=32+16+4+1=53. This example shows how you convert from binary to decimal. Simply add together the powers of two for which the bits are one. A byte stores eight bits that would represent powers of two from 128 down to 1. The word “would” is used here because there is a significant nuance to this dealing with negative numbers that we will discuss shortly.

There are two basic approaches to converting from decimal to binary. One involves repeated subtraction of powers of two while the other involves repeated division by two. We will start with the first one and use the value 296 in decimal for the conversion. We start by finding the largest power of 2 that is smaller than our value. In this case it is 256 = 28. So we will have a one in the 28 position or the 9th digit.1 Now we subtract and get 296 − 256 = 40 and repeat. The largest power of 2 smaller than 40 is 32 = 25. So the digits for 27 and 26 are 0. Subtract again to get 40 − 32 = 8. We now have 8 = 23 so the final number in binary is 100101000. This procedure is written out the way you might actually do it in figure 3.1.

Figure 3.1

Figure showing Illustration of the conversion from decimal to binary using the subtraction method. This method works from the top down. To get the number in binary just read down the list of digits.

Illustration of the conversion from decimal to binary using the subtraction method. This method works from the top down. To get the number in binary just read down the list of digits.

The other approach is a bit more algorithmic in nature and is probably less prone to error. It works based on the fact that in binary, multiplying and dividing by 2 moves the “binary point” the same way that multiplying or dividing by 10 moves the decimal point in the decimal number system. The way it works is you look at the number and if it is odd you write a 1. If it is even you write a 0. Then you divide the number by 2, throwing away any remainder or fractional part, and repeat with each new digit written to the left of those before it. Do this until you get to 0. You can also think of this as just dividing by two repeatedly and writing the remainder as a bit in the number with the quotient being what you keep working with.

The number 296 is even so we start off by writing a 0 and divide by 2 to get 148. That is also even so write another 0. Divide to get 74. This is also even so write another 0. Divide to get 37. This is odd so write a 1. Divide to get 18, which is even so you write a 0. Divide to get 9 and write a 1. Divide to get 4 and write a 0. Divide to get 2 and write a 0. Divide to get 1 and write that one. The next division gives you zero so you stop. This procedure is illustrated in figure 3.2.

Figure 3.2

Figure showing Illustration of the conversion from decimal to binary using the repeated division method. This method works from the bottom up so you get the bits in the result starting with the smallest.

Illustration of the conversion from decimal to binary using the repeated division method. This method works from the bottom up so you get the bits in the result starting with the smallest.

3.4.1 Binary Arithmetic

Now that you know how to go from binary to decimal and decimal to binary, let’s take a minute to do a little arithmetic with binary numbers. It is certainly possible to do this by converting the binary to decimal, doing the arithmetic in decimal, then convert back to binary. However, this is quite inefficient and not worth it because it really is not hard to work in binary. If anything, it is easier to work in binary than in decimal. Let us begin with the operation of addition. Say we want to add the numbers 110101 and 101110. To do this you do exactly what you would do with long addition in decimal. The difference is that two in binary is 10 so there is a lot more carrying.

 110101
+ 101110
 -------
 1100011

Multiplication in binary can also be done just like in decimal and you have a lot fewer multiplication facts to memorize. Zero times anything is zero and one times anything is that number. That is all we have to know. Let us do multiplication with the same numbers we just worked with. First we will get all the numbers that need to be added up.

 110101
  * 101110
-----------
 1101010
  11010100
 110101000
11010100000

Adding these numbers is best done in pairs. The reason is that as soon as you add together 3 or more numbers in binary you have the capability to have to do something you are not accustomed to doing in decimal: carry a value up two digits. In decimal you would have to have a column sum up to one hundred or more for this to happen. However, in binary you only have to get to four (which is written as 100 in binary). That happens in this particular instance in the 6th digit. To reduce the odds of an error, it is better to add the values two at a time as we have shown here.

 1101010
 + 11010100
 -----------
  100111110
 + 110101000
 -----------
 1011100110
+11010100000
 -----------
100110000110

You can do division in the same way that you do long division with integers, but we will not cover that here.

3.4.2 Negative Numbers in Binary

We still have not addressed the question of how we represent negative numbers on a computer. The description that we have given so far only deals with positive values. Numbers that are interpreted this way are called unsigned. All the numeric types in Scala are signed so we should figure out how that works.2 To do this, there are two things that should be kept in mind. The first is that our values have limited precision. That is to say that they only store a certain number of bits. Anything beyond that is lost. The second is that negative numbers are defined as the additive inverses of their positive counterparts. In other words, x + (−x) = 0 for any x.

To demonstrate how we can get negative numbers, let’s work with the number 110101 (53 in decimal). Unlike before though, now we will limit ourselves to a single byte. So we have 8 digits to work with and the top digits are zeros. Our number stored in a byte is really 00110101. So the question of what should be the negative is answered by figuring out what value we would add to this in order to get zero.

 00110101
+ ????????
 --------
 00000000

Of course, there is nothing that we can put into the question marks to make this work. However, if we go back to our first fact we can see what we must do. We do not need zero, we need eight digits of zero. So in reality, what we are looking for is the following.

 00110101
+ ????????
 --------
 100000000

This problem is solvable and the top 1 will be thrown away because we can only store 8 bits in a byte. So the answer is given here.

 00110101
+ 11001011
 --------
 100000000

Note that top bit is on in the negative value. The top bit is not exactly a sign bit, but if a number is signed, the top bit will tell us quickly whether the number is positive or negative. This style of making negatives is called twos complement. In the early days of digital computing other options were tried, such as adding a sign-bit or a method called ones’ complement where the bits are simply flipped. However, two’s complement is used in machines today because it allows numeric operations to be done with negative numbers using the same circuitry as is used for positive numbers.

This process gives us the correct answer and is based on the proper definition of what a negative number is. Finding negatives using the definition of what a negative value is works and can be a fallback, but there is a simpler method. To get the two’s complement negative of a binary number of any size, simply flip all the bits and add one. You can verify that this approach works for our example above.

3.4.3 Other Integer Types

There are larger groups of bits beyond the 8-bit bytes that have meaning in Scala. In fact, if you go back to section 3.2 and you look at the different methods on an Int, you will see that toDouble and toChar are not the only conversions we can do. Scala has other integer types of Byte, Short, and Long. A Byte in Scala is an 8-bit number. A Short is a 16-bit number. The Int that we have been using is a 32-bit number. The Long type is a 64-bit number. The reason for the odd behavior that was demonstrated at the beginning of this section is that we added two numbers together whose sum is bigger than what can be stored in the lower 31 bits of an Int and the overflow, as it is called, wrapped it around to a negative value. Table 3.1 shows the minimum and maximum values for each of the different integer types.

Table 3.1

Integer types with their sizes and ranges.

Type

Bits

Min

Max

Byte

8

-128

127

Short

16

-32768

32767

Int

32

-2147483648

2147483647

Long

64

-9223372036854775808

9223372036854775807

Occasionally you will need to use literals that are bigger than what an Int can store. You can do this with a Long. Making a numeric literal into a Long is done by simply adding an L to the end. You can see this here.

scala> 5000000000L
res8: Long = 5000000000

The value five billion is not a valid Int. If you leave off the L here you get an error. The L can be lower case, but then it looks a lot like the number one so it is better to use the upper case.

We talked about binary above and Scala has a method that will let you see the binary form of a number. This method works on the four normal numeric types and Char. Here we use it to see the binary representation for 53 and -53 for the values as both Int and Long types.

scala> 53.toBinaryString
res15: String = 110101
 
scala> (-53).toBinaryString
res16: String = 11111111111111111111111111001011
 
scala> 53L.toBinaryString
res17: String = 110101
 
scala> (-53L).toBinaryString
res18: String =
 1111111111111111111111111111111111111111111111111111111111001011

The toBinaryString method does not display leading zeros, so the positive values only show six digits in both formats. However, the negative form has many leading ones and all of these are printed.

3.4.4 Octal and Hexadecimal

Binary is what the machine uses, but it really is not that useful to humans. This is in large part due to the fact that the number of digits in a binary number is often large, even if the number itself is not what we consider large. There are two other bases that are commonly seen in programming and dealing with computers. They are base 8, octal, and base 16, hexadecimal or hex. Like decimal, these bases allow you to represent fairly large numbers with relatively few digits. Unlike decimal, converting from octal or hex to binary and back is trivial. The reason for this is that eight and 16 are powers of two.

To see this, let us start with octal. When working in base 8, the digits can be between 0 and 7 with each subsequent digit being a higher power of 8. The ease of converting to and from binary comes from the fact that eight is 23. In binary the values 0 to 7 are represented with three bits between 000 and 111. The fourth and subsequent bits represent values that are multiples of eight. Because of this, we can convert a binary number to an octal number by grouping the bits into groups of three and converting those groups. The only catch is that this grouping has to start with the ones digit. So our favorite binary number, 110101 is 65 in octal. The lowest three bits, 101, convert to 5 and the next three, 110, convert to 6. We can use the toOctalString method to confirm this.

scala> 53.toOctalString
res21: String = 65

To go the other way, from octal to binary, we simply convert the octal digits to three digit binary numbers. So the octal value, 3726 converts to 011111010110. We can emphasize the groupings of bits by spacing them out: 011 111 010 110.

Moving between hexadecimal and binary is similarly simple. The only catch is that now a single digit needs to have 16 possible values. So the 0-9 that we are used to will not suffice. It is typical to augment the normal digits with the letters A-F where A is ten and F is fifteen. Because 16 = 24, we use groups of 4 bits when converting between hexadecimal and binary. Once again, you start the process with the lower bits and work up. So 110101 becomes 35. The large number we got from octal above, 0111 1101 0110 becomes 7D6 in hex. Again, there is a method called toHexString that can be used on the numeric types to quickly get the hexadecimal representation of a number.

While toOctalString and toHexString give us octal and hexadecimal representations of numeric values that we have in decimal, it is sometimes helpful to be able to enter values into programs using octal or hexadecimal. It is possible to enter literals in Scala using these bases. To get an octal literal, prefix the number with a leading zero. To get a hexadecimal literal, prefix it with 0x. The following uses of this confirm the conversions that we did for the larger number above.

scala> 03726.toBinaryString
res23: String = 11111010110
 
scala> 0x7D6.toBinaryString
res24: String = 11111010110

Values in hex or octal are particularly significant when you want to make sure that you know how many bits will be in the binary representation of the number.

3.4.5 Non-Integer Numbers

You now have a rather complete description of the integer number types and how they are represented in the machine. What about non-integer numeric values? We saw previously that if we type in a numeric value that includes a decimal point Scala tells us that it has type Double. The Double literal format is more powerful than just including decimal points. It also allows you to use scientific notation to enter very large or very small numbers. Simply follow a number by an e and the power of ten it should be multiplied by. So 15000.0 can also be written as 1.5e4.

The name Double is short for double precision floating point number. The full name includes information about the way that these numbers are stored in the memory of a computer. Like all values in a computer, the Double is stored as a collection of bits. To be specific, a Double uses 64-bits. This size is related to the double precision part. There is another type called Float that is a single precision floating point number and only uses 32-bits. In both cases, the internal representation uses floating point format. In many ways, this is similar to scientific notation, but in binary instead of decimal. The bits in a floating point number are grouped into three different parts. We will call them s, e, and m and the value of the number is given by (−1)s * (1 + m) * 2(e−bias). The first bit in the number is the sign bit, s. When that bit is on, the number is negative and when it is off it is positive. After the sign bit is a group of bits for the exponent, e. Instead of using two’s compliment for determining if the exponent is negative, the exponent is biased by a value that is picked to match with the number of bits in the exponent. Using a bias instead of two’s compliment means that comparisons between floating point values can be done with the same logic used for integer values with the same number of bits. All remaining bits are used to store a mantissa, m. The stored mantissa is the fractional part of the number in normalized binary. So the highest value bit is 12, the next is 14, and so on. Table 3.2 below gives the number of bits used for e and m, the bias, and the range of numbers they can represent in the Double and Float types. The E notation is short for multiplication by 10 to that power.

Table 3.2

Floating point types with sizes and ranges.

Type

e Bits

m Bits

bias

Min

Max

Float

8

23

127

-3.4028235E38

3.4028235E38

Double

11

52

1023

-1.7976931348623157E308

1.7976931348623157E308

As we have seen, floating point literals are by default considered to be of type Double. If you specifically need a Float you can append an f to the end of the literal. There are many other details associated with floating point values. There is only one main point that will be stressed here though. That is the fact that floating point values, whether Double or Float, are not Real numbers in the sense you are used to in math with arbitrary precision. Floating point numbers have limited precision. Like the integers, they can be overflowed. Unlike the integers, they are fundamentally imprecise because they represent fractional values with a finite number of bits.

To understand this, let us look at a situation where you should have seen this before. Consider the simple fraction, 13. Now consider the representation of this value as a decimal. The decimal representation is 0.33333... In order to write this fraction accurately in decimal, you need an infinite number of digits. In math we can denote things like this by putting in three dots or putting a line over the digits that are repeated. For floating point values though, the digits simply cut off when you get to the end of the mantissa. As such, they are not exact and the circuitry in the computer employs a rounding scheme to deal with this. This imprecision is not visible most of the time, but one immediate implication of it is that you can not trust two floating point numbers to be equal if they were calculated using arithmetic. It also means that you should not use floating point numbers for programs that involve money. The decimal value 0.1 is a repeating fraction in binary and as such, is not perfectly represented. That is generally considered a bad thing when dealing with people’s money. Instead you should use an integer type and store cents instead of dollars.

3.5 The math Object

While on the topic of numbers, there are quite a few standard functions that you might want to do with numbers beyond addition, subtraction, multiplication, and division. There are a few other things you can get from operators that we will discuss later. Things like square root, logarithms, and trigonometric functions are not operators. They are found as methods in the math object. You can use tab completion in the REPL to see all the different methods that you can call on math.

scala> math.

BigDecimal

BigInt

E

Equiv

Fractional

IEEEremainder

Integral

LowPriorityOrderingImplicits

Numeric

Ordered

Ordering

PartialOrdering

PartiallyOrdered

Pi

ScalaNumber

ScalaNumericConversions

abs

acos

asin

atan

atan2

cbrt

ceil

cos

cosh

exp

expm1

floor

hypot

log

log10

log1p

max

min

package

pow

random

rint

round

signum

sin

sinh

sqrt

tan

tanh

toDegrees

toRadians

ulp

Many of these probably do not make sense right now and you should not worry about them. However, many of them should be identifiable by the name. So if we wanted to take a square root of a number, we could do the following.

scala> math.sqrt(9)
res15: Double = 3.0

You would use a similar syntax for taking cosines and sines. The functions provided in the math object should be sufficient for the needs of most people. Only two of the contents of math that start with capital letters are worth noting at this point. Pi and E are numeric constants for π and e.

scala> math.Pi
res16: Double = 3.141592653589793

3.6 Details of Char and String

There is a lot more to Char and String than we covered in section 3.3. Some of it you really should know before we go further. We saw how we can make character literals or string literals that contain keys that appear on the keyboard and that go nicely into a text file. What about things hat can not type as nicely or that have other meanings? For example, how do ou put double quotes in a String? Typing the double quote closes off the string literal instead of putting one in. How would you make a character of a single quote? How do you get a new line? You are not allowed to have a normal string break across lines.

We can do all of these things and more with escape characters. These are denoted by a backslash in front of one or more characters. For example, if you want to put a double quote in a string, simply put a backslash in front of the double quote. The same thing is true for a single quote in a character. You can insert a newline with a . What if you want to insert a backslash itself? Simply put two backslashes. Table 3.3 shows other commonly used escape characters.

Table 3.3

Table of special character escape sequences in Scala

Literal

Meaning

Unicode Hex Encoding



backspace

u0008

f

form feed

u000C

line feed

u000A

carriage return

u000D

tab

u0009

"

double quote

u0022

single quote

u0027

\

backslash

u005C

In addition to escape characters, the backslash can be used to put any type of special character into a string. If you know the Unicode value for a special character, you can put u followed by four hexadecimal digital in a string to specify that character. For characters from the lower 256 characters that are represented in ASCII you can follow the backslash by an octal number between 0 and 377.

There are some times when using the escape characters becomes a pain. For example, there are times when you need to build strings that have a number of backslashes. Each one you want requires you to put in two. This can get unwieldy. In addition, if you have a long, multiline string, it can be difficult to format the string the way you want. For these types of situations, Scala includes a special form of string that begins and ends with three double quotes. Anything you type between the set of three double quotes is taken to be literally part of the string without alteration. The following shows an example of using this to enter a long string in the REPL.

scala> """This is a long string.
| It spans multiple lines.
| If I put in 
 and \ or " they are taken literally."""
res8: java.lang.String =
This is a long string.
It spans multiple lines.
If I put in 
 and \ or " they are taken literally.

3.7 Naming Values and Variables

We have seen enough that you can solve some simple problems. For example, if you were given a number of different grades and asked to find the average, you could type in an expression to add them all up and divide by the number of them to get the average. We basically have the ability to use Scala now to solve anything we could solve with a calculator. We will develop a lot more over time, but we have to start somewhere. As it stands we are not just limited to solving problems we could do with a calculator, we are solving them the way we would with a calculator. We type in mathematical expressions the way we would write them on paper and get back an answer. Real programming involves tying together multiple lines of instructions to solve larger problems. In order to do this, we need to have a way to give names to the work we have done and refer back to them.

There are three keywords in Scala that give names to values. We will see the first two here: val and var. To begin with, let us look at the full syntax of val and var in two samples. Then we can pull them apart, talk about what they do, see how they are different, and discuss what parts of them are optional.

scala> val age:Int=5
age: Int = 5
 
scala> var average:Int=(2+3+4+5)/4
average: Int = 3

Syntactically the only difference between these two is that one says val and the other says var. That is followed by a name with a colon and a type after it. The rules for names in Scala are that they need to start with a letter or an underscore followed by zero or more letters, numbers, and underscores. Scala is also case sensitive. So the names AGE, age, Age, and agE are all different. In general it is considered very poor style to use names that differ only in capitalization. Scala borrows a standard naming convention from Java called camel case. The names of values begin with a lower case letter and the first letter of subsequent words are capitalized. For example, theClassAverage is a name that follows this convention. Type names use the same convention except that the first letter is capitalized. This is called camel case because the capital letters look like humps.

The types in both of these examples are followed by an equals sign and an expression. Unlike many other programming languages, this is not optional in Scala. In Scala, when you declare a val or var, you must give it an initial value at that point.3

While the initial value is not optional, the type generally is. This too is unlike most programming languages. As we have already seen, Scala is able to figure out the types of things for us in many situations. If we leave off the colon and the type, Scala will simply use whatever type it determines is appropriate for the expression in the initial value. Most of the time, the type that it gives us will be exactly what we want. Using this we could instead have the following shorter forms of these declarations.

scala> val age=5
age: Int = 5
 
scala> var average=(2+3+4+5)/4
average: Int = 3

The reason for using a val or var declaration is that they give a name to the value that we can refer back to later. For example, we could now type in age+10 and Scala would give us 15. The names serve two purposes. They prevent us from typing in expressions over and over again. They also help give meaning to what we are doing. You should try to pick names that help you or other readers figure out what is going on with a piece of code.

So far we have discussed all the similarities between val and var and you might be wondering in what way they are different. The declarations themselves are basically identical. The difference is not in the syntax, but in the meaning, or semantics. A val declaration gives a name to a reference to a value. That reference can not be changed. It will refer to the thing it was originally set to forever. In the REPL, you can declare another val with the same name, but it does not do anything to change the original. A var declaration, on the other hand, allows the reference to change. In both cases we are not naming the value, we are naming a box that stores a reference to the value. The significance of this will be seen in section 7.8.

The act of changing the reference stored in one of these boxes we call variables is referred to as an assignment. Assignment in Scala is done with the same equal sign that was used to set the initial value. In an assignment though there is no val or var keyword. If you accidentally include either var or val you will be making a new variable, not changing the old one.

scala> average=8
average: Int = 8
 
scala> average=2*average
average: Int = 16

The first assignment causes the box named average to change from referring to the object 3 to the object 8. The second one uses the previously referred to value and multiplies it by two, then stores a reference to that new value back into the variable.

There is a bit more to the initialization of val and var declarations than was mentioned above. Technically, the assignment is able to do something called pattern matching that we will get to in detail in chapter 6. For now, the only aspect we will care about is that we can put tuples on the left-hand side of the equals sign where we would normally put just a variable name. First, let us see what happens if we do a val declaration with a tuple on the right-hand side.

scala> val t=(100,5.7)
t: (Int, Double) = (100,5.7)

Note that t refers to the tuple and has a type (Int,Double). This is exactly what we would expect. The new power that pattern matching provides is that if you put multiple names inside of a parentheses on the left of the equals, much like a tuple, all the names will be bound. That type of behavior is shown here.

scala> val (price,weight)=(100,5.7)
price: Int = 100
weight: Double = 5.7

The same can be done with a var and then all the names will have the ability to change what they refer to. This is the second way of getting values out of tuples. It is more readable because we can pick meaningful names for the variables.

Let us use the ability to name values to do a little problem solving. We are given a total time in seconds and we want to know what that is in hours, minutes, and seconds. We then want to print that out in a reasonable format of “hh:mm:ss”. The first step in solving this problem is to figure out how to go from just seconds to hours, minutes, and seconds. Once we have that, we can worry about formatting it to get the right string value.

How do we get from seconds to hours, minutes, and seconds? First, how do you get from seconds to minutes? That is fairly easy, you simply divide by 60. Thanks to the fact that integer division truncates, you will get the proper number of whole minutes. Here are two lines that define a number of total seconds as well as a number of total minutes.

scala> val totalSeconds=123456
totalSeconds: Int = 123456
scala> val totalMinutes=totalSeconds/60
totalMinutes: Int = 2057

That number of minutes is not exactly the amount of time we want though. There are seconds left over. How do we figure out how many seconds we should display? We could do totalSeconds-(60*totalMinutes), but a simpler expression is used here.

scala> val displaySeconds=totalSeconds%60
displaySeconds: Int = 36

The modulo gives us the remainder after we have gotten all the full groups of 60. That is exactly what we want. Now how do we get the number of hours and the number of minutes to display? The math is the same because there are 60 minutes in each hour.

scala> val displayMinutes=totalMinutes%60
displayMinutes: Int = 17
 
scala> val isplayHours=totalMinutes/60
displayHours: Int = 34

What we see from this is that 123456 seconds is 34 hours, 17 minutes, and 36 seconds. We could repeat this same process for a different number of seconds if we used a different value for totalSeconds.

Now that we have these values, we want to figure out how to get them into a string with the format “hh:mm:ss”. A first attempt at that might look like the following.

scala> val finalString=displayHours+":"+displayMinutes+":"+
| displaySeconds
finalString: java.lang.String = 34:17:36

For this particular number of seconds, this works just fine. However, if you play around with this at all, you will find that it has a significant shortcoming. If the number of minutes or seconds is less than 10, only one digit is displayed when we want two. So we need to come up with a way to get a leading zero on numbers that only have one digit. To do this, we will break the problem into two steps.

The first step will be to get the number of minutes and seconds as Strings.

scala> val min=displayMinutes.toString
min: java.lang.String = 17
 
scala> val sec=displaySeconds.toString
sec: java.lang.String = 36

This might seem odd, but the string version has something that the number itself does not, an easy way to tell how many digits/characters are in it. When there is only one digit, we want to add an extra zero. When there is not, we leave it as is. We can get this effect by using the * method on the String and a little math. The short names were selected to keep our expression shorter for formatting, but that is not required.

scala> val finalString=displayHours+":"+("0"*(2-min.length))+min+":"+(
| "0"*(2-sec.length))+sec
finalString: java.lang.String = 34:17:36

The result for these values is the same, but we could force some different value into min and sec to see that this does what we want.

scala> val min="5"
min: java.lang.String = 5
 
scala> val sec="2"
sec: java.lang.String = 2
 
scala> val finalString=displayHours+":"+("0"*(2-min.length))+min+":"+(
| "0"*(2-sec.length))+sec
finalString: java.lang.String = 34:05:02

3.8 Sequential Execution

Working in the REPL is great for certain tasks, but what if you have a sequence of things you want to do, and you want to do it multiple times. Having to type in the same set of instructions repeatedly is not a very good option. What we just did is a perfect example of that. If we want to do this for a different number of seconds, we have to repeat all the commands we just performed. Indeed, you can not really say that you have programmed until you put in a fixed set of instructions for solving a problem that you can easily run multiple times. That is what a program really is. So now it is time to write our first program of any significance.

We have used the REPL to enter commands one at a time. This is a great way to test things out in Scala and see what a few commands do. A second way of giving commands to Scala is to write little programs as scripts. The term script is used to describe small programs that perform specific tasks. There are languages, called scripting languages, that have been created specifically to make the task of writing such small programs easier. Scala is not technically a scripting language, but it can be used in that way. The syntax was created to mirror a lot of the things that are commonly put into scripting languages and if you run the scala command and give it the name of a file that ends in .scala, that file will be run as a script where the statements in it are executed in order.4 We will use Scala as a scripting language until chapter 17 when we move up to a different style that helps us to organize larger pieces of code. The script for our time conversion looks like this.

val totalSeconds=123456
val displaySeconds=totalSeconds%60
val totalMinutes=totalSeconds/60
val displayMinutes=totalMinutes%60
val displayHours=totalMinutes/60
val sec=displaySeconds.toString
val min=displayMinutes.toString
val finalString=displayHours+":"+("0"*(2-min.length))+min+
":"+("0"*(2-sec.length))+sec
println(finalString)

If you put this into a file called TimeScript.scala and then run scala TimeScript.scala, you will get the output 34:17:36. The println statement is required for the script because unlike the REPL, the script does not print out values of all statements. You can run through this code in the REPL using the :load command. If you do “:load TimeScript.scala” you will see it print out all of the intermediate values as well as the result of the println.

This script allows us to run the commands repeatedly without retyping. By editing the value of totalSeconds, we can test other total times fairly quickly. However, a better solution would be to allow a user to tell us how many seconds to use every time we run the script. We can easily get this behavior by replacing the top line of the script we had with these two lines.

print("Enter the number of seconds. ")
val totalSeconds=readInt()

The first line does nothing more than print a prompt to let the user know that we are waiting for something to be input. After that we have altered the initialization of totalSeconds so that instead of giving it a fixed number, it is initialized to the value returned by readInt. This calls a function that reads in a single integer from the user. If you make this change and run the script, you will be able to enter any number of seconds, assuming it is a valid Int, and see it converted to hours, minutes, and seconds.

3.8.1 Comments

When writing programs in files, not in the REPL, it is often useful to include plane English descriptions of parts of the code. This is done by writing comments. If you are writing code for a course you likely need to have your name in the code. Your name is most likely not valid Scala so it should go in a comment. Different instructors and companies will have different commenting standards that you should follow. In a professional setting, comments are used primarily for two reasons. The first is to indicate what is going on in the code, particularly in parts of the code that might be difficult for readers to understand. The second is for documentation purposes as we will see in section 17.3.

There are two basic comment types in Scala, single-line comments and multiline comments. Single-line comments are made by putting // in the code. Everything after that in the line will be a comment and will not be ignored when the program is compiled and run. Multiline comments begin with /* and end with */. You can put anything you want between those and they can be spaced out across many lines. Code shown in this book will have limited commenting as descriptions of the code appear in the text of the book and there is little point in duplicating that content.

You have now been introduced to Scala and programming. This idea of giving specific instructions in the order that we need them to happen to solve a problem underpins everything we will do in this book. However, this is only a start. There is a lot more to explore and we begin to open the door to these possibilities in the next chapter.

3.9 A Tip for Learning to Program

In many ways, learning to program, whether in Scala or any other programming language, is very much like learning a new natural language. The best way to learn a natural language is through immersion. You need to practice it and be surrounded by it. The key is to not simply memorize the rules and vocabulary, but to put them into use and learn them through regular usage. You should strongly consider approaching programming in the same way.

So what does it mean to immerse yourself in a programming language? Clearly you are not going to have conversations in it or enjoy television or radio broadcasts in it. The way to immerse yourself in a programming language like Scala is to take a few minutes every day to write in it. You should consider trying to spend 15-30 minutes each day writing code. The REPL in Scala is an excellent tool for you to enter in statements to see what they do. Try to play around with the language. Instead of approaching it as memorizing keywords and rules, try to put things into use. The things that you use frequently will stick and become natural. Those things that you do not use regularly you will have to look up, but that is normal. Programmers, even professional programmers with many years of experience in a language, still keep references handy.

Over time, the number of lines of code that you write in these short time intervals each day will grow as the basics become second nature and you begin to practice more advanced concepts. By the end of this book you might find yourself writing a hundred lines or so of functional code on certain days during that time span. By that time you will hopefully also pick up a “pet project”, something that you are truly interested in programming and that you will think about the structure and logic of much more frequently.

Especially early on, you might find it hard to think of anything that you can write. To help you with this, many of the chapters in this book contain a “Self-Directed Study” section, like the one below. Use these as a jumping-off point for the material in each chapter. After that will come a set of exercises and often a set of larger problems called projects. Remember that one of the significant goals of learning to program is improving your problem-solving skills. While the Self-Directed Study section will help you to familiarize yourself with the details presented in a chapter, the exercises and projects are actual problems that you are supposed to solve in a formal way using Scala. You should use these to help provide you with the immersion you need to learn the language.

3.10 End of Chapter Material

3.10.1 Problem-Solving Approach

Many students who are new to programming struggle with putting the English descriptions for solving a problem that they have in their head into whatever programming language they happen to be learning. The reality is that for any given line of code, there are a fairly small number of “productive” things that you could write. In the REPL you can test out any statement that you want, but in a script, an expression like 4+5 does not do much when used alone as a statement. These sections will appear at the end of a number of chapters as we introduce new concepts that might stand alone as statements, or which alter statements we have talked about previously in a significant way. The goal of these sections is to help focus your thinking so you can narrow down the list of possibilities any time that you are trying to decide what to put into the next line of code.

Given what we have just learned, there are only three types of statements that you would put in a script that stand alone:

  1. A call to print or println to display information to the user. The function name should be followed with parentheses that contain the expression you want to print.
  2. A variable declaration using val or var. A val declaration would look like val name = expression. The name could be followed with a colon and a type though most of the time it won’t be.
  3. An assignment into a previously declared var of the form name = expression. The expression must have a type that agrees with the type the variable was created with.

If you want to read information using a function like readLine(), readInt(), or readDouble, that should appear as part of an expression in one of the above statements.

3.10.2 Summary of Concepts

  • Programming languages have relatively simple rules that they always follow with no ambiguity.
    • Tokens are the smallest piece with meaning. They are like words in English.
    • Expressions are combinations of tokens that have a value and a type.
    • Statements are complete instructions to the language. In Scala, any expression is a valid statement.
    • The simplest expressions are literals.
      • Int literals are just numbers with no decimal points like 42 or 365.
      • Adding an L to the end of an integer number makes a Long literal.
      • Numbers that include decimal points or scientific notation using e syntax are of the type Double.
      • Adding an f to the end of a number makes it a Float
      • Char literals are single characters between single quotes.
      • String literals can have multiple characters between double quotes. Raw strings start and end with three double quotes and allow newlines.
  • An object is a combination of information and functionality that operates on that information.
    • The information is called data members, fields, or properties.
    • The functionality is called methods.
    • All values in Scala are objects.
    • Methods are normally invoked using the “dot” notation. Arguments go in parentheses after the method name.
      • Scala allows the . to be left out.
      • Parentheses are also optional for argument lists of length zero or one.
      • Operators are really method calls. So 4+5 is really (4) .+(5).
  • Numbers in computers are not exactly like numbers in math and you need to know some of the differences so you will understand when they lead to unexpected behavior.
    • All values stored in a computer are stored in binary, base 2, numbers. Each digit is called a bit. Different types use different numbers of bits for storage. The finite number of bits means that there a minimum and maximum values that can be stored in each type.
    • Negative integer values are stored using two’s compliment numbers.
    • Binary numbers require a large number of digits, though they are all either 0 or 1, and converting to and from decimal is non-trivial. For this reason, computer applications frequently use base 8, octal, and base 16, hex. You can make an octal literal by putting a leading 0 on an integer. You can make a hexadecimal literal by putting a leading 0x on an integer.
    • Non-integer numeric values are stored in floating point notation. This is like scientific notation in binary. These use the types Float and Double. Due to finite precision, not all decimal numbers can be represented perfectly in these numbers and there are small rounding errors for arithmetic.
  • Additional mathematical functions, like trigonometric functions and square root are methods of the math object.
  • You can declare variables using the keywords val and var. The name of a variable should start with a letter or underscore and can be followed by letters, underscores, or numbers. A var declaration can be reassigned to reference a different value.
  • Instructions can be written together in scripts. The default behavior of a script is for lines of code to execute sequentially. Script files should have names that end with .scala. You run a script by passing the filename as a command-line argument to the scala command.
  • Learning a programming language is much like learning a natural language. Do not try to memorize everything. Instead, immerse yourself in it and the things you use frequently will become second nature. Immersion in a programming language means taking a few minutes each day to write code.

3.10.3 Self-Directed Study

Enter the following statements into the REPL and see what they do. Try some variations to make sure you understand what is going on. Not all of these will be valid. You should try to figure out why.

scala> val a=5
scala> val b=7+8
scala> var c=b-a
scala> a=b+c
scala> c=c*c
scala> b=a/c
scala> b%2
scala> b%4
scala> b%a
scala> 0.3*b
scala> val name = "Your name here."
scala> name.length
scala> name+a
scala> println("Hi there "+name)
scala> println("


")
scala> println("""


""")
scala> ’a’+5
scala> (’a’+5).toChar
scala> math.Pi/2
scala> math.sqrt(64)-4.0
scala> math.sqrt(1e100)
scala> math.cos(math.Pi)
scala> 3000000000
scala> 3000000000L
scala> 3000000000.0
scala> 3e9
scala> 1/0
scala> 1.0/0.0
scala> 0.0/0.0

3.10.4 Exercises

  1. What are the types of the following expressions?
    1. (a) 1
    2. (b) 1.7
    3. (c) 1.0
    4. (d) ’h’
    5. (e) ”hi”
    6. (f) 5/8
    7. (g) 1+0.5
    8. (h) 7*0.5
    9. (i) “hi”.length
  2. Do the following 8-bit binary arithmetic by hand.
    1. (a) 101011012 + 110101002
    2. (b) 001111102 + 001110112
    3. (c) 010010102 * 001100102
  3. Convert the following decimal values to binary (8-bit), hex (2-digit), and octal (3-digit) by hand.
    1. (a) 7
    2. (b) 18
    3. (c) 57
    4. (d) 93
    5. (e) 196
  4. Convert the following hex values to binary and decimal by hand.
    1. (a) 0x35
    2. (b) 0x96
    3. (c) 0xA8
    4. (d) 0x7F
  5. Convert the following decimal values to binary (8-bit) and hex (2-digit) by hand.
    1. (a) −87
    2. (b) −32
    3. (c) −105
    4. (d) −1
  6. Write a script that will calculate how far a projectile will go given a launch speed and an angle ignoring friction. Assume that the projectile is launched from ground level with a certain speed in m/s and at a certain angle in radians. Use the fact that acceleration due to gravity is 9.8m/s2. The steps in doing this would be to calculate the speed parallel and perpendicular to the ground with math.sin and math.cos, then figure out how long it takes for the projectile to slow to a vertical speed of zero (v = v0 − at), and use double that time as how long it stays in the air.
  7. Using Scala as a calculator, figure out how much you have to make each year to bring home $100,000 assuming a 27% tax rate.
  8. In the REPL, declare a variable with the type String that has the name str. Give it whatever value of string you want. On the next line, type str. then hit Tab to see the methods for String. By playing around with the REPL, try to figure out what the following methods do.
    • toUpperCase
    • trim
    • substring – This method takes two Int arguments.
    • replace – This method can be called with two Char arguments or two String arguments.
  9. Kepler’s third law of planetary motion says that P2 ∝ a3, where P is the orbital period and a is the semi-major axis of the orbit. For our Sun, if you measure P in years and a in Astronomical Units (AU), the proportionality becomes equality. Look up the semi-major axis values for three bodies in our solar system other than the Earth and use Scala as a calculator to find the period according to Kepler’s third law.
  10. In this option you will write a little script that does part of a 1040-EZ. We have not covered enough for you to do the whole thing, but you can write enough to ask questions and do most of the calculations for lines 1-13. Do what you can and remember this is a learning exercise.
  11. Your goal for this exercise is to write a script to calculate the cost of a simple, 3-ingredient, recipe. You start by asking the user for the names and amounts of the three ingredients. Then prompt them for the cost, per unit, of each ingredient. Output the total cost of each ingredient and for the whole recipe. To make things simple, feel free to use the Double type.

1Remember that the first digit is 20 = 1.

2The Char is actually a 16-bit unsigned numeric value, but the normal numeric types are all signed.

3There are very good reasons for requiring initialization of variables. Even in languages that do not require it, a programmer can make his/her life a lot easier by initializing all variables at creation. The declaration and initialization should ideally happen at the point where you have a real value to put into the variable. This prevents many errors and as a result, can save you a lot of time in your programming.

4We will see later that the statements are not always executed in order because there are statements that alter the flow of control through the program. Since we have not gotten to those yet though, execution is completely sequential at this point.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset