Scala is a functional language, and we’ll talk about a few concepts:

  • Higher-order functions
  • Methods the nested
  • Multiparameter list
  • The sample class
  • Pattern matching
  • singleton
  • Regular expression pattern
  • For expression

Higher-order functions

A higher-order function is usually a function of a function, that is, the output parameter of a function is a function or the return result of a function is a function. Functions are first-class citizens in Scala.

Let’s take a look at the higher-order map function of the Scala collections class:

val salaries = Seq(20000.70000.40000)
val doubleSalary = (x: Int) => x * 2
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
Copy the code

Map takes a function as an argument. So map is a higher-order function, and map can also receive an anonymous function directly, as follows:

val salaries = Seq(20000.70000.40000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
Copy the code

In the example above, we do not show the form x:Int because the compiler can infer the type of x by type inference. A more simplified form for this is:

val salaries = Seq(20000.70000.40000)
val newSalaries = salaries.map(_ * 2)
Copy the code

Since the Scala compiler already knows the type of the argument (a single Int), you can just give the right half of the function, but use _ instead of the argument name (x in the previous example).

Cast method to function

If you pass a method to a higher-order function, Scala casts that method to a function, as follows:

case class WeeklyWeatherForecast(temperatures: Seq[Double]) {

  private def convertCtoF(temp: Double) = temp * 1.8 + 32

  def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
}
Copy the code

In this example, the method convertCtoF is passed in for the forecastInFahrenheit. This is ok because the compiler forces the method convertCtoF into function x => convertCtoF(x) (note: x is the compiler generated variable name, guaranteed to be unique in its scope).

Methods the nested

Methods can be nested within Scala’s methods, as follows:

 def factorial(x: Int) :Int = {
    def fact(x: Int, accumulator: Int) :Int = {
      if (x <= 1) accumulator
      else fact(x - 1, x * accumulator)
    }  
    fact(x, 1)
 }

 println("Factorial of 2: " + factorial(2))
 println("Factorial of 3: " + factorial(3))
Copy the code

The program output is:

Factorial of 2: 2
Factorial of 3: 6
Copy the code

Multiparameter list

Scala differs from Java in that it can define multiple argument lists. Here’s an example:

def foldLeft[B](z: B)(op: (B.A) = >B) :B
Copy the code

As you can see, this method defines two argument lists, z is the initial value, op is a binary operation, and here is a call to it:

val numbers = List(1.2.3.4.5.6.7.8.9.10)
val res = numbers.foldLeft(0)((m, n) => m + n)
print(res) / / 55
Copy the code

Using Scala’s type inference, we can make our code more concise:

numbers.foldLeft(0+ _) (_)Copy the code

The sample class

Case classes are primarily used for immutable data. They are almost identical to ordinary classes.

case class Book(isbn: String)

val frankenstein = Book("978-04862821 14")
Copy the code

The new keyword is not required when instantiating the case class, because the case class has a default apply method that takes care of object creation.

In case class, the arguments are public and val, which means that case class arguments are immutable:

case class Message(sender: String, recipient: String, body: String)
val message1 = Message("[email protected]"."[email protected]".Ca va? "")

println(message1.sender)  // prints [email protected]
message1.sender = "[email protected]"  // this line does not compile
Copy the code

Here the message1.sender cannot be reassigned because it is val.

To compare

Case class comparisons are made by value, not by reference:

case class Message(sender: String, recipient: String, body: String)

val message2 = Message("[email protected]"."[email protected]"."Com va?")
val message3 = Message("[email protected]"."[email protected]"."Com va?")
val messagesAreTheSame = message2 == message3  // true
Copy the code

The above objects are different, but because they have the same value, the final comparison is true.

copy

You can use copy to make a shallow copy of a case class.

case class Message(sender: String, recipient: String, body: String)
val message4 = Message("[email protected]"."[email protected]"."Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "[email protected]")
message5.sender  // [email protected]
message5.recipient // [email protected]
message5.body  // "Me zo o komz gant ma amezeg"
Copy the code

Pattern matching

Scala uses the match keyword and case for pattern matching, similar to switch in Java.

Here is a simple pattern matching example:

import scala.util.Random

val x: Int = Random.nextInt(10)

x match {
  case 0= >"zero"
  case 1= >"one"
  case 2= >"two"
  case_ = >"other"
}
Copy the code

The last case _ matches all other cases.

The match expression has a value as follows:

def matchTest(x: Int) :String = x match {
  case 1= >"one"
  case 2= >"two"
  case_ = >"other"
}
matchTest(3)  // other
matchTest(1)  // one
Copy the code

Case can also match case class as follows:

abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class SMS(caller: String, message: String) extends Notification

case class VoiceRecording(contactName: String, link: String) extends Notification

def showNotification(notification: Notification) :String = {
  notification match {
    case Email(sender, title, _) =>
      s"You got an email from $sender with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
    case VoiceRecording(name, link) =>
      s"you received a Voice Recording from $name! Click the link to hear it: $link"}}val someSms = SMS("12345"."Are you there?")
val someVoiceRecording = VoiceRecording("Tom"."voicerecording.org/id/123")

println(showNotification(someSms))  // prints You got an SMS from 12345! Message: Are you there?

println(showNotification(someVoiceRecording))  // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

Copy the code

Case can also be followed by an if statement, which we call pattern guard.

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String) :String = {
  notification match {
    case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
      "You got an email from special someone!"
    case SMS(number, _) if importantPeopleInfo.contains(number) =>
      "You got an SMS from special someone!"
    case other =>
      showNotification(other) // nothing special, delegate to our original showNotification function}}Copy the code

You can also do type matching only:

abstract class Device
case class Phone(model: String) extends Device {
  def screenOff = "Turning screen off"
}
case class Computer(model: String) extends Device {
  def screenSaverOn = "Turning screen saver on..."
}

def goIdle(device: Device) = device match {
  case p: Phone => p.screenOff
  case c: Computer => c.screenSaverOn
}
Copy the code

Seal type

Traits and classes can be sealed, meaning that all of their subclasses must be defined in the same file as them.

sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furniture

def findPlaceToSit(piece: Furniture) :String = piece match {
  case a: Couch= >"Lie on the couch"
  case b: Chair= >"Sit on the chair"
}
Copy the code

singleton

A singleton is a special class that can be represented by the keyword object. Singletons are created lazily, only when they are used for the first time.

package logging

object Logger {
  def info(message: String) :Unit = println(s"INFO: $message")}Copy the code

One function of a singleton is to define functional methods that can be used anywhere, like the INFO method in the example above. It can be used as follows:

import logging.Logger.info

class Project(name: String, daysToComplete: Int)

class Test {
  val project1 = new Project("TPS Reports".1)
  val project2 = new Project("Website redesign".5)
  info("Created projects")  // Prints "INFO: Created projects"
}
Copy the code

Associated object

A companion object is a singleton object with the same name as a class, and a class and its companion can access each other’s private members. Here is an example of a companion object:

import scala.math._

case class Circle(radius: Double) {
  import Circle. _def area: Double = calculateArea(radius)
}

object Circle {
  private def calculateArea(radius: Double) :Double = Pi * pow(radius, 2.0)}val circle1 = Circle(5.0)

circle1.area
Copy the code

Circle1, the companion object, can access the area defined in the class.

Note: the class and its companion objects must be defined in the same source file.

Regular expression pattern

In Scala, you can use the.r method to turn any string into a regular expression. As follows:

import scala.util.matching.Regex

val numberPattern: Regex = "[0-9].".r

numberPattern.findFirstMatchIn("awesomepassword") match {
  case Some(_) => println("Password OK")
  case None => println("Password must contain a number")}Copy the code

You can also use parentheses to match multiple sets of regular expressions simultaneously.

import scala.util.matching.Regex val keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r val input: String = """background-color: #A03300; |background-image: url(img/header100.png); |background-position: top center; |background-repeat: repeat-x; |background-size: 2160px 108px; |margin: 0; |height: 108px; |width: 100%;" "".stripMargin for (patternMatch <- keyValPattern.findAllMatchIn(input)) println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}")Copy the code

For expression

In Scala, the for loop is used with yield, of the form for (enumerators) yield e. Here enumerators are a group of enumerators separated by semicolons. The enumerator here is either a generator that generates new variables or a filter. The for expression evaluates the e value in each binding produced by the enumerator and returns a sequence of these values at the end of the loop. As follows:

case class User(name: String, age: Int)

val userBase = List(User("Travis".28),
  User("Kelly".33),
  User("Jennifer".44),
  User("Dennis".23))

val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30))
  yield user.name  // i.e. add this to a list

twentySomethings.foreach(name => println(name))  // prints Travis Dennis

Copy the code

Here is a more complex example:

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- i until n if i + j == v)
   yield (i, j)

foo(10.10) foreach {
  case (i, j) =>
    println(s"($i.$j)")  // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5)
}

Copy the code

You can omit the yield statement when using a for expression. Unit is returned.

See flydean’s blog for more tutorials