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 ohjelmien 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.7: Creating Custom Functions
About This Page
Questions Answered: How do I write a Scala function? How does a function do what it does? How can I make a function use the values that it receives as parameters?
Topics: Defining functions in program code: def
, parameter
variables, the function body, returning a value, local variables.
The stages of executing a function; the call stack and frames.
What Will I Do? Read and practice. Program both in the REPL and outside of it.
Rough Estimate of Workload:? A couple of hours or more. For many students, this will be the most challenging chapter thus far. Even though we don’t do anything particularly complex yet, your first steps as a programmer may be coltish, and that’s normal. It will also turn out that Scala’s punctuation can be a bit involved.
Points Available: A90.
Related Modules: Subprograms.
Notes: This chapter makes occasional use of sound, so speakers or headphones are recommended. They aren’t strictly necessary, though.
Introduction
In this chapter, we’ll revisit some of the functions we already discussed and introduce some new ones. But what’s really new here is that now we’ll be taking a look at the program code that implements the functions.
To experiment with these functions, start up your REPL as you did in the previous chapter:
make sure you have the Subprograms module selected in the Project view as you
hit Ctrl+Shift+D
. (An alternative way is to open the code file from Subprograms in
the editor and then launch the REPL.)
The program code for the chapter is available in o1/subprograms.scala
within the
module, in case you want to take a look.
Defining a Function
The average
function is familiar to us from Chapter 1.6. We can use it in the REPL as
shown here:
average(2.0, 5.5)res0: Double = 3.75
In order for that command to work, the averaging function needs a definition. This example function’s definition is fairly simple but nevertheless introduces a number of important concepts.
In English, the function’s operating principle is: “When the function average
is
called, it produces a return value by adding the value of the first parameter to the
value of the second parameter and dividing the sum by two.” This principle is captured
in Scala below.
(Side note: From here on in, our examples will gradually grow in complexity, and many of them will be displayed in boxes such as this one, with different parts of the program highlighted in different colors. As you will have noticed, IntelliJ does something very similar albeit in different colors.)
def average(first: Double, second: Double) = (first + second) / 2
average
, one must pass
two Double
values as parameters”. Speaking more generally,
this is where we define the function’s parameter variables
(parametrimuuttuja; also known as formal parameters) and
their data types. In Scala, the names of data types are usually
capitalized.first
and the second
second
.Note that the parameter variables first
and second
genuinely are variables —
val
variables, to be more specific. Parameter variables don’t receive values from an
assignment command but indirectly through function calls; apart from that, though, you
can use them just like the other variables that you’ve seen.
Function Execution and the Call Stack
The following animation shows another diagram of data in computer memory. The diagram evolves step by step as the program runs. This time, we’ll also inspect what happens within a function: you’ll see how parameter variables work, for instance. Examine this animation thoroughly! It introduces concepts that play crucial roles in the programs that we’re about to write.
The piece of code illustrated in the animation doesn’t accomplish anything much. It just calls the averaging function a couple of times.
The animation featured frames (kehys). The frames formed a call stack (kutsupino), which is often known simply as the stack and which indeed works like a real-world stack of items: when a function call begins, a new frame is added to the top of the stack, and when a call terminates by returning a value, that topmost frame gets removed from the stack. The computer uses the frames of the call stack to keep track of which function calls are active and what data is associated with each of those calls. At any given time, it’s the topmost frame of the stack that is active, while the lower frames (of which there was just one in this animation) wait for the frame above to finish its task before resuming theirs.
Hopefully, the animation already helped you make sense of how a function call unfolds. Be that as it may, the significance of the call stack and its frames will soon be highlighted as we take on some more complex functions.
A function call... is what exactly?
Chapter 1.2 mentioned that we use the word “program” in reference to static program code (as in “The program has 100 lines.”) but also in reference to the dynamic processes that take place when programs are run (as in “The program stored the information entered by the user.”).
“Function call” is similarly ambiguous. Sometimes we mean an expression written in code: “The function call on line 15 has a punctuation error.” And sometimes we refer to processes like the one in the preceding animation, which take place when a function is executed: “The function call is over and yielded a return value of 4.”
Be wise to both meanings as you go through this ebook and other resources on programming.
Practice on Defining a Function
Programming Exercise: meters
Task description
In the United Kingdom and some of its former colonies, feet and inches are still widely used for measuring length. One inch equals 2.54 cm, and one foot is 12 inches. Measurements are commonly expressed as a combination of these units. For instance, a person who is about 180 cm tall might be described as “five foot eleven”: five feet plus eleven inches.
Implement an effect-free Scala function that:
- is called
toMeters
; - takes in, as its first parameter, a number of feet represented as a
value of type
Double
; - similarly takes in a number of inches as its second parameter; and
- computes and returns a
Double
equal to the total length in meters of the given feet and inches (e.g., given the values 5 and 11, returns 1.8034).
Proceed in the following stages: set up the assignment, write some code, test. Keep adjusting and testing your program until it works.
There are some additional instructions for each stage below.
Setup
In IntelliJ, find the Subprograms module and its file o1/subprograms.scala
. Near the top
of the file, there is a comment that shows you where you’ll be writing your code as you
work on this chapter.
Writing your function
First, make sure you understand precisely what the function is supposed to do! This sounds self-evident, sure, but it’s easy to forget. You can lay substantial pain upon yourself just by overlooking something simple in the task description.
Write the function definition in the indicated section of the file. In this case, one line is enough.
- Start with
def
and the name of the function. - Type in the parameter definitions. Choose sensible names for the parameter variables.
- Write the function body. A single arithmetic expression will do to convert between the units. (Don’t print out the resulting value; simply return it!)
Error messages in IntelliJ
As you edit your code, you’ll see some saw-toothed highlights and other annotations in red. Some of your code may also turn red. This is IntelliJ’s way of letting you know that it has spotted problems.
Don’t be flustered if your program lights up in red as soon as you start writing. IntelliJ adjusts these annotations constantly as you type, and incomplete code often prompts a complaint. For instance, when you’ve typed in the word
def
and a name, IntelliJ will cry out, noticing that your function definition is incomplete.The red highlights can be a great help, and you should take them seriously, but only if they remain after you consider your
toMeters
function to be ready.You can view more detailed error messages by hovering your mouse cursor over red highlights. Unfortunately, it can be hard to understand what each error message means if you’re going by just what we’ve covered in O1 so far; at this stage, you may find it easier to fix your code by comparing it to the ebook’s examples.
- Start with
Take care that no red error annotations remain. In particular, eliminate any errors in punctuation. Did you use at least round brackets, a comma, and an equals sign? Were you consistent in how you wrote the name of each variable?
Compiling your code
Before the computer can run your Scala code (in the REPL or otherwise), it needs to compile (kääntää) the code into a form that it can execute. As it compiles your code, the computer also thoroughly checks that it follow’s the rules of the programming language. That compilation-time check may produce errors that you need to fix before your code can run.
IntelliJ compiles your code automatically whenever you launch a program either for the first time ever or after modifications. You can also ask IntelliJ to compile your program at any time, to make sure that there are no automatically detectable errors. Let’s try that now.
In IntelliJ’s menu, select Build → Build Module 'Subprograms' or
press the corresponding shortcut key F10
. (In IntelliJ, the command for compiling
your code is known as building, which word refers generally to preparing a program for
use.)
After a moment, one of the following happens.
Either:
- You don’t get any error messages, only a barely noticeable text
at IntelliJ’s bottom-left corner: Build completed
successfully. If this happened, great, but do make an intentional
little formatting error in your code now and press
F10
again, just to see the other possible outcome.
Or:
- The Messages tab pops up with one or more error
messages. This list is generally similar to what accompanies the
red highlights in the editor (but may be more comprehensive).
You’ll need to fix your code. After fixing it, press
F10
again.
Fixing errors and compiling your code
Before running a program you wrote, always make sure you have gotten rid of the errors highlighted by IntelliJ. The next step of testing your code will surely fail if you don’t!
IntelliJ is quite clever and notices many errors that prevent the code from running “in advance”, immediately as you type code into the editor. This is not foolproof, however, and some errors only show up when you compile the entire code.
Since IntelliJ compiles your code automatically when you run it —
such as when you launch the REPL — it’s not necessary that you
expressly ask IntelliJ to compile. But it’s very easy to just
hit F10
whenever you feel like it and so have your code’s
grammaticality checked.
Testing your function
Start a REPL session (
Ctrl+Shift+D
), making sure that the Subprograms module loads up there. Note that in order to test your new code, you need a fresh REPL session.Try calling your function on different parameter values. Does it work right? If not, return to Step 1 of Writing your code above (yes, to Step 1, not straight to Step 2). Ask the O1 staff for help as needed.
(P.S. Did you notice that the result is expected in meters, not in centimeters?)
On “rebooting” the REPL
Whenever you edit your program and wish to test the new version in the REPL, you need to load the new code into the REPL. You can either close the REPL and start it again or press the Rerun icon in the REPL’s top left-hand corner. If you then wish to re-enter some of your earlier commands, remember that you can access them using the arrow keys.
Submit your solution
When you’re confident that your function works, enjoy the moment and submit your solution via IntelliJ.
A+ presents the exercise submission form here.
More simple functions
If you wish, you can build some programming routine with this voluntary and easy practice task that greatly resembles the previous assignment.
In the same file, subprograms.scala
, write the following functions:
volumeOfCube
, which expects a single parameter that represents a cube’s edge length and returns the cube’s volume. The parameter and the return value are bothDouble
s.areaOfCube
, which similarly computes and returns a cube’s area, given its edge length.
A+ presents the exercise submission form here.
Defining Functions within the REPL
In the previous assignment, you used IntelliJ’s editor to write and save your functions in a separate file. That’s what we’ll be doing in upcoming assignments as well. However, it’s good to realize that you can also enter function definitions straight into the REPL:
def average(first: Double, second: Double) = (first + second) / 2average: (first: Double, second: Double)Double average(2, 1)res2: Double = 1.5
This can be quite convenient when experimenting. Howevever, using IntelliJ’s editor is better preparation for future assignments and makes for convenient A+ submissions. Saving files in the editor also lets you keep the code you wrote.
As you experiment with Scala commands on your own, feel free to define functions in the REPL.
Practice on Functions and Modulo
The modulo operator
In addition to the everyday arithmetic operators, programmers commonly employ the modulo
operator, which is written in Scala as %
. This operator is usually associated with
integer numbers; one might evaluate the expression 25 % 7
, for example.
Now go and experiment with the modulo operator on your own in the REPL.
Example: modulo and parity
For various reasons, we may find it useful to determine a number’s parity: whether it is even or odd. We might, for instance, wish to do something to every other element of a list, such as coloring the even and odd rows of a large table in a different background colors. Right now, we’re going to use parity simply as an additional example of defining a custom function and applying the modulo operator.
Let’s create an effect-free function that returns zero to signal that a given number is even. Otherwise, it will return either 1 or -1, matching the sign of the given number. Like so:
parity(100)res3: Int = 0 parity(2)res4: Int = 0 parity(103)res5: Int = 1 parity(7)res6: Int = 1 parity(-7)res7: Int = -1
The function is easily implemented with the modulo operator: if we divide a number by two, we get a remainder of zero if and only if the number was even. Here’s a Scala implementation:
def parity(number: Int) = number % 2
Go ahead and define the function and experiment with it in the REPL, if you want. Or continue right away to the next assignment.
Chess boards: introduction
Let’s say we’ve numbered the squares on a chess board from 1 to 64 starting at the top left and progressing from left to right one row at a time. Moreover, we have numbered the rows and columns (ranks and files in chess lingo) from 1 to 8 each. There’s a picture right there.
If we know the number of a square, such as 35, how do we get the computer to work out which row and which column the square is at? One possibility is to define two effect-free functions that can be used as follows.
row(35)res8: Int = 5 column(35)res9: Int = 3
Here are the function definitions:
def row(square: Int) = ((square - 1) / 8) + 1
def column(square: Int) = ((square - 1) % 8) + 1
Chess boards: an assignment
But what if we had instead numbered the squares from 0 to 63 and the rows and columns
from 0 to 7, as shown in this picture? Write two effect-free functions, row
and
column
, that work like this:
row(34)res10: Int = 4 column(34)res11: Int = 2
Instructions and hints:
- Use the same workflow as in the
toMeters
assignment. - These functions, too, should go in
subprograms.scala
. - If you understand the given versions above, where numbering runs from one upwards, this isn’t a difficult assignment. Under the zero-based numbering scheme, the solution actually simpler.
- Test your code in the REPL before you submit.
A+ presents the exercise submission form here.
Consecutive Commands in a Function
Just to try out something, let’s create a function that prints out the same four lines of
text every time it’s called, followed by a fifth line that the function’s caller must pass
in as a parameter. We intend to be able to use our function, careerAdvicePlease
, like this:
careerAdvicePlease("I don't know what to do.")Oppenheimer built the bomb, but now he's dead. (Dead!) Einstein was very, very smart, but not enough not to be dead. (Dead!) So don't go into science; you'll end up dead. Don't go into science; you'll end up dead. I don't know what to do. careerAdvicePlease("Such is the human condition.")Oppenheimer built the bomb, but now he's dead. (Dead!) Einstein was very, very smart, but not enough not to be dead. (Dead!) So don't go into science; you'll end up dead. Don't go into science; you'll end up dead. Such is the human condition.
Here is the function definition:
def careerAdvicePlease(addendum: String) = {
println("Oppenheimer built the bomb, but now he's dead. (Dead!)")
println("Einstein was very, very smart, but not enough not to be dead. (Dead!)")
println("So don't go into science; you'll end up dead.")
println("Don't go into science; you'll end up dead.")
println(addendum)
}
careerAdvicePlease
function is defined over multiple lines
of code. In cases like this, we place the lines within curly
brackets. What this means is that each of the five println
commands gets executed, in order, whenever careerAdvicePlease
is called.In a technical sense, Scala is quite permissive about whitespace, but it’s good programming style to use space to clarify the relationships between pieces of program code. Indenting is part of that. In Scala, indentations that are two spaces wide are common.
What about the return value?
Our careerAdvicePlease
function returns the contentless Unit
value, or “returns
nothing”, so to speak. This is because the command in the function body that is
executed last determines the return value of the entire function. In our example,
the last command to be executed is the last of the println
s, which yields only a
Unit
value (as discussed in Chapter 1.6). Therefore, the same Unit
value is
also returned by the careerAdvicePlease
function that calls println
.
Punctuation in Function Definitions
There are a number of punctuation rules that govern function definitions in Scala. Newcomers to the language may find these rules distasteful at first, and indeed such rules aren’t the most delicious dish that programming has to offer. Nevertheless, we do need to take a moment to ingest them. For starters, let’s have equals:
The equals sign
Whenever you define a function, always remember to write the equals sign between the parameter list and the function body. Forget to do so, and you may be faced with some rather cryptic error messages or elusive bugs.
Curly brackets and line breaks
In the careerAdvicePlease
function, it was necessary to use the curly brackets because
we wanted to group multiple print commands in the same function. There is in fact nothing
preventing us from using curly brackets and line breaks even in smaller functions. For
instance, all three implementations of average
below do the same thing.
Version 1:
def average(first: Double, second: Double) = (first + second) / 2
Version 2:
def average(first: Double, second: Double) =
(first + second) / 2
Version 3:
def average(first: Double, second: Double) = {
(first + second) / 2
}
Many programmers have adopted the following conventions for writing functions in Scala:
- If the function body consists of a single expression and the
function is effect-free (Chapter 1.6), the curly brackets are
omitted.
- If the expression is short, it can be placed
on the same line with the
def
keyword. Version 1 is therefore the recommended way to writeaverage
. - If the line would become excessively long, insert a line break after the equals sign (as in Version 2).
- If the expression is short, it can be placed
on the same line with the
- If the function body contains multiple commands or if the function
is effectful (e.g., it prints out something), use line breaks, curly
brackets, and indentation. This is what we did in
careerAdvicePlease
and in Version 3 above.
In this ebook, we generally follow those style conventions. We recommend that you do the same. You may find the conventions confusing at first, but don’t let that bother you too much when writing your own programs. If it feels easier for you, go ahead and format all your functions as in Version 3. It’s always okay to write the curly brackets!
Summary of punctuation rules
Below is the summary table from the previous chapter with added notes on punctuation. Hover the mouse over the underlined bits to view additional explanations.
Does the function affect state? | Does it return a value? | The term we use in O1 | Write an equals sign in front of the function body? | Write curly brackets around the body? | Use line breaks and indentations? |
---|---|---|---|---|---|
Never | Yes | Effect-free function | Yes | At least if the body has multiple commands. | Always where you have curly brackets. Also used for splitting long one-liners. |
No | (If a function doesn’t have an effect on program state or return a value, it can’t be very useful, barring some special circumstances. You won’t need to write such an empty function in O1.) | ||||
At least sometimes | Yes | Effectful function | Yes | Yes | Yes |
No | Effectful function | Yes (Your code will work without it, but write it anyway. Required in later Scala versions.)` | Yes | Yes |
How do I know if my function is effectful?
Imagine the scene. Your program is running and is in a particular state: certain things have been stored in memory and something is displayed onscreen. A function is then called. It does its job and produces a return value; a bit of time passes. Apart from that, is the program back in the state where it was before the function call?
- If the called function is, say,
average
, we are effectively back to where we were: we obtained a result but nothing else changed. This is an effect-free function. - If the called function is, say,
println
, we aren’t back to the original state: the program has generated a line of output. This is an effectful function.
Search terms for the interested
For further reading, do a web search for referential transparency or side effect.
Here’s another way to think about it: if the program had access to the return value of
the function already, would it even be necessary to call the function? In the case of our
effect-free averaging function, the answer is no: we could have just as well replaced
the call average(5, 10)
with the literal 7.5
. In the case of the effectful println
,
on the other hand, we don’t get any printing done without calling the function.
Assignment: Text Accompanied by Music
Task description
Write an effectful function called accompany
that prints out some text and accompanies
it with sound. The function should take two parameters, each of which is a string: the
function prints out the first string and plays the second string.
When you’re done, it should be possible to use the function as in this example.
accompany("Nananana nananana nananana nananana BATMAAN!", "[49]<" + "(d<g>)(d<g>)(db<g>)(db<g>)(c<g>)(c<g>)(c#<g>)(c#<g>)" * 2 + "[54]>(FG)-.(FG)---/160")Nananana nananana nananana nananana BATMAAN! accompany("... and introducing acoustic guitar", "[26] >EF#A---G#---F#---E---F#G#---------EF#A---G#---F#---E---F#EF#---------/192")... and introducing acoustic guitar
The line breaks in the example are there just to prevent overlong lines. They aren’t strictly necessary.
Instructions and hints
- Write your solution in the same file,
subprograms.scala
. - Pick sensible names for the parameter variables.
- In this assignment, the order in which you call
println
andplay
within the function body has no practical significance. Take your pick, as long as you do both.- The
play
function has been defined so that it plays the notes in the background and the computer doesn’t wait for the sound to finish before executing the next command. So the printout will appear promptly even if you place theplay
command before theprintln
.
- The
- Follow the guidelines on programming style given above and split your code over multiple lines. Remember to punctuate.
A+ presents the exercise submission form here.
Tools for Building New Functions
The preceding examples have shown that when you implement a function body, you can apply
the programming techniques that you know from earlier chapters: arithmetic operators,
println
, play
, etc.. Other familiar commands work, too. For instance, a bit
further down in this chapter, we’re going to define new variables within a function.
Throughout O1, you’ll learn additional tools for building your own functions: you’ll be
able to make functions choose between alternatives, execute commands repeatedly, and so on.
One very common thing to do in a function is to call another function. For instance,
we can use the hypotenuse function from package scala.math
to create a function that
determines the distance between two points (x1,y1) and
(x2,y2):
def distance(x1: Double, y1: Double, x2: Double, y2: Double) = hypot(x2 - x1, y2 - y1)
The picture functions from package o1
, introduced in Chapter 1.3, are also available to
us as we create new functions:
A picture-creating function
This function produces red “balls” of different sizes:
def redBall(size: Int) = circle(size, Red)
A usage example:
val ball = redBall(50)ball: Pic = circle-shape val biggerBall = redBall(300)biggerBall: Pic = circle-shape show(biggerBall)
Vertical bars
In subprograms.scala
, create a function called verticalBar
that forms pictures of
blue rectangles that are ten times as many pixels high as they are wide.
The function should take a single parameter of type Int
, namely the width of the bar.
Here’s a usage scenario:
val picOfBar = verticalBar(80)picOfBar: Pic = rectangle-shape show(picOfBar)show(verticalBar(180))
Give a meaningful name to the parameter variable. Use show
to test your function in
the REPL. Note that your function must not call show
and display the picture; it must
return the picture. Then you’ll be able to use your function and show
in combination
as illustrated above.
You can return to Chapter 1.3 for a recap on how to create shapes.
A+ presents the exercise submission form here.
An overloaded function
Continue from the previous assignment by creating another verticalBar
function.
This is, do not erase the original you just wrote but add another function with the
same name. This new function should take two parameters, a width (Int
) and a color
(Color
). It should be usable like this:
val blackBar = verticalBar(80, Black)blackBar: Pic = rectangle-shape show(blackBar)
Clearly, this second verticalBar
function will be more flexible (more abstract) as
far as color is concerned. On the other hand, it requires its caller to pass in an
additional parameter. This second function, too, returns a picture that’s ten times
as high as it’s wide.
A+ presents the exercise submission form here.
As you see, Scala allows us to define multiple functions with the same name, even in the same context. This is called overloading (kuormittaa) the function name. Overloading is allowed only if the functions that share a name differ in which parameters they take.
Despite their shared name, overloaded functions (here: the two verticalBar
functions
you wrote) are completely distinct from each other. When you use the functions’ name,
the parameter expressions that you provide determine which function gets called.
Practice Understanding Functions
Local Variables
Example: tax rates
Let’s write an effect-free function that calculates how much income tax one has to pay under this simple system of taxation:
- Until a specific threshold income, one pays a base percentage of one’s income as tax (e.g., 20 percent), as set by the government.
- In addition, one must pay a higher percentage (e.g., 40 percent) of all that one earns above the threshold (if anything).
When invoking our function, incomeTax
, we’d like to pass in the earner’s total income,
the threshold income, the base rate of taxation, and the additional rate, as shown below.
incomeTax(50000, 30000, 0.2, 0.4)res12: Double = 14000.0 incomeTax(25000, 30000, 0.2, 0.4)res13: Double = 5000.0
It would be possible to define this function as a one-liner, whose body consists of a single expression that evaluates to the function’s return value. But we can do better than that. Let’s store intermediate results in variables so that our code is a bit neater.
def incomeTax(income: Double, thresholdIncome: Double, baseRate: Double, additionalRate: Double) = {
val baseTax = min(thresholdIncome, income)
val additionalTax = max(income - thresholdIncome, 0)
baseTax * baseRate + additionalTax * additionalRate
}
Local variables are accessible only within the program code of the function itself. A
function’s caller doesn’t need to be aware of the called function’s local variables,
nor could the caller use them even if they were so aware. Any user of incomeTax
, for
instance, can remain blissfully unaware of what the function’s local variables are called
and indeed the fact that the function has any local variables to begin with. And the
following attempt to tamper with the function’s internal variables is unsuccessful,
irrespective of whether we have previously called incomeTax
.
additionalTax * additionalRate<console>:14: error: not found: value additionalTax additionalTax * additionalRate ^
As a matter of fact, the local variables don’t even exist during the entirety of the program run but only while the function is being executed. Local variables are created within the frame associated with the function call on the call stack. As soon as the function call is over, any memory reserved for the variables is released for other use. This is depicted in the animation below.
More function-reading practice
Exercise: Course Grades
Task description
Create an effect-free function that determines a student’s overall course grade according to a specific course policy. In our imaginary example course, the overall grade is the sum of a project grade (between 0 and 4), a bonus from an exam (0 or 1), and a bonus for active participation (0 or 1). However, the overall grade can never exceed 5.
Your function must adhere to the following specification.
- Its name is
overallGrade
. - As parameters, it expects three integers: a project grade and the bonuses for exam and participation, in that order.
- It returns the corresponding overall grade between 0 and 5, also as an integer. (It doesn’t print the grade but returns it.)
You must ensure that even if the sum of the components is more than five, the overall grade isn’t. However, you don’t need to concern yourself with what happens if someone were to call your function on invalid parameter values (such as a negative project grade or an excessive exam bonus).
Instructions and hints
Once again, write your code in subprograms.scala
.
Follow the now-familiar workflow recapped below.
- Make sure you understand precisely what the function is supposed to do!
- Implement the function in stages: name, parameter variable (with sensible names!) and their types, function body.
- Reset your REPL.
- Test your function on a variety of parameter values. Go back to Step 1 if needed.
- Submit your program only when you’re satisfied that it works.
Chapter 1.6 introduced a mathematical function that we already used in this chapter
too and that you’re likely to find useful. (If you experiment with those functions in
the REPL, remember to import scala.math._
. That import
already appears at the top
of subprograms.scala
, so the math functions are available in that file.)
A+ presents the exercise submission form here.
Summary of Key Points
- A function definition includes the function’s name, its parameter variables and their data types, and a function body; all in all, it is an implementation for an algorithm that takes care of whatever the function is expected to do.
- When a function call starts, the computer reserves a so-called
frame of memory to track the call.
- The parameter values received by the function are stored in variables within the frame.
- You can also define additional variables in the frame. Such a variable is known as a local variable.
- Frames form a call stack (which we’ll discuss further in the next chapter).
- Links to the glossary: function, function call, function body; frame, call stack; local variable, parameter variable, to overload.
The diagram below includes a handful of concepts that are central to how functions work on the inside.
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!
The ebook’s chapters, programming assignments, and weekly bulletins have been written in Finnish and translated into English by Juha Sorva.
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 developed by: (in alphabetical order) Riku Autio, Nikolas Drosdek, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, 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 did 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 O1Library for simple graphical programming (such as Pic
) 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+ was originally created at Aalto’s LeTech research group as a student project. The open-source project is now shepherded by the Computer Science department’s edu-tech team and hosted by the department’s IT services. Markku Riekkinen is the current lead developer; dozens of Aalto students and others have also contributed.
The A+ Courses plugin, which supports A+ and O1 in IntelliJ IDEA, is another open-source project. It was created by Nikolai Denissov, Olli Kiljunen, and Nikolas Drosdek with input from Juha Sorva, Otto Seppälä, Arto Hellas, and others.
For O1’s current teaching staff, please see Chapter 1.1.
def
keyword followed by a name for the function, as chosen by the programmer. In Scala, it’s customary to start function names with a lower-case letter, just as we did earlier with variables.