Chapter 2 built-in control structure
Scala does not have many built-in control structures. Only if, while, for, try, match, and function call are supported. So few reasons are that Scala included function text from the very beginning. Instead, scala adds a high-level control structure one by one over the basic syntax. Chapter 1 will show in detail how to do this. This chapter shows the only several built-in control structures.
One thing you will notice is that almost all Scala control structures generate a value. This is the method used by functional languages,ProgramIt is regarded as a value calculation activity, so the control of the program should also do so. You can also think of this method as a trend that already exists in the script language (function call return values, called function updates are also considered as output variables passed in as parameters) logic deduction. In addition, script languages often have ternary operators (such as C, C ++, and Java? : Operator), which is like if but produces values. Scala uses this Trielement operator model, but calls it if. In other words, Scala's if can generate values. Scala continues this trend to generate values for, try, and match.
Programmers can use these results to simplify theirCodeAs if using the return value of the function. Without this mechanism, programmers must create temporary variables to save the computing results in the control structure. Removing these temporary variables makes the code more concise and avoids many bugs that you have set variables in one branch but forgot to set in another branch.
All in all, although Scala has few basic control structures, it is enough to provide important things in the directive language. Further, because they all have result values, they can help you shorten the code. To show you how all of this works, this chapter provides a close view of Scala's basic control structure.
1.1 If expression
Scala's if function works as in many other languages. It tests a status and runs one of the two branches based on whether the status is true. The following is a common example written in the directive style:
VaR filename = "default.txt"
If (! Args. isempty)
Filename = ARGs (0)
This Code declares a variable named filename, Which is initialized as the default value. Then, use the if expression to check whether any parameters are provided to the program. If yes, change the variable to the value defined in the parameter list. If no parameter exists, set the variable to the default value.
This code can be written better, because, as mentioned in step 3 in Chapter 2nd, Scala's if expression can return values. Code 7.1 demonstrates how to achieve the same effect of the previous example without using any var:
Val filename =
If (! Args. isempty) ARGs (0)
Else "default.txt"
Code 7.1 is a convention for condition-based initialization in Scala
This time, if has two branches. If ARGs is not empty, the initialization element, argS (0), is selected. Otherwise, the default value is selected. This if expression generates the selected value, and the filename variable is initialized to this value. This code is a little shorter, but its practical advantage is that it uses VAL instead of var. Using Val is a functional style and can help you in the same way as Java's final variable. It makes the code reader convinced that the variable will never change, saving them the effort to scan all the code of the variable field to check whether it has changed.
The second advantage of using VAL instead of VaR is that it can better supportEquivalent inference:Equational Reasoning. Variables introduced when the expression has no side effectsEquivalentIs used to calculate its expression. Therefore, you can replace the variable name with an expression at any time. For example, to replace println (filename), you can write as follows:
Println (if (! Args. isempty) ARGs (0) else "default.txt ")
You have the right to choose. You can write anything. Using Val can help you safely execute such refactoring to continuously innovate your code.
Try to find opportunities to use Val. They make your code easy to read and refactor.
1.2 WHILE LOOP
Scala's while loop is the same as in other languages. Contains a state and a loop body. As long as the State is true, the loop body is executed over and over again. Code 7.2 shows an example:
Def gcdloop (X: Long, Y: Long): Long = {
VaR A = x
VaR B = y
While (! = 0 ){
Val temp =
A = B %
B = temp
}
B
}
Code 7.2 use a while loop to calculate the maximum Common Divisor
Scala also has a do-while loop. In addition to moving the status test from the front to the back, there is no difference with the while loop. Code 7.3 shows the scala script that uses do-while feedback to read Row Records from standard input until empty rows are read:
VaR line = ""
Do {
Line = Readline ()
Println ("read:" + line)
} While (line! = NULL)
Code 7.3 read information from standard input using do-while
The while and do-while structures are called "loops", not expressions, because they do not produce meaningful results. The result type is unit. It indicates that the generated value (and actually a unique value) is of the unit type. CalledUnit Value, Write (). () Is the difference between Scala unit and Java void. Try the following code in the interpreter:
Scala> def greet () {println ("hi ")}
Greet: () Unit
Scala> greet () = ()
Hi
Res0: Boolean = true
Since there is no equal sign before the method body, greet is defined as a process in which the result type is unit. Therefore, greet returns the unit value ,(). This is confirmed by the next line: Compare the GREET result with the unit value, (), equal, produces true.
Another architecture that generates unit values is the re-assignment of VaR values. For example, if you try to use the following while loop examples in Java (or C ++) to read a row of records in Scala, you will be in trouble:
VaR line = ""
While (line = Readline ())! = "") // Does not work
Println ("read:" + line)
Scala will warn you when compiling this code! = Values of the comparison type "unit" and "string" are always true. In Java, the value assignment statement can return the assigned value. In the same case, a record returned by the standard input will always generate the unit value in the scala value assignment statement ,(). Therefore, the value of the value assignment statement "line = Readline ()" will always be () rather "". As a result, the while loop State will never be false, so the loop will never end.
Because while loops do not generate values, they are often discarded by Pure Functional Languages. This language has only expressions and has no loops. Even so, scala still contains the while loop, because in some cases the directive solution is more readable, especially for those programmers with a directive background. For example, if you want to repeat a process until some status changesAlgorithmCode, while loop can be expressed directly, and functional substitution is probably implemented using recursion. Maybe it is not so obvious for some readers of code.
For example, code 7.4 shows an alternative method for calculating the maximum common divisor of two numbers. [1] Given the same values X and Y, The GCD function displayed in code 7.4 returns the same result as the gcdloop function in code 7.2. The difference between the two methods is that gcdloop is written into the script style and uses the VaR and while loops, while GCD is more functional and uses recursion (GCD calls itself) without var:
Def gcd (X: Long, Y: Long): Long =
If (y = 0) x else gcd (Y, X % Y)
Code 7.4 use recursion to calculate the maximum Common Divisor
In general, we recommend that you question the while loop in your code as you question var. In fact, while loops and VaR are often paired. Because while loops do not generate values. In order to make any changes to your program, while loops generally do not update var or execute I/O. You can see in the previous gcdloop example. When the while loop is working, two VaR values A and B are updated. Therefore, we recommend that you have a more skeptical attitude towards the while loop in the code. If you do not have a good decision on a specific while or do loop, try to find a way to do the same thing without them.
1.3 for expression
Scala's for expression is the Swiss army knife for enumeration ". It allows you to combine several simple components in different ways to express various enumerations. A simple method is used to complete tasks such as enumerating an integer sequence. More advanced expressions can list multiple sets of different types, filter elements using any conditions, and create new sets.
Enumeration set class
The simplest thing you can do with for is to enumerate all the elements of a collection class. For example, code 7.5 shows an example of printing all file names in the current directory. I/O operations use Java APIs. First, we create a file pointing to the current directory, ".", and then call its listfiles method. Returns an array of file objects, each representing the directory or file contained in the current directory. We save the result array in the fileshere variable.
Val fileshere = (New java. Io. File ("."). listfiles
For (File <-fileshere)
Println (file)
Code 7.5 use the files in the for loop List Directory
By using the syntax "file <-fileshere" called generator: generator, we traverse the elements of fileshere. During each enumeration, the new Val named file is initialized by the element value. The compiler deduced that the file type is file, because fileshere is array [file]. For each enumeration, the for expression's function body, println (file), will be executed once. Because the tostring method of file generates the name of the file or directory, the names of all files and directories in the current directory will be printed.
The for expression syntax is effective for all types of collection classes, not just arrays. [2] The range type shown in Table 5-4 on page 80th is a convenient special case. You can create a range using a syntax similar to "1 to 5, use for enumeration. The following is a simple example:
Scala> for (I <-1 to 4)
Println ("iteration" + I)
Iteration 1
Iteration 2
Iteration 3
Iteration 4
If you do not want to include the upper boundary of the enumerated range, use until instead of:
Scala> for (I <-1 until 4)
Println ("iteration" + I)
Iteration 1
Iteration 2
Iteration 3
Enumerating integers like this is common in Scala, but not in other languages. In other languages, you may need to traverse the array as follows:
// Not common in Scala ......
For (I <-0 to fileshere. Length-1)
Println (fileshere (I ))
This for expression introduces the variable I, sets it to an integer from 0 to fileshere. Length-1 in sequence, and then executes the for expression loop body for each setting of I. For each I value, the I-th element of fileshere is taken out and processed.
The uncommon reason for this type of enumeration in Scala is that the direct enumeration set class is also doing the same well. In this way, your code becomes shorter and avoids frequent occurrences of many enumerated arrays.Superbit overflow:Off-by-one error. Should it start from 0 or 1? Should I add-1, + 1, or do I have to wait until the last index? These questions are easy to answer, but they are also easy to answer incorrectly. It is better to avoid this problem.
Filter
Sometimes you do not want to enumerate all elements of a collection class. Instead, we want to filter out a subset. You canFilter:Filter: An if clause is added to the brackets of. For example, code 7.6 only lists the names of files ending with ". Scala" in the current directory:
Val fileshere = (New java. Io. File ("."). listfiles
For (File <-fileshere if file. getname. endswith (". Scala "))
Println (file)
Code 7.6 uses a for file with a filter to discover the. Scala File
Alternatively, you can write as follows:
For (File <-fileshere)
If (file. getname. endswith (". Scala "))
Println (file)
This code can generate the same output as the previous code, and it looks more familiar to programmers with the script background. However, the script format is only an option, because the use of this for expression is executed to print this side effect and generate the unit value (). As will be shown later in this section, a for expression is called an "expression" because it produces values of interest. The type of A for expression depends on the set of <-clause.
You can include more filters if you like. You only need to add it to the clause. For example, to enhance defense, the code in code 7.7 only prints files instead of directories. By adding a filter to check the isfile method of file:
For (
File <-fileshere
If file. isfile;
If file. getname. endswith (". Scala ")
) Println (file)
Code 7.7 uses multiple filters in a for expression
Note:
If more than one filter is added to the generator, the if clause must be separated by semicolons. This is because the "If file. isfile" filter in code 7.7 is followed by a semicolon.
Nested Enumeration
If you add multiple <-clauses, you get the nested "loop ". For example, the for expression displayed in code 7.8 has two nested loops. The loop enumeration of the outer layer is fileshere, which is used to enumerate all filelines (Files) ending with. Scala ).
Def filelines (File: Java. Io. File) =
Scala. Io. Source. fromfile (file). getlines. tolist
Def grep (pattern: string) =
For {
File <-fileshere
If file. getname. endswith (". Scala ")
Line <-filelines (file)
If line. Trim. Matches (pattern)
} Println (File + ":" + LINE. Trim)
Grep (". * GCD .*")
Code 7.8 uses multiple generators in the for expression
If you like, you can use braces instead of parentheses to enclose the generator and filter. One advantage of braces is that you can omit semicolons that must be added to parentheses.
Mid-stream variable binding
Pay attention to the line. Trim expression repeated in the previous code segment. This is not a negligible calculation, so you may want to calculate it only once. You can bind the result to a new variable by using the equal sign (=. The bound variable is introduced and used as Val, but the keyword Val is not required. Code 7.9 shows an example.
Def grep (pattern: string) =
For {
File <-fileshere
If file. getname. endswith (". Scala ")
Line <-filelines (file)
Trimmed = line. Trim
If trimmed. Matches (pattern)
} Println (File + ":" + trimmed)
Grep (". * GCD .*")
Code 7.9 assigns a value between streams in the for expression
In the code, the variable named trimmed is introduced into the for expression from the half and initialized to the result value of line. Trim. The subsequent for expression can use this new variable in two places, one in IF and one in println.
New set of Manufacturing
Until now, all examples have only been used to operate on the enumerated values and then let them go. In addition, you can create a value to remember each iteration. You only need to add the keyword yield before the for expression. For example, the following function identifies the. Scala file and stores it in an array:
Def scalafiles =
For {
File <-fileshere
If file. getname. endswith (". Scala ")
} Yield File
The for expression creates a value each time it is executed. In this example, file is used. When the for expression is complete, the result is a set containing all generated values. The type of the result set is based on the collection type processed by the enumeration clause. In this example, the result is array [file], because fileshere is an array and the generated expression type is file.
Note the place where the yield keyword is placed. The syntax for the for-yield expression is as follows:
For{Clause}Yield{Loop body}
Yield is before the entire loop body. Even if the loop body is a code block surrounded by braces, yield must be placed before the left parenthesis, rather than before the last expression of the code block. Resist the temptation to write as follows:
For (File <-fileshere if file. getname. endswith (". Scala ")){
Yield file // syntax error!
}
For example, the for expression displayed in code 7.10 first converts an array [file] named fileshere that contains all files in the current directory into an array containing only. Scala files. For each object, an iterator [String] (the result of the filelines method is defined and displayed in code 7.8) is generated. The next and hasnext methods are provided for you to enumerate each element of the set. The original enumerator is converted to another iterator [String] that contains only the trimmed rows containing the sub-string ". Finally, an integer length is generated for each row. The result of this for expression is an array [int] containing these lengths.
Val forlinelengths =
For {
File <-fileshere
If file. getname. endswith (". Scala ")
Line <-filelines (file)
Trimmed = line. Trim
If trimmed. Matches (". * .*")
} Yield trimmed. Length
Code 7.10 use for to convert array [file] to array [int]
Currently, you have read all the main features of Scala's for expression. However, this paragraph is faster. A more thorough introduction to for expressions is provided in Chapter 23rd.
1.4 Use try expression to handle exceptions
Scala exceptions are the same as those in many other languages. Instead, return a value in the normal way. You can throw an exception to abort it. The caller of the method can either capture and handle this exception, or simply abort it and upgrade the exception to the caller. An exception can be upgraded in this way, releasing the call stack layer by layer until a method processes it or there are no other methods left.
Throw an exception
Exception throws look exactly the same as Java throws. First create an exception object and then throw it with the throw Keyword:
Throw new illegalargumentexception
Although it may seem unexpected, in Scala, throw is also a result type expression. The following is an example of the result type:
Val half =
If (N % 2 = 0)
N/2
Else
Throw new runtimeexception ("n must be even ")
What happens here is that if n is an even number, half will be initialized to half of N. If n is not an even number, an exception will be thrown before half can be initialized to any value. Therefore, it is safe to treat the thrown exception as a value of any type. Any attempt to use the return value from throw does not work, so it is harmless.
Technically speaking, the type of thrown exception is nothing. Although throw does not actually produce any value, you can regard it as an expression. This kind of trick may look weird, but it is often useful in examples like above. If one branch calculates the value, and the other throws an exception and generates a nothing. The type of the IF expression is the branch type of the actually calculated value. The Nothing type will be discussed in later section 11.3.
Capture exceptions
The syntax used to capture exceptions is displayed in code 7.11. The reason for selecting a catch clause is to be very important to scala:Pattern Matching:Pattern MatchingAlways consistent. Pattern matching is a powerful feature that will be outlined in this chapter and detailed in chapter 15th.
Import java. Io. filereader
Import java. Io. filenotfoundexception
Import java. Io. ioexception
Try {
Val F = new filereader ("input.txt ")
// Use and close file
} Catch {
Case ex: filenotfoundexception => // handle missing file
Case ex: ioexception => // handle other I/O Error
}
Code 7.11 Scala try-catch clause
The behavior of this try-catch expression is the same as that of exception handling in other languages. The program is executed. if an exception is thrown, each catch clause is tried in sequence. In this example, if the exception is filenotfoundexception, the first clause is executed. For the ioexception type, the second clause is executed. If not, try-catch will end and raise the exception.
Note:
You will soon find that the difference with Java is that Scala does not require your captureCheck exceptions:Checked exception, Or declare them in the throws clause. If you want to, you can declare a throws clause with the atthrows annotation, but this is not necessary.
Finally clause
If you want some code to be executed no matter how the method is aborted, you can put the expression in the finally clause. For example, you may want to make sure that the opened file is closed even if the method throws an exception and exits. Code 7.12 demonstrates this example.
Import java. Io. filereader
Val file = openfile ()
Try {
// Use the File
} Finally {
File. Close () // make sure to close the file
}
Code 7.12 Scala try-finally Clause
Note:
Code 7.12 demonstrates a conventional approach to ensure that non-memory resources, such as files, sockets, or database links are disabled. First, you get the resource. Then you start a try code block to use resources. Finally, you close the resource in the finally code block. This Scala convention is the same as that in Java. However, you also use the loan pattern technique, also known as the lending mode, to achieve the same purpose more concisely. The lending mode is described in Section 9.4.
Generate value
Like most Scala control structures, try-catch-finally also produces values. For example, code 7.13 shows how to split a URL, but if the URL format is incorrect, the default value is used. The result is that if no exception is thrown, it corresponds to the try clause. if an exception is thrown and caught, it corresponds to the catch clause. If an exception is thrown but not captured, the expression does not return a value. The value calculated by the finally clause is discarded if any. In general, the finally clause does some cleanup work, such as closing the file; they should not change the value calculated in the main function body or try catch clause.
Import java.net. url
Import java.net. malformedurlexception
Def urlfor (Path: string) =
Try {
New URL (PATH)
} Catch {
Case E: malformedurlexception =>
New URL ("http://www.scalalang.org ")
}
Catch clause that code 7.13 can generate values
If you are familiar with Java, you do not know that the difference between Scala behavior and Java is only caused by Java's try-finally without generating values. In Java, if the finally clause contains an explicit return statement or throws an exception, this return value or exception will be "above" any value or exception that was previously generated by a try code block or a catch clause of it. For example:
Def F (): Int = Try {return 1} finally {return 2}
Call F () to generate result value 2. Conversely:
Def g (): Int = Try {1} finally {2}
Call g () to generate 1. These two examples demonstrate the behavior that may surprise other programmers, so it is usually better to avoid returning values from the finally clause. It is best to use the finally clause as a way to ensure certain side effects, such as closing open files.
1.5 match expression
Scala's matching expression allows youOptional:AlternativeIn other languages. Generally, the match expression allows you to use anyMode:PatternThe selection will be introduced in chapter 15th. The general mode can be discussed later. Currently, you only need to consider using match to select among several options.
For example, the script in code 7.14 reads the food name from the parameter list and prints the food ingredients. The match expression checks the first parameter firstarg In the parameter list. For the string "salt", print "Pepper". For the string "Chips", print "Salsa. By default, underlines (_) are used to indicate wildcards that are commonly used as placeholders in Scala to indicate completely unclear values.
Val firstarg = If (ARGs. length> 0) ARGs (0) else ""
Firstarg match {
Case "salt" => println ("Pepper ")
Case "Chips" => println ("Salsa ")
Case "eggs" => println ("bacon ")
Case _ => println ("huh? ")
}
A match expression with side effects in code 7.14
Compared with the switch statement in Java, the matching expression has some important differences. One of them is a constant of any type, or anything else, which can be used as a case in Scala, not just an integer type and enumeration constant in a Java case statement. In this example, the optional values are strings. Another difference is that there is no break at the end of each option. Instead, break is implicit and will not be transferred from one option to another. This usually shortens the code and avoids the root cause of some errors, because programmers no longer turn around in options by mistake.
However, the most significant difference from the Java switch is that the match expression can also generate values. In the previous example, each option of the match expression prints a value. You can only generate values instead of printing them, as shown in code 7.15. The value generated by the match expression is stored in the friend variable. In addition to shortening the Code (at least a few commands are reduced), this also removes two irrelevant concerns: first, select the food name, and then print it.
Val firstarg = If (! Args. isempty) ARGs (0) else ""
Val friend =
Firstarg match {
Case "salt" => "Pepper"
Case "Chips" => "Salsa"
Case "eggs" => "bacon"
Case _ => "huh? "
}
Println (friend)
The match expression of the value generated by code 7.15
1.6 exit break and continue
You may have noticed that break and continue are not mentioned here. Scala removes these commands because they have a bad engagement with functional text and the next chapter talks about this feature. The meaning of continue in the while loop is clear, but what does it represent in functional text? Although Scala supports both the directive and functional styles, it is slightly more inclined to functional programming to replace the language's conciseness. However, do not worry. There are many programming methods that do not use break and continue. If you can use functional text effectively, you can write less than the original code.
The simplest way is to replace every with IF and every break with a Boolean variable. A boolean variable indicates whether the while loop containing it should continue. For example, if you are searching for a parameter list to search for strings ending with ". Scala" but not starting with a hyphen. In Java, you can -- if you like the while loop, break and continue -- write as follows:
Int I = 0; // in Java ......
Boolean foundit = false;
While (I <args. Length ){
If (ARGs [I]. startswith ("-"))
{
I = I + 1;
Continue;
}
If (ARGs [I]. endswith (". Scala ")){
Foundit = true;
Break;
}
I = I + 1;
}
If you want to literally translate the code into Scala and execute an if statement and then continue, you can write an if statement to enclose all the remaining content while. To remove break, you can add a Boolean variable to indicate whether to continue, but here you can reuse foundit. With these two tips, the code can be completed like code 7.16:
VaR I = 0
VaR foundit = false
While (I <args. Length &&! Foundit ){
If (! ARGs (I). startswith (""))
{
If (ARGs (I). endswith (". Scala "))
Foundit = true
}
I = I + 1
}
Code 7.16 does not contain a loop of break or continue
This version is very similar to the original Java code. All major paragraphs still exist and remain in the original order. There are two variables that can be re-assigned and a while loop. There is a test in the loop that checks whether I is smaller than args. length, then checks "-", and then checks ". Scala ".
If you want to remove the VaR in code 7.16, you can try to use a recursive function to override the loop. For example, you can define a searchfrom function with an integer as the input, search forward, and return the desired parameter index. The code using this technique looks like this in code 7.17:
Def searchfrom (I: INT): Int =
If (I> = args. Length)-1 // do not overwrite the last parameter
Else if (ARGs (I). startswith ("-") searchfrom (I + 1) // skip Option
Else if (ARGs (I). endswith (". Scala") I // find!
Else searchfrom (I + 1) // continue searching
Val I = searchfrom (0)
Code 7.17 uses recursive substitution methods that do not use VaR for Loops
Code 7.17 provides a name that can be understood to describe what the function is doing. It replaces the loop with recursion. Each continue is replaced by a recursive call with an I + 1 parameter, effectively redirecting to the next integer. Many people have found that this programming style is easier to understand when they start to use recursion.
Note:
The Scala compiler does not actually generate recursive functions for the code displayed in code 7.17. Because all recursive calls areEnd call:Tail-callThe compiler generates code similar to the while loop. Each recursive call is implemented as a jump back to the start position of the function. The final call optimization will be discussed in section 8.9.
1.7 variable range
Now that you have read Scala's built-in control structure, we will use them in this section to explain how Scala's scope works.
JavaProgrammer's quick Channel
If you are a Java programmer, you will find that Scala's range rules are almost a replica of Java. However, there is still a difference between the two. Scala allows you to define variables of the same name within the nested range. Therefore, if you are a Java programmer, you may have a quick view at least.
The variable definition in Scala has a usableRange:Scope. The most common example of range setting is that braces usually introduce a new range, so anything defined in parentheses is out of the range after parentheses. [3] for demonstration, please refer to the functions shown in code 7.18:
Def printmultitable (){
VaR I = 1
// Here only I is in the range
While (I <= 10 ){
VaR j = 1
// Here I and j are in the range
While (j <= 10 ){
Val prod = (I * j). tostring
// Here I, j, and prod are in the range
VaR K = prod. Length
// Here I, j, prod and K are in the range
While (k <4 ){
Print ("")
K + = 1
}
Print (prod)
J + = 1
}
// I and j are still in the range; prod and K are out of the range
Println ()
I + = 1
}
// I is still in the range; j, prod and K are out of the range
}
Code 7.18: variable range used to print a multiplication table
The printmultitable function prints the multiplication table. [4] the first statement of the function introduces variable I and initializes it to integer 1. Then you can use name I in the rest of the function.
The subsequent statement of printmultitable is a while loop:
While (I <= 10 ){
VaR j = 1
...
}
You can use I here because it is still in the range. In the first statement of the while loop, you introduce another variable called J and initialize it as 1 again. Because the variable J is defined in the braces of the while loop, it can only be used in the while loop. If you want to try to follow the braces in the while loop, after the comments that say J, prod, and K already have a range, and then use J to do something, your program cannot be compiled.
All variables -- I, j, prod, and K -- defined in this example areLocal variable:Local variable. The defined functions are "local. Each time a function is called, a new set of local variables will be used.
Once a variable is defined, you cannot define the same name in the same range. For example, the following script will not be compiled:
Val a = 1
Val a = 2 // compilation failed
Println ()
However, you can define variables with the same name as those in an external range within an internal range. The following scripts are compiled and can be run:
Val a = 1;
{
Val a = 2 // compiled
Println ()
}
Println ()
During execution, the script prints 2 and 1 first, because a defined in the internal parentheses is different variables and will only be valid within the braces. [5] One difference between Scala and Java is that, unlike Scala, Java does not allow you to create variables with the same name as external variables within the internal scope. In Scala, internal variables are said to beMasking:ShadowBecause internal and external variables become invisible within the internal range.
You may have noticed something that looks like a shadow in the interpreter:
Scala> Val a = 1
A: Int = 1
Scala> Val a = 2
A: Int = 2
Scala> println ()
2
In the interpreter, you can reuse variable names for your core content. Aside from anything else, this will allow you to change your mind when you find that you made a mistake defining variables for the first time in the interpreter. The reason you can do this is that, in theory, the interpreter creates a new nested range every time you input a new statement. Therefore, you can think of the Code virtualization:
Val a = 1;
{
VaR A = 2;
{
Println ()
}
}
This code can be compiled and executed like a Scala script, and output 2 is printed like the Code entered into the interpreter. Remember that such code is confusing for readers, because the variable name has a new meaning in the nested range. Generally, a better way is to select a new meaningful variable name instead of masking external variables.
1.8 refactor the script-style code
To help you learn more about the functional style, We Will refactor code 7.18 to print the multiplication table in the directive style. Our functional alternatives are displayed in code 7.19.
The code in code 7.18 shows the directive style in two aspects. First, calling printmultitable has a side effect: print the multiplication table on the standard output. In code 7.19, We reconstructed the function and asked it to return the multiplication table as a string. Since the function does not execute printing, we rename it as multitable. As mentioned above, one advantage of functions without side effects is that they are easy to perform unit tests. To test printmultitable, You need to redefine print and println to check whether the output is correct. Testing multitable is much simpler, as long as you check the results.
// Returns a multiplication table in sequence.
Def makerowseq (row: INT) =
For (COL <-1 to 10) yield {
Val prod = (row * col). tostring
Val padding = "" * (4-Prod. length)
Padding + Prod
}
// Returns a multiplication table in the form of a string
Def makerow (row: INT) = makerowseq (ROW). mkstring
// Returns the multiplication table in the form of strings. each row of records occupies one row of strings.
Def multitable () = {
Val tableseq = // a sequence of line record strings
For (row <-1 to 10)
Yield makerow (ROW)
Tableseq. mkstring ("\ n ")
}
Code 7.19 create a multiplication table Functional Method
Another signal in printmultitable that exposes its directive style comes from its while loop and var. In contrast, the multitable function uses the Val and for expressions,Help functions:Helper FunctionAnd the mkstring is called.
We extract two helper functions, makerow and makerowseq, to make the code easy to read. The makerowseq function uses the for expression to enumerate the number of columns from 1 to 10. The for function calculates the product of rows and columns, determines the space occupied before the product, and generates the result from the placeholder space and the product string. The result of the for expression is a sequence containing these generated strings as elements (a subclass of scala. seq ). Another help function, makerow, only calls the mkstring function of the result returned by makerowseq. The strings in the superposition sequence return them as a string.
The multitable method first uses the result of a for expression to initialize tableseq. This for expression starts from 1 to 10 enumerated rows and calls makerow on each row to obtain the strings of this row. Because the string prefix is the yield keyword, the result of the expression is the sequence of the line string. Now, the only task is to convert the string sequence into a single string. Mkstring is called to complete this work, and because we pass in "\ n", line breaks are inserted at the end of each string. If you pass the string returned by multitable to println, you will see the same output result as that generated by printmultitable: (omitted)
[1] The GCD function displayed in code 7-4 uses the same name function first displayed in code 6-3, which is the maximum common divisor for rational computing, the main difference is that the GCD parameter in code 7-4 uses long instead of Int.
[2] more accurately, the expression on the right of the <-symbol must support the method named foreach.
[3] There are several exceptions to this rule, because in Scala, sometimes you can replace parentheses with braces. The alternative to expression syntax is one of the examples using braces, which will be described in section 7.3.
[4] The printmultitable function displayed in code 7-18 is written in the directive style. In the next section, we will refactor it in the functional style.
[5] In addition, a plus sign is required after the first definition of A in this example, because the semicolon Inference Mechanism of scala does not add a semicolon here.