Scala: Higher-order functions, implicit conversions
Course objectives
- Understand the concepts of higher-order functions (functions as values, anonymous functions, closures, Currification)
- Master implicit conversions and implicit parameters
- Master Akka concurrent programming framework
1. Higher-order functions
Scala is a blend of object-oriented and functional features. In functional programming languages, functions are “first-class citizens,” on a par with other types like Int, String, and Class, and can be passed and manipulated just like any other type of variable.
Higher order functions contain
- As a function of values
- Anonymous functions
- closure
- And so on
1.1 as a function of values
In Scala, functions can be passed to a method just like numbers and strings. It is useful to be able to encapsulate an algorithm and then pass specific actions to a method.
We learned earlier about the map method of a List, which accepts a function to convert the List.
The sample
example
Converts each element in a list of integers to a corresponding number of small stars
List(1, 2, 3...) = > *, * *, * * *Copy the code
steps
- Creates a function that replaces a number with a specified number of stars
- Create a list and call the map method
- Prints a list of conversions to
Reference code
val func: Int= >String = (num:Int) = >"*" * num
println((1 to 10).map(func))
Copy the code
1.2 Anonymous Functions
define
The above code assigns (num:Int) => “*” * num to a variable, but this is a bit verbose. In Scala, you don’t need to assign a function to a variable; a function that doesn’t assign a variable is an anonymous function
val list = List(1.2.3.4)
// String * method, which generates a specified number of strings
val func_num2star = (num:Int) = >"*" * num
print(list.map(func_num2star))
Copy the code
The sample
Optimize the above code with anonymous functions
Reference code
println((1 to 10).map(num => "*" * num))
// Because the num variable is used only once here, and only for a simple calculation, you can omit the argument list and use _ instead
println((1 to 10).map("*" * _))
Copy the code
1.3 Mr Currie
The Scala and Spark source code makes extensive use of cremation. In order to read the source code later, we need to understand the currization.
define
Currying is the process of converting a method that originally takes multiple arguments into multiple argument lists that only take one argument.
Outside the chain picture archiving failure, the source station might be hotlinking prevention mechanism, proposed to directly upload picture preserved (img – Yitd5lcH – 1625207556241) (/ assets / 1552811606951 PNG)]
Analysis of the Currie process
The sample
example
- Write a method to evaluate two ints
- How do you compute encapsulation in a function
- Use Coriolization to do this
Reference code
// Currization: implement the calculation of two numbers
def calc_carried(x:Double, y:Double)(func_calc:(Double.Double) = >Double) = {
func_calc(x, y)
}
def main(args: Intrray[String) :Unit = {
println(calc_carried(10.1.10.2){
(x,y) => x + y
})
println(calc_carried(10.10)(_ + _))
println(calc_carried(10.1.10.2)(_ * _))
println(calc_carried(100.2.10) (_ _))}Copy the code
1.4 the closure
A closure is simply a function whose return value depends on the variables declared outside the function.
You can simply think of it as access to a function that is not in the current scope.
The sample a
Define a closure
val y=10
val add=(x:Int)=>{
x+y
}
println(add(5)) 15 / / results
Copy the code
The add function is a closure
Example 2
The Currization is just a closure
def add(x:Int)(y:Int) = {
x + y
}
Copy the code
The above code is equivalent to
def add(x:Int) = {
(y:Int) => x + y
}
Copy the code
2. Implicit conversions and implicit parameters
Implicit conversions and implicit parameters are features of Scala that are absent from other programming languages such as Java. Implicit transformations can easily be used to enrich the functionality of existing classes. Implicit conversion and implicit parameters will be seen later in Akka concurrent programming, Spark SQL, and Flink.
2.1 define
Implicit conversions are methods that take a single parameter and are declared with an implicit keyword. It is called automatically, automatically converting one type to another.
Using the step
- Defining implicit conversion methods in Object (using Implicit)
- Introduce implicit conversions where they are needed (using import)
- Automatically invoke the implicitly transformed method
The sample
example
Use an implicit conversion to make File read — read the contents of the text as a string
steps
- Create a RichFile class that provides a read method to read the contents of the file as a string
- Define an implicit conversion method to implicitly convert File to a RichFile object
- Create a File, import the implicit conversion, and call the read method of File
Reference code
class RichFile(val file:File) {
// Read the file as a string
def read() = {
Source.fromFile(file).mkString
}
}
object RichFile {
// Define implicit conversion methods
implicit def file2RichFile(file:File) = new RichFile(file)
}
def main(args: Array[String) :Unit = {
// Load the file
val file = new File("./data/1.txt")
// Import implicit conversions
import RichFile.file2RichFile
// The file object has the read method
println(file.read())
}
Copy the code
2.2 Timing of implicit conversion
- When an object calls a method or member that does not exist in the class, the compiler automatically converts the object implicitly
- When the type of the parameter in a method is inconsistent with the target type
2.3 Automatic import of implicit conversion methods
Earlier, we used import manually to import implicit transformations. Is it possible not to import manually?
In Scala, implicit conversions are automatically imported if they exist in the current scope.
Example: Define an implicit conversion method in the object of Main
class RichFile(val f:File) {
// Read the contents of the file as a string
def read() = Source.fromFile(f).mkString
}
object ImplicitConvertDemo {
// Define implicit conversion methods
implicit def file2RichFile(f:File) = new RichFile(f)
def main(args: Array[String) :Unit = {
val f = new File("./data/textfiles/1.txt")
// Call RichFile's read method
println(f.read())
}
}
Copy the code
2.4 Implicit parameters
Methods can take a list of arguments labeled implicit. In this case, the compiler looks for the default value to provide to the method.
define
- Add a list of parameters to the method using the implicit modifier
- Define an implicit value with an implicit modifier in object
- The method can be called without passing in a list of parameters that are implicit, and the compiler automatically looks for defaults
[!NOTE]
- As with implicit conversion, implicit parameters can be imported manually using import
- If an implicit value is defined in the current scope, it is imported automatically
The sample
example
- Defines a method to include a value passed in with a delimiter prefix and suffix
- Use implicit parameters to define delimiters
- Call this method and print the test
Reference code
// Use implicit to define a parameter
def quote(what:String) (implicit delimiter:(String.String)) = {
delimiter._1 + what + delimiter._2
}
// Implicit arguments
object ImplicitParam {
implicit val DEFAULT_DELIMITERS = ("< < <"."> > >")}def main(args: Array[String) :Unit = {
// Import implicit parameters
import ImplicitParam.DEFAULT_DELIMITERS
println(quote("Li Lei and Han Meimei"))}Copy the code
elimiter:(String, String)) = { delimiter._1 + what + delimiter._2 }
Object ImplicitParam {implicitVal DEFAULT_DELIMITERS = (“<<<“, “>>>”)}
Def main(args: Array[String]): Unit = {// Import implicit parameter import implicitParam.default_delimiters
Println (quote(" println "))Copy the code
}
Copy the code