I touched Scala ~ [Control syntax] ~

Introduction

image.png

It's a direct migration of the dwango tutorial, where you'll study, edit it, and replace it with your own terms.

Since this text is about learning the basics of Scala, there will often be explanations of some Scala syntax from now on. Here is a brief description of the notation for expressing syntax. In this section you will learn about Scala control syntax. Don't worry, it's not that outlandish compared to regular programming languages.

About notation

Since this text is about learning the basics of Scala, there will often be explanations of some Scala syntax from now on. Here is a brief description of the notation for expressing syntax.

Alphabet and other sequences

First, as shown below, when a sequence of alphabets and symbols appears as it is, it represents the character string itself. Here, it represents the string if itself.

if

A sequence of alphabets, etc. enclosed in quotes

Next, treat the alphabet and symbols enclosed in quotes in the same way. This is used to avoid confusion with some characters as they have a special meaning, as described below. The following has the same meaning as before.

'if'

Elements enclosed in (and)

Some element, enclosed in (), represents a grouping. The following represents a group of characters called if (), not some string that starts with (. This is used to group the repeated expressions described later.

('if' '(' ')')

Grouping takes precedence unless explicitly stated as'(' or')'.

Elements enclosed in

Named with , such as , represents some syntactic element. Writing indicates that it is some syntactic element that expresses the concept of an expression. The following represents a part of the syntax of the Java if statement. I haven't mentioned what the is, but I'm assuming an expression that returns boolean in the Java language.

if '(' <Conditional expression> ')'

Some element followed by *

Any element followed by * is added to mean that the element appears zero or more times. The following means that an element with; after appears 0 or more times.

(<formula> ;)*

Here, it is ambiguous whether a * is the character * after a or whether a is repeated 0 or more times. To resolve the ambiguity, iteration shall take precedence unless explicitly marked with a'*'.

Some element followed by a +

Any element followed by a + means that the element appears more than once. The following means that an element such as; after appears more than once.

(<formula> ;)+

Here, it is ambiguous whether a + is followed by the letter + or a is repeated one or more times. To resolve the ambiguity, iteration shall take precedence unless explicitly stated as'+'.

Some element followed by?

Any element followed by a? Means that the element appears 0 or 1 times. In other words, that element is optional. The following is

It means that an element that starts with else and is followed by appears 0 or 1 times.

(else <formula>)?

Here, it is ambiguous whether a? Is followed by the letter?, Or is the 0 or 1 occurrence of a. To resolve the ambiguity, the option shall prevail unless explicitly stated as'?'.

| Appended between two elements

Any two elements A and B with a | added between them means that either A or B is acceptable. The following means that either val or var can be used.

('val'|'var')

Where a|b is a|It is ambiguous whether it is the three letters b or a or b. Explicitly to resolve ambiguity'|'Unless, the interpretation a or b takes precedence.

Some element followed by ...

It is used to exemplify the first few elements when any number of elements come, and to clearly indicate that the rest will appear in the same pattern. The following represents a pattern that is enclosed in [and] and has an arbitrary number of expressions.

'[' <Equation 1>, <Equation 2>, ... ']'

if expression syntax

Based on the above, the syntax of Scala's if expression can be expressed as follows.

if '(' <Conditional expression> ')' <formula> ( else <formula> )?

We'll talk more about Scala's if expressions later.

Control syntax

In this section you will learn about Scala control syntax. Don't worry, it's not that outlandish compared to regular programming languages.

About the terms "syntax", "expression" and "sentence" In this section, the terms "syntax", "expression", and "sentence" are used in a mixed manner and may be a little difficult to understand, so I would like to explain these three terms first.

First, "Syntax" is a rule for a program to have a structure within the programming language. Often, it contains keywords that are treated specially within the programming language, such as class, val, if, and there are rules for constructing the correct program. In the case of class, class is followed by the class name, the contents of the class are enclosed in {and}, and so on. Since this section explains Scala's control syntax, it explains the rules for creating programs that control the flow of processing.

Next, the "Expression" is the part of the program that becomes a value when the evaluation is successful. For example, 1 or 1 + 2, "hoge". By evaluating these, they become numerical values or string values. I used the expression that the evaluation was successful, but when an exception is thrown as a result of the evaluation, it corresponds to the case where the evaluation fails.

Finally, there is the "Statement", which, in contrast to the expression, is the part of the program that does not become a value even if evaluated. For example, if the variable definition val i = 1 is evaluated, the variable i is defined and the value of i becomes 1, but this definition as a whole has no value. So this is a sentence.

Scala has more syntax than statements in procedural languages such as C and Java. Scala uses a syntax that uses more expressions than statements. This makes it easier to write easy-to-understand code that eliminates the states of variables as much as possible.

Please pay attention to the usage of such words and read the explanation below.

Block type

In Scala, if you enclose a sequence of expressions with {}, it becomes an expression as a whole, but for convenience we will call it a block expression.

The general form of the block type is

{ <Equation 1>(;|<new line>) <Equation 2>(;|<new line>) ... }

It will be. The list of expressions represents the individual expressions that are evaluated in order. You can omit the semicolon if the expression is separated by a newline. The {} expression evaluates expression 1, expression 2 ... and the sequence of expressions in order, and returns the value that evaluated the last expression.

In the following formula


scala> { println("A"); println("B"); 1 + 2; }
A
B
res0: Int = 3

A and B are output, and you can see that 3 which is the result of the last expression 1 + 2 is the value of the {} expression.

This becomes important in method definitions that will be described later. In Scala


def foo(): String = {
  "foo" + "foo"
}

It is common to define a method in the form of (described later), but here {} is just a {} expression, and the method definition syntax does not include {}. However, {} in class definition syntax etc. is part of the statement.

if expression

If expressions are used in much the same way as Java if statements. The syntax of the if expression is as follows:

if '('<Conditional expression>')' <then expression> (else <else expression>)?

The conditional expression must be of type Boolean. else can be omitted. The then expression is the expression that evaluates when the conditional expression is true, and the else expression is the expression that evaluates when the conditional expression is false.

Let's use the if expression immediately.

scala> var age = 17
age: Int = 17


scala> if(age < 18) {
     |   "Under 18 years old"
     | } else {
     |   "18 years old and over"
     | }
res1: String =Under 18 years old

In addition, it is as follows when evaluated by another sentence. This operation is possible because age is a variable string.


scala> age = 18
age: Int = 18

scala> if(age < 18) {
     |   "Under 18 years old"
     | } else {
     |   "18 years old and over"
     | }
res2: String =18 years old and over

I'm trying to return a different string depending on whether the mutable variable age is less than 18.

All Scala control syntax is expressions, not just if expressions. That is, it always returns some value. You may have seen the ternary operator?: In languages such as Java, but Scala uses if expressions when you need a value as well.

In addition, I wrote that else can be omitted, but in that case, the same value as the Unit type value () is supplemented is returned as shown below.

if '(' <Conditional expression> ')' <then expression> else ()

The Unit type is equivalent to void in Java and is used when there is no value to return and has only one value ().

while expression

The syntax of the while expression is similar to that of Java.

while '(' <Conditional expression> ')'Body type

The conditional expression must be of type Boolean. The while expression continues to evaluate the body expression while the conditional expression is true. Since the while expression is also an expression, it returns a value, but since there is no appropriate value to be returned in the while expression, it returns a Unit type value ().

Now, let's output a value from 1 to 10 using a while expression.

scala> var i = 1
i: Int = 1

scala> while(i <= 10) {
     |   println("i = " + i)
     |   i = i + 1
     | }
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Similar to using a while statement in Java. There is also a do while expression, but since it is similar to Java, the explanation is omitted. There is no language function equivalent to Java's break statement and continue statement. However, with proper use of higher-order functions, which we will discuss later, break and continue are not necessary in most cases.

for expression

Scala has a control syntax called for expressions. This is a control syntax that can be used in a similar way to the Java extension for statement, but has various applications other than loops. To understand the true power of for expressions, you need to know the methods flatMap, map, withFilter, foreach, but here we will only explain the basic usage of for expressions.

The basic syntax of the for expression is as follows:


for '(' (<generator>;)+ ')' '<Body>' 
# <generator> = x <- <formula>

You can use a loop variable with any name you like for the variable x in each generator. You can write various expressions in the expression. However, keep in mind that you can use an expression that expresses a range of numbers, as we can't explain everything at the moment. For example, 1 to 10 is in the range 1 to 10 (including 10) and 1 until 10 is in the range 1 to 10 (not including 10).

Now let's use the for expression.


scala> for(x <- 1 to 5; y <- 1 until 5){
     |   println("x = " + x + " y = " + y)
     | }
x = 1 y = 1
x = 1 y = 2
x = 1 y = 3
x = 1 y = 4
x = 2 y = 1
x = 2 y = 2
x = 2 y = 3
x = 2 y = 4
x = 3 y = 1
x = 3 y = 2
x = 3 y = 3
x = 3 y = 4
x = 4 y = 1
x = 4 y = 2
x = 4 y = 3
x = 4 y = 4
x = 5 y = 1
x = 5 y = 2
x = 5 y = 3
x = 5 y = 4

It loops x from 1 to 5, loops y from 1 to 4, and outputs the values of x and y. I've only used two generators here, but you can increase the number to loop multiple times.

This is not the only power of the for expression. You can also narrow down only those that meet the conditions from the loop variables. After until, if x! = Y is written, but this is extracted only when x and y are different values.


scala> for(x <- 1 to 5; y <- 1 until 5 if x != y){
     |   println("x = " + x + " y = " + y)
     | }
x = 1 y = 2
x = 1 y = 3
x = 1 y = 4
x = 2 y = 1
x = 2 y = 3
x = 2 y = 4
x = 3 y = 1
x = 3 y = 2
x = 3 y = 4
x = 4 y = 1
x = 4 y = 2
x = 4 y = 3
x = 5 y = 1
x = 5 y = 2
x = 5 y = 3
x = 5 y = 4

The for expression can also be used to do something by tracing the elements of the collection one by one. Let's write a process to output all by following a list consisting of 5 elements "A", "B", "C", "D", "E".


scala> for(e <- List("A", "B", "C", "D", "E")) println(e)
A
B
C
D
E

In addition, for expressions can be processed to create new collections. Let's add the string Pre to all the elements in the list above.


scala> for(e <- List("A", "B", "C", "D", "E")) yield {
     |   "Pre" + e
     | }

res9: List[String] = List(PreA, PreB, PreC, PreD, PreE)

The point here is the keyword yield. In fact, the for syntax can be used for a completely different purpose, processing and returning the elements of a collection by using the yield keyword. In particular, for expressions that use the yield keyword are sometimes called for-comprehension.

match expression

A match expression is a control structure that can express multiple branches like a Java switch, but it can do more than a switch. The basic syntax of the match expression is


<Symmetric> match {
  (case <pattern> (if <guard>)? '=>'
    (<formula> (;|<new line>))*
  )+
}

However, this is because the contents that can be written in this "pattern" are very diverse. First, let's use it like a Java switch-case. For example


scala> val taro = "Taro"
taro: String = Taro

scala> taro match {
     |   case "Taro" => "Male"
     |   case "Jiro" => "Male"
     |   case "Hanako" => "Female"
     | }
res10: String = Male

You can use it like this. Here, taro contains the string "Taro", which matches case "Taro", so "Male" is returned. As some of you may have noticed here, the match expression also returns a value. The value of the match expression is an evaluation of the expression on the right-hand side of => in the matched pattern.

Patterns can handle not only character strings but also various values such as numerical values.


scala> val one = 1
one: Int = 1

scala> one match {
     |   case 1 => "one"
     |   case 2 => "two"
     |   case _ => "other"
     | }
res11: String = one

Here, _ appears in the place of the pattern, which is equivalent to the default of switch-case, and is a pattern that matches everything. This pattern is called a wildcard pattern. When using match expressions, we often use wildcard patterns to avoid omissions.

Put together a pattern

Those who have learned switch-case statements in languages such as Java and C may find it strange that Scala pattern matching does not do so-called fall through behavior.


"abc" match {
  case "abc" => println("first")   //Processing ends here
  case "def" => println("second") //This is not displayed
}

The fall-through behavior of C language switch-case statements was notorious for being more buggy than an advantage. It is often criticized that Java has taken over the fallthrough behavior of C. That's why Scala pattern matching doesn't have fallthrough behavior, but there's a | in case you want to combine multiple patterns.


"abc" match {
  case "abc" | "def" =>
    println("first")
    println("second")
}

Extracting values by pattern matching

Other than switch-case, there is a way to match some of the elements of the collection. Let's take a look at the following program.


scala> val lst = List("A", "B", "C")
lst: List[String] = List(A, B, C)

scala> lst match {
     |   case List("A", b, c) =>
     |     println("b = " + b)
     |     println("c = " + c)
     |   case _ =>
     |     println("nothing")
     | }
b = B
c = C

Here, if the first element of List is "A" and matches the pattern of three elements, the second and subsequent elements of List are bound to the remaining b and c, and the expression on the right side of => is evaluated. Become. Match expressions are often used to match elements in a collection.

In pattern matching, you can use a guard expression so that the expression on the right side is not evaluated unless it matches the pattern and also the guard expression (must be a Boolean type).


scala> val lst = List("A", "B", "C")
lst: List[String] = List(A, B, C)

scala> lst match {
     |   case List("A", b, c) if b != "B" =>
     |     println("b = " + b)
     |     println("c = " + c)
     |   case _ =>
     |     println("nothing")
     | }
nothing

Here, because the guard condition of pattern matching is that the second element of List is not "B", it did not match the first condition and matched _.

Also, pattern matching patterns can be nested. Let's modify the previous program a little to match a List that starts with List ("A").


scala> val lst = List(List("A"), List("B", "C"))
lst: List[List[String]] = List(List(A), List(B, C))

scala> lst match {
     |   case List(a@List("A"), x) =>
     |   println(a)
     |   println(x)
     |   case _ => println("nothing")
     | }
List(A)
List(B, C)

lst is List("A")And List("B", "C")It is a List consisting of two elements. Here, by using a match expression, the beginning is List("A")You can see that we can describe the nested pattern of. Also before the pattern@The one with is called as pattern,@An expression that matches the pattern that follows@Bind to the variable before (a in this case). The as pattern is useful when you want to cut out only a part of the pattern when the pattern is complicated. However|Note that you cannot retrieve the value for pattern matching using. As described below|If you use variables in pattern matching, you will get a compile error.


scala> (List("a"): Any) match {
     |   case List(a) | Some(a) =>
     |     println(a)
     | }
<console>:14: error: illegal variable in pattern alternative
         case List(a) | Some(a) =>
                   ^
<console>:14: error: illegal variable in pattern alternative
         case List(a) | Some(a) =>
                             ^

Pattern matching that does not retrieve the value is possible.


(List("a"): Any) match {
  case List(_) | Some(_) =>
    println("ok")
}

Extracting values using introductory patterns

You can write a pattern match like the one we wrote in the previous section in a different notation. For example


scala> val lst = List("A", "B", "C")
lst: List[String] = List(A, B, C)

scala> lst match {
     |   case List("A", b, c) =>
     |     println("b = " + b)
     |     println("c = " + c)
     |   case _ =>
     |     println("nothing")
     | }
b = B
c = C

The code can be rewritten as follows.


scala> val lst = List("A", "B", "C")
lst: List[String] = List(A, B, C)

scala> lst match {
     |   case "A" :: b :: c :: _ =>
     |     println("b = " + b)
     |     println("c = " + c)
     |   case _ =>
     |     println("nothing")
     | }
b = B
c = C

Here, a pattern name (: :) appears between the elements of the list, such as "A" :: b :: c :: _, is called an inset pattern. When pattern matching is done with an inset pattern (: :), the element before :: points to the first element of the list and the element after :: points to the rest of the list. If you want to ignore the end of the list, you'll need to insert _ at the end of the pattern, as shown above. Keep in mind that list inlay patterns are common in Scala programming, so there is such a feature.

Pattern matching by type

You can also use a pattern that matches only if the value belongs to a particular type. Patterns that match only if the value belongs to a particular type are used in the form of name: matching type. For example, you can use it like this: The AnyRef type is a type equivalent to the Java Object type, and any reference type value can be stored in the AnyRef type variable.


scala> import java.util.Locale
import java.util.Locale

scala> val obj: AnyRef = "String Literal"
obj: AnyRef = String Literal

scala> obj match {
     |   case v:java.lang.Integer =>
     |     println("Integer!")
     |   case v:String =>
     |     println(v.toUpperCase(Locale.ENGLISH))
     | }
STRING LITERAL

You can see that it doesn't match java.lang.Integer, it matches String. This pattern is sometimes used for exception handling and the definition of equals. Values that match a type can be treated as if they were cast to that type.

For example, v that matches the String type in the above expression can call the String type method toUpperCase. It's a good idea to keep in mind that Scala often uses pattern matching instead of casting.

Pitfalls of type pattern matching due to JVM constraints

There is one thing to keep in mind when pattern matching types. Due to the restrictions of the JVM that runs Scala, pattern matching will not be performed correctly when using type variables.

For example, if you try to perform the following pattern matching with REPL, you will get a warning.


scala> val obj: Any = List("a")
obj: Any = List(a)

scala> obj match {
     |   case v: List[Int]    => println("List[Int]")
     |   case v: List[String] => println("List[String]")
     | }
<console>:16: warning: non-variable type argument Int in type pattern List[Int](the underlying of List[Int]) is unchecked since it is eliminated by erasure
         case v: List[Int]    => println("List[Int]")
                 ^
<console>:17: warning: non-variable type argument String in type pattern List[String](the underlying of List[String]) is unchecked since it is eliminated by erasure
         case v: List[String] => println("List[String]")
                 ^
<console>:17: warning: unreachable code
         case v: List[String] => println("List[String]")
                                        ^
List[Int]

List [Int] and List [String] are different types, but pattern matching cannot distinguish them.

The meaning of the first two warnings is that the Scala compiler's "type erasure" action erases the Int part of List [Int] and is not checked.

As a result, the two patterns are indistinguishable, and pattern matching is performed in order from the top, so the second pattern is unreachable code. The third warning means this.

For pattern matching of types that include type variables, it's a good idea to use wildcard patterns as follows:


obj match {
  case v: List[_] => println("List[_]")
}


end

Next time I will study about classes.

reference

This document is CC BY-NC-SA 3.0

image.png It is distributed under.

https://dwango.github.io/scala_text/

Also, regarding the yield formula etc., it seems good to continue the following article.

https://qiita.com/harry0000/items/e37ca3bfb68df839bf55

Recommended Posts

I touched Scala ~ [Control syntax] ~
I touched Scala
I touched Scala ~ [Class] ~
I touched Scala ~ [Object] ~
I touched Scala ~ [Trait] ~
Java control syntax
Java control syntax
Control syntax operator
I touched Scala ~ [Type parameters and displacement specification] ~
I first touched Java ②
I first touched Java ③
[Java] Control syntax notes
I first touched Java ④
I first touched Java
I went to Scala Fukuoka 2019!
[Java] Summary of control syntax