The latest instance of the course can be found at: O1: 2024
Luet oppimateriaalin englanninkielistä versiota. Mainitsit kuitenkin taustakyselyssä osaavasi suomea. Siksi suosittelemme, että käytät suomenkielistä versiota, joka on testatumpi ja hieman laajempi ja muutenkin mukava.
Suomenkielinen materiaali kyllä esittelee englanninkielisetkin termit. Myös suomenkielisessä materiaalissa käytetään ohjelmaprojektien koodissa englanninkielisiä nimiä kurssin alkupään johdantoesimerkkejä lukuunottamatta.
Voit vaihtaa kieltä A+:n valikon yläreunassa olevasta painikkeesta. Tai tästä: Vaihda suomeksi.
Chapter 1.3: Numbers, Words, Sounds, and Pictures
About This Page
Questions Answered: Could we learn some Scala now, please? How do I use numbers in a Scala program? What about text? What’s a convenient way to experiment with individual commands?
Topics: Working in the REPL environment. Some Scala basics:
numbers, arithmetic operations, strings of characters, the
println
command. Accessing packages with import
. Sounds
and images.
What Will I Do? Alternate between reading and practising.
Rough Estimate of Workload:? A bit over an hour.
Points Available: A13.
Related Projects: GoodStuff, which you should already have from the previous chapter. This chapter, and nearly all upcoming ones, also require O1Library.)
Notes: This chapter makes occasional use of sound, so speakers or headphones are recommended. They aren’t strictly necessary, though.
Where Are We?
Now that you have an overview of programming from the previous chapters, let’s start building some concrete skills one small step at a time.
This is the plan:
- Between this chapter and Chapter 1.6, you’ll learn to use selected programming techniques and understand the concepts behind them. You’ll program by giving individual instructions to the computer, one by one. You’ll use various techniques as “separate pieces”: they don’t form an entire application yet, but they’ll be useful when implementing just about any application program.
- In Chapters 1.7 and 1.8, you’ll learn to create commands of your own by combining existing ones.
- From Chapter 2.1 onwards, we’ll put the pieces together and construct application programs. The techniques that you’ll have learned by then will be useful as we do this.
One Plus One
Practically all programs require at least a bit of elementary arithmetic: addition, subtraction, multiplication, and/or division. For instance, the GoodStuff application from the previous chapter divides numbers to determine value-for-money figures, and Pong constantly computes new coordinates for the paddles and the ball while the game is running.
Scala, like other programming languages, gives us tools for computing with numbers. For instance, you can issue this Scala command to instruct the computer to compute one plus one:
1 + 1
Simple. But where should you write such a command?
One option would be to create an entire program that makes use of this command somehow and save that program in a file. But there’s another option that is often more convenient as we try out individual Scala commands:
The REPL
In the Eclipse menu, choose Window ‣ Show View ‣ Scala Interpreter
(in some installations, this is Window ‣ Show View ‣ Other... ‣
Scala Interpreter) to open a view that is commonly known as the REPL. Assuming
you have O1’s official Eclipse preferences set, you can alternatively bring up the REPL
by pressing Alt+F11
. Try it!
When the REPL is launched, Eclipse asks you which project you’d like to associate with the new REPL session. Select GoodStuff. You’ll be greeted with a view that looks like this:
This view is good for interactive experimentation. “REPL” is short for the words read–evaluate–print loop, which convey the basic idea:
- read: You can type in bits of Scala that the REPL receives as input, or “reads”.
- evaluate: The REPL runs your code as soon as it receives it. In the case of arithmetic, for instance, it performs the given calculation to produce a result.
- print: The REPL reports the results of evaluation onscreen.
- loop: This interaction between the user and the REPL keeps repeating as long as the user likes.
Find the prompt at the bottom edge of the REPL. Enter 1 + 1
, for example, and press
Ctrl+Enter
. The output will be something like this:
1 + 1res0: Int = 2
This the Scala REPL’s way of informing you that the result of the computation is two.
res0
comes from the word result and means “result number 0”, that is, the first
result obtained during this REPL session. Here, as in many programming contexts, numbers
run from zero upwards.
Int
is the Scala name for integer numbers. In Scala, as in many programming contexts,
many terms are based on English words.
Ask the REPL to compute some more values. Each result gets reported after the previous ones and labeled with a larger number:
2 + 5res1: Int = 7 1 - 5res2: Int = -4 2 * 10res3: Int = 20 15 / 3res4: Int = 5
Feel free to experiment with other arithmetic operations.
A practical hint
Hold down the Ctrl
key and press the up and down arrows on
your keyboard (on a Mac, Ctrl+Cmd
plus the arrows). As you
see, you can access and rerun the commands you issued previously
during the same REPL session. This is often very convenient.
The REPL vs. a whole program
In the previous chapter, we looked at the GoodStuff application. It was stored in files, which is why it was easy for us to run and rerun the entire app at will. The acts of writing the program code and running it were unmistakeably separate from each other.
Notice how working in the REPL has a different character. The commands you enter there don’t get permanently stored anywhere. In the REPL, the same separation between writing and running a program doesn’t exist: each command is executed as soon as you finish typing it in.
On Expressions
The examples above were extremely simple but they already involve a number of fundamental programming concepts that’ll be essential to understand as we move forward. The most important of these concepts is the expression (lauseke).
For our purposes, it’s not necessary to define the concept formally. This will suffice: an expression, in programming, is a piece of code that has a value (arvo).
So far, we’ve used arithmetic expressions, which get their values from mathetical
computations. For example, 1 + 1
is an expression; its value is the integer two.
Below is one more example in the REPL and a few other key concepts explained in terms of the example. (Reminder: highlight the parts of code associated with a boxed explanation by hovering the mouse cursor over the box.)
50 * 30res5: Int = 1500
Int
is one of the data types defined as part of the Scala
language.It’s perfectly possible to enter just a literal as an input to the REPL, as shown below. The evaluation of such inputs couldn’t be much simpler: the literal’s value is exactly what it says on the tin:
123res6: Int = 123
Already in this chapter — and increasingly in subsequent ones — you’ll see data types
other than Int
, operators other than those for basic math, and expressions more complex
than literal and arithmetic ones.
Spaces around operators
The spaces that surround the operators in our examples are not mandatory and don’t affect the values of the arithmetic expressions. Spaces do, however, make the code a bit easier to read (once we start working with larger amounts of code). Using them is part of good programming practice.
Dividing Numbers
Scala’s arithmetic is largely familiar from mathematics. However, there are some things that may surprise you.
Experiment with division. Try these, for instance:
15 / 5res7: Int = 3 15 / 7res8: Int = 2 19 / 5res9: Int = 3
What if I want to round numbers “right”?
Scala’s way of handling integers is common in programming languages. It’s perhaps surprising how often it’s convenient that integer division works as it does.
There are certainly tools for your other rounding needs. We’ll run into them later (e.g., in Chapter 4.5).
Operator Precedence and Brackets
Some operators have precedence over others. As far as arithmetic operators are concerned, the rules should be familiar from mathematics. Multiplication is evaluated before addition, for instance:
1 + 2 * 3res10: Int = 7 5 * 4 + 3 * 2 + 1res11: Int = 27
You can use round brackets to influence evaluation order. This, too, has a familiar feel:
(1 + 2) * 3res12: Int = 9 5 * ((4 + 3) * 2 + 1)res13: Int = 75
A parenthetical warning
In school math, you may have used a different notation where one set of brackets appears inside another. For instance, you may have written square brackets around regular round brackets just for clarity of reading; each kind of bracket had the same meaning.
That won’t work in Scala code.
In Scala, round brackets are used for grouping expressions (and for a few other things). Square or curly brackets won’t do for that purpose. Those other brackets have other uses, which we’ll get to in due time.
In this matter, Scala is a typical programming language. It’s common in programming that different kinds of brackets have different meanings.
On Decimals
What about decimal numbers?
In many ways, they work as you might expect. In Scala, like in most English-speaking countries, the dot is used as the decimal mark:
4.5res14: Double = 4.5 2.12 * 3.2205res15: Double = 6.82746 4.0res16: Double = 4.0
What’s a “Double”?
The word Double
appears in a number of programming languages.
Indeed, the reason why the word’s been adopted into Scala is
presumably that it’s familiar to programmers turning to Scala
after using other languages.
The name of the data type is short for “double precision floating-point number”, which describes how the type has been implemented: in computing, “decimal numbers” are often represented as so-called floating-point numbers (liukuluku). We’ll say a bit more about them in Chapter 5.2.
As you can tell from the above, “decimal numbers” go by the name Double
in Scala. The
type is oddly named for historical reasons.
When you use values of type Double
, division works more like how you might expect.
Dividing a Double
by a Double
yields a result that is also a Double
:
999.0 / 1000.0res17: Double = 0.999
Combining numerical types
You can use Double
values and Int
values in the same expression:
29 / 10res18: Int = 2 29.0 / 10res19: Double = 2.9 29 / 10.0res20: Double = 2.9 30.0 / 10.0res21: Double = 3.0
If one of the operands is an integer and one is a decimal number, the result is a decimal number. That applies even if the quotient is equal to an integer.
What about more complex expressions that contain different kinds of numbers? Say,
(10 / 6) * (2.0 + 3) / 4
. Here, the operators and parentheses determine the order of
evaluating the subexpressions; that order determines the type of each value produced
as an intermediate result. Can you work out the value of that expression?
To check your thinking, see this interactive animation:
Computer memory and the animations in this ebook
The memory of a computer stores potentially vast amounts of data in the form of bits. As a program runs, it can reserve parts of this memory for various purposes. Each part of memory has its own unique address, a running number of sorts, that identifies that particular memory location.
The bit-level details of how computer memory works are unimportant in this introductory course. Nonetheless, even you the beginner (we presume) will greatly benefit from having a general idea of what gets stored in memory and when. This is why we use animated diagrams, like the one above, that depict the effects of a program run on the contents of memory. The “areas” in these diagrams correspond to parts of computer memory that have been reserved for different purposes.
The first animated example was extremely simple, and you might have understood the expression just fine without it. Even so, it’s a good idea to get familiar with this way of representing program runs, since we’ll be using similar animations to illustrate phenomena that are considerably more complex.
Strings of Characters
Besides numbers, most programs need to manipulate text. Let’s try it:
"Hi"res22: String = Hi "Hey, there's an echo!"res23: String = Hey, there's an echo!
As the REPL tells us, chunks of text or strings (merkkijono) are represented in
Scala by the String
type.
A string literal is a string written into program code as is (cf. the Int
and Double
literals above). String literals go in double quotation marks as shown.
(Thinking back to the previous chapter, you may recall another string literal: the quoted bit in GoodStuff that contained a typo.)
Forget the quotation marks, and you’re liable to get an error message, because the REPL will then try to interpret the text you wrote as a Scala command:
Hi<console>:8: error: not found: value Hi Hi ^
A Command for Printing
The REPL reports the value and type of each expression, which is a useful service. But
suppose we wish to output something other than the familiar litany of resX: SomeType = value
?
Scala’s println
command lets us tailor the output to what we want.
println("Greetings!")Greetings! println(123)123 println(-5.2)-5.2
Even though these commands are fairly self-evident, we can spot several features of Scala that are worthy of our attention:
println
is short
for print line.println
command is given a single parameter
expression that details what the command should print out.(The word “print” may lead the non-programmer’s thoughts to the kinds of printers that churn out paper. But programmers also use this word about outputting text onscreen.)
A parameter expression doesn’t have to be simply a literal. The following animation
portrays the stages of executing the command println("Programming" + (100 - 99))
.
As you saw, execution progressed “from inside the brackets outwards”. Later on, you’ll see how this rule of thumb holds in many situations: the parameter expressions inside the round brackets get evaluated first, producing parameter values (parametriarvo) that are then passed to the command, which uses them as it executes.
An argument about terminology
In other texts on programming, either one or both of the things that we’ve called “parameter expressions” and “parameter values” are often called “arguments”. For further comments on this, please see argument on our glossary page.
A String
ed Instrument
We can use strings of characters to represent text, but strings are useful for representing other things as well. Such as music. Here is a print command that outputs the first couple of bars of Ukko Nooa (“Uncle Noah”), a very simple song traditionally taught to Finnish children as they start to learn the piano, as evidenced on YouTube.
In our program, we represent notes as letters that correspond to the keys of a piano as shown in the picture above.
println("cccedddf")cccedddf
That was none too exciting. It would be more rewarding to play the notes than just printing them out.
Let’s pick up another command. Unlike println
, which is part of the Scala’s basic
toolkit, the command we use next, o1.play
, has been designed for the needs of this very
course. This command will bring some life to programs such as the string-manipulating
examples in this chapter. (The play command is defined in O1Library. If you didn’t import
that project into your workspace in Chapter 1.2, do it now and relaunch your REPL.)
Try the following.
o1.play("cccedddf")
The command produces no visible printout. Instead, it plays the notes listed in its
String
parameter on a virtual piano. Did you have sounds enabled on your computer?
Also try passing other strings as parameters to o1.play
. For instance, you can include
spaces, which o1.play
interprets as pauses:
o1.play("cccdeee f ffe e")
And hyphens, which o1.play
interprets as longer notes:
o1.play("cccdeee-f-ffe-e")
Sound in O1
Here and there during O1, we’ll make use of sound, as we just did. Obviously, this will work only if you have a sounds enabled on your computer.
If you study among other people, please take a pair of headphones along.
If you don’t have access to headphones, you can use println
instead of o1.play
. Parts of this ebook will be duller for it,
but you won’t endanger your course performance.
We don’t require O1 students to have musical ability.
A note about notes
o1.play
uses “European” names for notes. This means that what is
known as a B note
in many English-speaking countries, is known as H instead. Here’s an
example:
o1.play("cdefgah")
The character b
(or alternatively ♭
) denotes flat notes. This plays an
E-flat, for instance:
o1.play("eb")
Strings attached
We can combine strings with the plus operator. When it comes to numbers, the operator stands for addition, but it has a different meaning when strings are involved:
"cat" + "fish"res24: String = catfish "REPL with" + "out" + " a cause"res25: String = REPL without a cause
Notice that spaces inside the quotation marks do affect the result.
Of course, the same operator works fine even in a parameter expression:
println("Uncle Noah, Uncle Noah," + " was an upright man.")Uncle Noah, Uncle Noah, was an upright man. o1.play("cccedddf" + "eeddc---")
String multiplication
Another way to change octaves
In addition to supporting <
and >
as just described, o1.play
lets you mark an octave number for any individual note. You can use
the numbers from 0 to 9; the default octave is number 5. The following
two commands have the same effect, for instance:
o1.play(">cd<<e")o1.play("c6d6e4")
This is unimportant as such, since it’s specific to a particular
music-playing command created for the purposes of this course.
Still, this can be convenient to know if you choose to mess around
with o1.play
just for fun.
Pictures and Packages
GoodStuff marks the favorite experience with a picture of a grinning face. Pong displays the paddles as pictures of rectangles and the ball as a picture of a circle. Programs manipulate pictures.
Displaying a picture
Let’s use Scala to load an image from a network address. Try it.
o1.Pic("https://en.wikipedia.org/static/images/project-logos/enwiki.png")res26: o1.gui.Pic = https://en.wikipedia.org/static/images/project-logos/enwiki.png
Pic
is the name of a data type for
representing images and it’s spelled in upper case just as
String
and Int
are.o1
indicates that we’re again using a tool created for
this course. Not to worry, though: as you use these tools, you are
sure to pick up general principles that are useful outside O1 as well.Pic
that holds image data loaded from
the net.Where did that get us? A particular image is now stored in your computer’s memory, but
we didn’t get to see it. One easy way to display an image is o1.show
, which works for
pictures much like o1.play
did for sounds. See below for an example. We’ll come across
other ways of displaying images later on.
o1.show(o1.Pic("https://en.wikipedia.org/static/images/project-logos/enwiki.png"))
The image appears in a separate little window in the vicinity of the top-left corner of
your screen. Click it or press Esc
to close the window.
In the preceding example, we loaded the picture from the net. You can also load an image from a file stored on your computer’s disk drive. Like so:
o1.show(o1.Pic("d:/example/folder/mypic.png"))
On the other hand, it’s often not necessary to write the full path. If the image
file is located within an active project, there is a simpler way. Assuming you
selected GoodStuff when you launched the REPL, the following will also work, since
face.png
is a file within that project.
o1.show(o1.Pic("face.png"))
o1
and other packages
You’ve seen that some of the tools that we use are located in a package called
o1
. When we use those tools, we need to mention in the Scala program that we’re
accessing that package.
There are a number of reasons why programming tools are placed in different packages.
One of the main ones is to avoid name clashes: when building a larger program, perhaps as
a collaborative effort, or when using tools built by others, you easily end up with the
same name being used for different things in different places. For instance, the names
play
or show
might mean something else in a context other than o1
.
A solution is to place the tools with the same name in different packages. Then we can use the package names to indicate which tool we use. A downside of this approach is that repeatedly typing in the package name is tiresome and can make the code harder to read.
We can mitigate the downside with the import
command. Let’s get to know it before
we get back to working with pictures.
Convenient use of tools with import
When we plan to repeatedly use a command in package o1
— say, o1.show
— we can
first indicate this intention:
import o1.showimport o1.show
This import
command means, roughly: “Wherever it says show
below, it refers to the
show
command that’s defined in package o1
.” The import
command isn’t an expression
and doesn’t have a value. The REPL acknowledges it by simply repeating the command text
back at us.
Having issued the import
, we can now work with pictures a bit more conveniently:
show(o1.Pic("https://en.wikipedia.org/static/images/project-logos/enwiki.png"))show(o1.Pic("face.png"))
show
with o1
.Pic
, though. If we had also given the
command import o1.Pic
earlier, we could have omitted this
prefix, too.We could type in a separate import
command for each of the tools in package o1
that
we intend to use. But instead, we’ll often use this handy notation to import the entire
contents of the package in one go:
import o1._import o1._
_
has various context-dependent meanings in
Scala, but generally speaking it means something like “any” or “all”.Now we can use any tool in the package with no hassle:
play("cdefg")show(Pic("https://en.wikipedia.org/static/images/project-logos/enwiki.png"))show(Pic("face.png"))
Fair enough, the import
s didn’t save all that many keystrokes yet, but they will, as
we’ll soon use more tools from the package. In later chapters, we’ll be importing many
other tools from many other packages.
Colored shapes
The Pong game is an example of a program whose graphics are based on familiar geometric shapes. Our package provides tools that make it easy to define such shapes in different colors. Let’s play with these tools a bit.
Let’s start with colors. Assuming the command import o1._
has been issued, you can refer
to common colors simply by their English names:
More colors
For the whole list of named colors available, see the list in O1Library’s documentation.
Blueres27: o1.gui.Color = Blue Greenres28: o1.gui.Color = Green DarkGreenres29: o1.gui.Color = DarkGreen
Color
.Let’s use the circle
command to create a blue circle:
circle(200, Blue)res30: o1.gui.Pic = circle-shape
Pic
data type that already featured in earlier examples.Any value of type Pic
is a valid parameter for show
. Instead of an image from the net
or your local drive, we can show a circle whose attributes we define in the Scala code:
show(circle(200, Blue))
Experiment with other colors and shapes. For instance, here are a couple of rectangles and an isosceles triangle:
show(rectangle(200, 500, Green))show(rectangle(500, 200, Red))show(triangle(150, 200, Orange))
About the REPL
Perhaps you have wondered whether the REPL is a tool for students/beginners only rather than for serious professionals?
No, it isn’t.
Many professionals, too, use a REPL for experimentation and testing. Besides, even serious professionals are students as they familiarize themselves with new programming languages and program components created by others.
Complex program components can be used in the REPL, including the components of specific applications. Even though we haven’t practiced coding much yet, you can already easily use the REPL to experiment with a component of an existing program. Try the following if you want.
Launching the GoodStuff GUI in the REPL
In the previous chapter, you used the menu in Eclipse to launch the GoodStuff GUI. It’s also possible to display the GUI window by entering the following commands in the REPL.
import o1.goodstuff.gui.CategoryDisplayWindowimport o1.goodstuff.gui.CategoryDisplayWindow import o1.goodstuff.Categoryimport o1.goodstuff.Category new CategoryDisplayWindow(new Category("Hotel", "night"))...
Here’s a brief explanation: This is how we tell the computer to
create a new category of experiences for hotels (or whatever you
choose, if you replace the string literals with something else) and
a new GUI window that enables you to record new experiences.
To accomplish all this, we use tools defined in the packages
o1.goodstuff
and o1.goodstuff.gui
.
The exact meaning of the example code will be clearer to you after the first couple of weeks of O1.
Summary of Key Points
- You can combine integers (
Int
), decimal numbers (Double
), and arithmetic operators to form arithmetic expressions. - An expression is a piece of code that has a value. To evaluate an expression is to determine the expression’s value.
- You can use the type
String
to form expressions whose values are strings of characters, such as text. - You can use the
println
command to output strings or other values. - The
import
command comes in handy when you want to use the tools contained in a particular package, such as theo1
package that we often use in O1. - The
o1
package gives us tools for playing notes represented as strings (play
) and working with images (Pic
,show
,circle
,Red
, etc.). - The REPL environment is convenient for experimenting with new programming techniques, testing, and learning.
- During the first weeks of O1, you’ll find out how the basic data types, expressions, and operations from this chapter can be used for building GoodStuff or another application program.
- Links to the glossary: REPL; expression, value, to evaluate, literal; operator; data type; string; to print; parameter expression, parameter value (or argument).
Below is a diagram that is intended to clarify some of the most important concepts we have covered and their main relationships. We’ll add to this diagram later.
Learn the concepts in this chapter!
Many of the terms and concepts introduced in this chapter are central to O1. Not simply for their own sake, but because you can use these concepts to make sense of programming phenomena. Knowing the terminology will help you both read this ebook and communicate with other programmers (such as your student pair or the teaching assistants). Pay particular attention to the concepts in the diagram above.
But it’s even more important that...
... you get a feel for programming in practice. The REPL is great for this. Don’t hesitate to try things in the REPL, including things that aren’t specifically suggested in these chapters.
Feedback
Please note that this section must be completed individually. Even if you worked on this chapter with a pair, each of you should submit the form separately.
Credits
Thousands of students have given feedback that has contributed to this ebook’s design. Thank you!
Weeks 1 to 13 of the ebook, including the assignments and weekly bulletins, have been written in Finnish and translated into English by Juha Sorva.
Weeks 14 to 20 are by Otto Seppälä. That part of the ebook isn’t available during the fall term, but we’ll publish it when it’s time.
The appendices (glossary, Scala reference, FAQ, etc.) are by Juha Sorva unless otherwise specified on the page.
The automatic assessment of the assignments has been programmed by Riku Autio, Jaakko Kantojärvi, Teemu Lehtinen, Timi Seppälä, Teemu Sirkiä, and Aleksi Vartiainen.
The illustrations at the top of each chapter, and the similar drawings elsewhere in the ebook, are the work of Christina Lassheikki.
The animations that detail the execution Scala programs have been designed by Juha Sorva and Teemu Sirkiä. Teemu Sirkiä and Riku Autio have done the technical implementation, relying on Teemu’s Jsvee and Kelmu toolkits.
The other diagrams and interactive presentations in the ebook are by Juha Sorva.
The O1Library software has been developed by Aleksi Lukkarinen and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.
The pedagogy of using tools from O1Library (such as Pic
) for simple graphical programming
is inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and
Krishnamurthi and Picturing Programs by Stephen Bloch.
The course platform A+ has been created by Aalto’s LeTech research group and is largely developed by students. The current lead developer is Jaakko Kantojärvi; many other students of computer science and information networks are also active on the project.
For O1’s current teaching staff, please see Chapter 1.1.
Additional credits appear at the ends of some chapters.
*
.