ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.01.2026

Просмотров: 409

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

10

Chapter 1 The Basics

 

1.6 The apply Method

In Scala, it is common to use a syntax that looks like a function call. For example, if s is a string, then s(i) is the ith character of the string. (In C++, you would write s[i]; in Java, s.charAt(i).) Try it out in the REPL:

"Hello"(4) // Yields 'o'

You can think of this as an overloaded form of the () operator. It is implemented as a method with the name apply. For example, in the documentation of the StringOps class, you will find a method

def apply(n: Int): Char

That is, "Hello"(4) is a shortcut for

"Hello".apply(4)

When you look at the documentation for the BigInt companion object, you will see apply methods that let you convert strings or numbers to BigInt objects. For example, the call

BigInt("1234567890")

is a shortcut for

BigInt.apply("1234567890")

It yields a new BigInt object, without having to use new. For example:

BigInt("1234567890") * BigInt("112358111321")

Using the apply method of a companion object is a common Scala idiom for constructing objects. For example, Array(1, 4, 9, 16) returns an array, thanks to the apply method of the Array companion object.

1.7 Scaladoc

Java programmers use Javadoc to navigate the Java API. Scala has its own variant, called Scaladoc (see Figure 1–1).

Navigating Scaladoc is a bit more challenging than Javadoc. Scala classes tend to have many more convenience methods than Java classes. Some methods use features that you haven’t learned yet. Finally, some features are exposed as they are implemented, not as they are used. (The Scala team is working on improving the Scaladoc presentation, so that it can be more approachable to beginners in the future.)

1.7

 

Scaladoc

11

 

Figure 1–1 The entry page for Scaladoc

Here are some tips for navigating Scaladoc, for a newcomer to the language.

You can browse Scaladoc online at www.scala-lang.org/api, but it is a good idea to download a copy from www.scala-lang.org/downloads#api and install it locally.

Unlike Javadoc, which presents an alphabetical listing of classes, Scaladoc’s class list is sorted by packages. If you know the class name but not the package name, use the filter in the top left corner (see Figure 1–2).

Figure 1–2 The filter box in Scaladoc

12

Chapter 1 The Basics

 

Click on the X symbol to clear the filter.

Note the O and C symbols next to each class name. They let you navigate to the class (C) or the companion object (O).

Scaladoc can be a bit overwhelming. Keep these tips in mind.

Remember to look into RichInt, RichDouble, and so on, if you want to know how to work with numeric types. Similarly, to work with strings, look into StringOps.

The mathematical functions are in the package scala.math, not in any class.

Sometimes, you’ll see functions with funny names. For example, BigInt has a method unary_-. As you will see in Chapter 11, this is how you define the prefix negation operator -x.

A method tagged as implicit is an automatic conversion. For example, the BigInt object has conversions from int and long to BigInt that are automatically called when needed. See Chapter 21 for more information about implicit conversions.

Methods can have functions as parameters. For example, the count method in StringOps requires a function that returns true or false for a Char, specifying which characters should be counted:

def count(p: (Char) => Boolean) : Int

You supply a function, often in a very compact notation, when you call the method. As an example, the call s.count(_.isUpper) counts the number of uppercase characters. We will discuss this style of programming in much more detail in Chapter 12.

You’ll occasionally run into classes such as Range or Seq[Char]. They mean what your intuition tells you—a range of numbers, a sequence of characters. You will learn all about these classes as you delve more deeply into Scala.

Don’t get discouraged that there are so many methods. It’s the Scala way to provide lots of methods for every conceivable use case. When you need to solve a particular problem, just look for a method that is useful. More often than not, there is one that addresses your task, which means you don’t have to write so much code yourself.

Finally, don’t worry if you run into the occasional indecipherable incantation, such as this one in the StringOps class:

def patch [B >: Char, That](from: Int, patch: GenSeq[B], replaced: Int) (implicit bf: CanBuildFrom[String, B, That]): That

Just ignore it. There is another version of patch that looks more reasonable:

def patch(from: Int, that: GenSeq[Char], replaced: Int): StringOps[A]


Exercises 13

If you think of GenSeq[Char] and StringOps[A] as String, the method is pretty easy to understand from the documentation. And it’s easy to try it out in the REPL:

"Harry".patch(1, "ung", 2) // Yields "Hungry"

Exercises

1.In the Scala REPL, type 3. followed by the Tab key. What methods can be applied?

2.In the Scala REPL, compute the square root of 3, and then square that value. By how much does the result differ from 3? (Hint: The res variables are your friend.)

3.Are the res variables val or var?

4.Scala lets you multiply a string with a number—try out "crazy" * 3 in the REPL. What does this operation do? Where can you find it in Scaladoc?

5.What does 10 max 2 mean? In which class is the max method defined?

6.Using BigInt, compute 21024.

7.What do you need to import so that you can get a random prime as probablePrime(100, Random), without any qualifiers before probablePrime and Random?

8.One way to create random file or directory names is to produce a random BigInt and convert it to base 36, yielding a string such as "qsnvbevtomcj38o06kul". Poke around Scaladoc to find a way of doing this in Scala.

9.How do you get the first character of a string in Scala? The last character?

10.What do the take, drop, takeRight, and dropRight string functions do? What advantage or disadvantage do they have over using substring?

Control Structures and Functions

Topics in This Chapter A1

2.1Conditional Expressions — page 16

2.2Statement Termination — page 17

2.3Block Expressions and Assignments — page 18

2.4Input and Output — page 19

2.5Loops — page 20

2.6Advanced for Loops and for Comprehensions — page 21

2.7Functions — page 22

2.8Default and Named Arguments L1 — page 23

2.9Variable Arguments L1 — page 24

2.10Procedures — page 25

2.11Lazy Values L1 — page 25

2.12Exceptions — page 26

Exercises — page 28


Chapter 2

In this chapter, you will learn how to implement conditions, loops, and functions in Scala. You will encounter a fundamental difference between Scala and other programming languages. In Java or C++, we differentiate between expressions (such as 3 + 4) and statements (for example, an if statement). An expression has a value; a statement carries out an action. In Scala, almost all constructs have values. This feature can make programs more concise and easier to read.

Here are the highlights of this chapter:

An if expression has a value.

A block has a value—the value of its last expression.

The Scala for loop is like an “enhanced” Java for loop.

Semicolons are (mostly) optional.

The void type is Unit.

Avoid using return in a function.

Beware of missing = in a function definition.

Exceptions work just like in Java or C++, but you use a “pattern matching” syntax for catch.

Scala has no checked exceptions.

15

16

Chapter 2

Control Structures and Functions

 

2.1 Conditional Expressions

Scala has an if/else construct with the same syntax as in Java or C++. However, in Scala, an if/else has a value, namely the value of the expression that follows the if or else. For example,

if (x > 0) 1 else -1

has a value of 1 or -1, depending on the value of x. You can put that value in a variable:

val s = if (x > 0) 1 else -1

This has the same effect as

if (x > 0) s = 1 else s = -1

However, the first form is better because it can be used to initialize a val. In the second form, s needs to be a var.

(As already mentioned, semicolons are mostly optional in Scala—see Section 2.2, “Statement Termination,” on page 17.)

Java and C++ have a ?: operator for this purpose. The expression

x > 0 ? 1 : -1 // Java or C++

is equivalent to the Scala expression if (x > 0) 1 else -1. However, you can’t put statements inside a ?: expression. The Scala if/else combines the if/else and ?: constructs that are separate in Java and C++.

In Scala, every expression has a type. For example, the expression if (x > 0) 1 else -1 has the type Int because both branches have the type Int. The type of a mixed-type expression, such as

if (x > 0) "positive" else -1

is the common supertype of both branches. In this example, one branch is a java.lang.String, and the other an Int. Their common supertype is called Any. (See Section 8.11, “The Scala Inheritance Hierarchy,” on page 96 for details.)

If the else part is omitted, for example in

if (x > 0) 1

then it is possible that the if statement yields no value. However, in Scala, every expression is supposed to have some value. This is finessed by introducing a class Unit that has one value, written as (). The if statement without an else is equivalent to

if (x > 0) 1 else ()


2.2

 

Statement Termination

17

 

Think of () as a placeholder for “no useful value,” and think of Unit as the analog of void in Java or C++.

(Technically speaking, void has no value whereas Unit has one value that signifies “no value”. If you are so inclined, you can ponder the difference between an empty wallet and a wallet with a bill labeled “no dollars”.)

NOTE: Scala has no switch statement, but it has a much more powerful pattern matching mechanism that we will discuss in Chapter 14. For now, just use a sequence of if statements.

CAUTION: The REPL is more nearsighted than the compiler—it only sees one line of code at a time. For example, when you type

if (x > 0) 1

else if (x == 0) 0 else -1

the REPL executes if (x > 0) 1 and shows the answer.Then it gets confused about else -1.

If you want to break the line before the else, use braces:

if (x > 0) { 1

} else if (x == 0) 0 else -1

This is only a concern in the REPL. In a compiled program, the parser will find the else on the next line.

TIP: If you want to paste a block of code into the REPL without worrying about its nearsightedness, use paste mode. Type

:paste

Then paste in the code block and type Ctrl+K. The REPL will then analyze the block in its entirety.

2.2 Statement Termination

In Java and C++, every statement ends with a semicolon. In Scala—like in JavaScript and other scripting languages—a semicolon is never required if it falls just before the end of the line. A semicolon is also optional before an }, an else, and similar locations where it is clear from context that the end of a statement has been reached.


18

Chapter 2

Control Structures and Functions

 

However, if you want to have more than one statement on a single line, you need to separate them with semicolons. For example,

if (n > 0) { r = r * n; n -= 1 }

A semicolon is needed to separate r = r * x and n -= 1. Because of the }, no semicolon is needed after the second statement.

If you want to continue a long statement over two lines, you need to make sure that the first line ends in a symbol that cannot be the end of a statement. An operator is often a good choice:

s= s0 + (v - v0) * t + // The + tells the parser that this is not the end

0.5 * (a - a0) * t * t

In practice, long expressions usually involve function or method calls, and then you don’t need to worry much—after an opening (, the compiler won’t infer the end of a statement until it has seen the matching ).

In the same spirit, Scala programmers favor the Kernighan & Ritchie brace style:

if (n > 0) { r = r * n n -= 1

}

The line ending with a { sends a clear signal that there is more to come.

Many programmers coming from Java or C++ are initially uncomfortable about omitting semicolons. If you prefer to have them, just put them in—they do no harm.

2.3 Block Expressions and Assignments

In Java or C++, a block statement is a sequence of statements enclosed in { }. You use a block statement whenever you need to put multiple actions in the body of a branch or loop statement.

In Scala, a { } block contains a sequence of expressions, and the result is also an expression. The value of the block is the value of the last expression.

This feature can be useful if the initialization of a val takes more than one step. For example,

val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx * dx + dy * dy) }

2.4

 

Input and Output

19

 

The value of the { } block is the last expression, shown here in bold. The variables dx and dy, which were only needed as intermediate values in the computation, are neatly hidden from the rest of the program.

In Scala, assignments have no value—or, strictly speaking, they have a value of type Unit. Recall that the Unit type is the equivalent of the void type in Java and C++, with a single value written as ().

A block that ends with an assignment statement, such as

{ r = r * n; n -= 1 }

has a Unit value. This is not a problem, just something to be aware of when defining functions—see Section 2.7, “Functions,” on page 22.

Since assignments have Unit value, don’t chain them together.

x = y = 1 // No

The value of y = 1 is (), and it’s highly unlikely that you wanted to assign a Unit to x. (In contrast, in Java and C++, the value of an assignment is the value that is being assigned. In those languages, chained assignments are useful.)

2.4 Input and Output

To print a value, use the print or println function. The latter adds a new line after the printout. For example,

print("Answer: ") println(42)

yields the same output as

println("Answer: " + 42)

There is also a printf function with a C-style format string:

printf("Hello, %s! You are %d years old.\n", "Fred", 42)

You can read a line of input from the console with the readLine function. To read a numeric, Boolean, or character value, use readInt, readDouble, readByte, readShort, readLong, readFloat, readBoolean, or readChar. The readLine method, but not the other ones, take a prompt string:

val name = readLine("Your name: ") print("Your age: ")

val age = readInt()

printf("Hello, %s! Next year, you will be %d.\n", name, age + 1)