A, functions,

1.1 Functions and methods

The difference between a function and a method in Scala is very small; if a function is a member of an object, it is called a method; otherwise, it is a normal function.

// Define the method
def multi1(x:Int) = {x * x}
// Define the function
val multi2 = (x: Int) => {x * x}

println(multi1(3)) / / output 9
println(multi2(3)) / / output 9
Copy the code

We can also use def to define functions:

def multi3 = (x: Int) => {x * x}
println(multi3(3))  / / output 9
Copy the code

Multi2 is essentially the same as multi3 because functions are first-class citizens. Val multi2 = (x: Int) => {x * x} is equivalent to using def to predefine the function and then assign it to the variable multi2.

1.2 Function Types

We said that multi2 and multi3 are essentially the same, so what type are they as functions? Both types are actually Int => Int, with the first Int representing the input parameter type and the second Int representing the return value type.

scala> val multi2 = (x: Int) => {x * x} multi2: Int => Int = ? Lambda$1092/594363215@1dd1a777

scala> def multi3 = (x: Int) => {x * x}
multi3: Int => Int

// if there are multiple parameters, the type is :(parameter type, parameter type...) => Return value typescala> val multi4 = (x: Int,name: String) => {name + x * x } multi4: (Int, String) => String = ? Lambda$1093/1039732747@2eb4fe7
Copy the code

1.3 First-class citizen & Anonymous function

Functions are first-class citizens in Scala, which means that not only can functions be defined and called, but they can also be passed as values:

import scala.math.ceil
object ScalaApp extends App {
  // Assign the function ceil to fun, using an underscore (_) to indicate that it is a ceil function but passing no arguments
  val fun = ceil _
  println(fun(2.3456))  / / output 3.0

}
Copy the code

You don’t have to name every function in Scala, for example (x: Int) => 3 * x is an anonymous function:

object ScalaApp extends App {
  // 1. Anonymous functions
  (x: Int) => 3 * x
  // 2. Named functions
  val fun = (x: Int) => 3 * x
  // 3. Use the anonymous function directly
  val array01 = Array(1.2.3).map((x: Int) => 3 * x)  
  // 4. Use placeholder shorthand for anonymous functions
  val array02 = Array(1.2.3).map(_ * 3)
  // 5. Use named functions
  val array03 = Array(1.2.3).map(fun)
  
}
Copy the code

1.4 Special function expressions

1. Variable length parameter list

In Java, if you want to pass variable length arguments, use String… The Scala equivalent is args: String*.

object ScalaApp extends App {
  def echo(args: String*): Unit = {
    for (arg <- args) println(arg)
  }
  echo("spark"."hadoop"."flink")}/ / output
spark
hadoop
flink
Copy the code

2. Pass named parameters

You can specify specific parameter names when passing parameters to a function.

object ScalaApp extends App {
  
  def detail(name: String, age: Int): Unit = println(name + ":" + age)
  
  // 1. The parameters are passed in the order defined
  detail("heibaiying".12)
  // 2. Specify a name when passing parameters, so you do not have to follow the defined order
  detail(age = 12, name = "heibaiying")}Copy the code

3. Default values

When you define a function, you can specify default values for parameters.

object ScalaApp extends App {

  def detail(name: String, age: Int = 88): Unit = println(name + ":" + age)

  // If no age value is passed, the default is used
  detail("heibaiying")
  detail("heibaiying".12)}Copy the code

Second, the closure

2.1 Definition of closures

var more = 10
// addMore a closure function that closes the function literal because it captures the free variable more
val addMore = (x: Int) => x + more
Copy the code

The function addMore has two variables x and more:

  • X: is a bound variable, because it is an input parameter to the function and is clearly defined within the context of the function.
  • More: is a free variable, because the function literal does not give any meaning to more.

By definition: when you create a function that needs to capture a free variable, the function that contains a reference to the captured variable is called a closure function.

2.2 Modifying free variables

It is important to note here that closures capture the variable itself, i.e. a reference to the variable itself, which means:

  • Modifications to free variables outside the closure are visible inside the closure;
  • Changes made to free variables inside a closure are also visible outside the closure.
// Declare the more variable
scala> var more = 10
more: Int = 10

// The more variable must already be declared, otherwise the following statement will report an errorscala> val addMore = (x: Int) => {x + more} addMore: Int => Int = ? Lambda$1076/1844473121@876c4f0

scala> addMore(10)
res7: Int = 20

// Note that the more variable is assigned, not redeclared
scala> more=1000
more: Int = 1000

scala> addMore(10)
res8: Int = 1010
Copy the code

2.3 Multiple copies of free variables

A free variable may change as the program changes, resulting in multiple copies, but the closure always points to the copy of the variable that was valid when it was created.

// Declare the more variable for the first time
scala> var more = 10
more: Int = 10

// Create closure functionsscala> val addMore10 = (x: Int) => {x + more} addMore10: Int => Int = ? Lambda$1077/1144251618@1bdaa13c

// Call the closure function
scala> addMore10(9)
res9: Int = 19

// Redeclare the more variable
scala> var more = 100
more: Int = 100

// Create a new closure functionscala> val addMore100 = (x: Int) => {x + more} addMore100: Int => Int = ? Lambda$1078/626955849@4d0be2ac

// redeclare the more variable
scala> addMore100(9)
res10: Int = 109

// reference the more variable that was declared the first time
scala> addMore10(9)
res11: Int = 19

// More is 100 globally
scala> more
res12: Int = 100
Copy the code

As can be seen from the above example, after redeclaring more, the global value of more is 100, but the closure function addMore10 still refers to more with the value of 10. This is implemented by the virtual machine. The virtual machine guarantees that after redeclaring more, the value of more is 100. The original captured copy of the variable continues to live on the heap.

Higher order functions

3.1 Using functions as arguments

When a function is defined, it can be passed in as an argument. The newly defined function is called a higher-order function.

object ScalaApp extends App {

  // 1. Define the function
  def square = (x: Int) => {
    x * x
  }

  // 2. Define higher-order functions: the first argument is a function of type Int => Int
  def multi(fun: Int => Int, x: Int) = {
    fun(x) * 100
  }

  // 3. Pass in the named function
  println(multi(square, 5)) / / output 2500
    
  // 4. Pass in the anonymous function
  println(multi(_ * 100.5)) / / output 50000

}
Copy the code

3.2 Currization of functions

The functions we defined above support only one argument list, whereas the Currified functions support multiple argument lists. Cremation refers to the process of changing a function that takes two arguments into a function that takes one. The new function takes the old second argument as an argument.

object ScalaApp extends App {
  // Define the Currization function
  def curriedSum(x: Int)(y: Int) = x + y
  println(curriedSum(2)(3)) / / output 5
}
Copy the code

Here, when you call curriedSum, you are actually making two traditional function calls in a row. The actual Currization is as follows:

  • The first call receives a call namedxReturns a function to be used for the second call, assumingxIs 2, returns the function2+y;
  • The returned function accepts argumentsyAnd evaluates and returns the value2 + 3The value of the.

To get the currified intermediate return function, it’s actually quite simple:

object ScalaApp extends App {
  // Define the Currization function
  def curriedSum(x: Int)(y: Int) = x + y
  println(curriedSum(2)(3)) / / output 5

  // Get the intermediate function 10 + y returned with an passed value of 10
  val plus: Int => Int = curriedSum(10)_
  println(plus(3)) // Output value 13
}
Copy the code

Currization supports multiple argument lists that perform currization from left to right:

object ScalaApp extends App {
  // Define the Currization function
  def curriedSum(x: Int)(y: Int)(z: String) = x + y + z
  println(curriedSum(2)(3)("name")) / / output 5 name
  
}
Copy the code

The resources

  1. Martin Odersky. Scala Programming (3rd edition)[M]. Publishing House of Electronics Industry. 2018-1-1
  2. Kay S. Horstman. Learn Scala quickly (2nd edition)[M]. Publishing House of Electronics Industry. 2017-7

See the GitHub Open Source Project: Getting Started with Big Data for more articles in the big Data series