Scala高阶函数与akka 1

简介: Scala高阶函数与akka

1 高阶函数

Scala 混合了面向对象和函数式的特性,在函数式编程语言中,函数是“头等公民”,它和Int、String、Class等其他类型处于同等的地位,可以像其他任何数据类型一样被传递和操作。

高阶函数包含:作为值的函数、匿名函数、闭包、柯里化等等。

1.1 作为值的函数


在scala中,函数就像和数字、字符串一样,可以将函数传递给一个方法。我们可以对算法进行封装,然后将具体的动作传递给算法,这种特性很有用。

我们之前学习过List的map方法,它就可以接收一个函数,完成List的转换。

示例:将一个小数列表中的每个元素转换为对应个数的小星星

List(1, 2, 3…) => *, **, **

代码:

val list = List(1, 2, 3, 4)
// 字符串*方法,表示生成指定数量的字符串
val func_num2star = (num:Int) => "*" * num
print(list.map(func_num2star))

1.2 匿名函数


上面的代码,给(num:Int) => “*” * num函数赋值给了一个变量,但是这种写法有一些啰嗦。在scala中,可以不需要给函数赋值给变量,没有赋值给变量的函数就是匿名函数

示例:优化上述代码

val list = List(1, 2, 3, 4)
list.map(num => "*" * num).foreach(println)
// 因为此处num变量只使用了一次,而且只是进行简单的计算,所以可以省略参数列表,使用_替代参数
list.map("*" * _).foreach(println)

1.3 柯里化


柯里化(Currying)是指将原先接受多个参数的函数多个一个参数的多参数列表的过程。

柯里化过程解析

使用柯里化,让传递匿名函数作为参数的语法更为简洁

示例:编写一个泛型方法,用来完成两个值类型的计算(具体的计算封装到函数中)

object CurryingDemo2 {
  // 实现对两个数进行计算的方法
  def calc[A <: AnyVal](x:A, y:A, func_calc:(A, A)=>A) = {
    func_calc(x, y)
  }
  // 柯里化:实现对两个数进行计算
  def calc_carried[A <: AnyVal](x:A, y:A)(func_calc:(A, A)=>A) = {
    func_calc(x, y)
  }
  def main(args: Array[String]): Unit = {
    // 这种写法是不能被简化的,必须要写出函数的定义
    println(calc(10, 10, (x:Int, y:Int)=> x + y))
    println(calc(10.1, 10.2, (x:Double, y:Double)=> x*y))
    // 柯里化之后可以快乐地使用下划线了
    println(calc_carried(10, 10)(_ + _))
    println(calc_carried(10.1, 10.2)(_ * _))
    println(calc_carried(100.2, 10)(_ - _))
  }
}

1.4 闭包


闭包其实就是一个函数,只不过这个函数的返回值依赖于声明在函数外部的变量。

可以简单认为,就是可以访问不在当前作用域范围的一个函数。

示例:定义一个闭包

object ClosureDemo {
  def add(x:Int) = {
    val y = 10
    // add返回一个函数,该函数引用了add方法的一个局部变量
    val funcAdd = () => x + y
    funcAdd
  }
  def main(args: Array[String]): Unit = {
    // 调用add方法时,任然可以引用到y的值
    // funcAdd函数就是一个闭包
    println(add(10)())
  }
}

2 隐式转换和隐式参数

隐式转换和隐式参数是scala非常有特色的功能,也是Java等其他编程语言没有的功能。我们可以很方便地利用隐式转换来丰富现有类的功能。

2.1 隐式转换


来看一个案例,

object SuperIntDemo {
  def main(args: Array[String]): Unit = {
    val a:Int = 1
  // 使用中缀调用to方法
    println(a to 10)
  }
}

通过查看Int的源代码,你会惊奇地发现,Int类型根本没有to方法。这难道是让人怀疑人生的大bug吗?


——这其实就是隐式转换的强(gui)大(yi)之处。它在背后偷偷摸摸地帮我们了某种类型转换。


所谓隐式转换,是指以implicit关键字声明的带有单个参数的方法。它是自动被调用的,自动将某种类型转换为另外一种类型。


隐式转换的使用步骤:


在object中定义隐式转换方法(使用implicit)

在需要用到隐式转换的地方,引入隐式转换(使用import)

自动调用隐式转化后的方法

示例:使用隐式转换,让File具备有reada

class RichFile(val f:File) {
  // 将文件中内容读取成字符串
  def read() = Source.fromFile(f).mkString
}
object MyPredef {
  // 定义隐式转换方法
  implicit def file2RichFile(f:File) = new RichFile(f)
}
object ImplicitConvertDemo {
  def main(args: Array[String]): Unit = {
    val f = new File("./data/textfiles/1.txt")
    // 导入隐式准换
    import MyPredef.file2RichFile
    // 调用的其实是RichFile的read方法
    println(f.read())
  }
}

2.2 自动导入隐式转换方法


前面,我们手动使用了import来导入隐式转换。是否可以不手动import呢?

在scala中,如果在当前作用域中有隐式转换方法,会自动导入隐式转换。

示例:将隐式转换方法定义在main所在的object中

class RichFile(val f:File) {
  // 将文件中内容读取成字符串
  def read() = Source.fromFile(f).mkString
}
object ImplicitConvertDemo {
  // 定义隐式转换方法
  implicit def file2RichFile(f:File) = new RichFile(f)
  def main(args: Array[String]): Unit = {
    val f = new File("./data/textfiles/1.txt")
    // 调用的其实是RichFile的read方法
    println(f.read())
  }
}

2.3 隐式转换的时机


什么时候会自动执行隐式转换呢?

  1. 当对象调用中不存在的方法时,编译器会自动将对象进行隐式转换
  2. 当方法中的参数类型与目标类型不一致时

示例:

object ImplicitConvertDemo {
  // 定义隐式转换方法
  implicit def file2RichFile(f:File) = new RichFile(f)
  def main(args: Array[String]): Unit = {
    val f = new File("./data/textfiles/1.txt")
    // test1接收的参数类型为Rich,此时也自动进行了隐式转换
    test1(f)
  }
  def test1(r:RichFile) = println(r.read())
}

2.4 隐式参数


函数或方法可以带有一个标记为implicit的参数列表。这种情况,编译器会查找缺省值,提供给该方法。

定义隐式参数:

  1. 在方法后面添加一个参数列表,参数使用implicit修饰
  2. 在object中定义implicit修饰的隐式值
  3. 调用方法,可以不传入implicit修饰的参数列表,编译器会自动查找缺省值

示例:

// 定义一个分隔符类
case class Delimiters(left:String, right:String)
object MyPredef1 {
  implicit val quoteDelimiters = Delimiters("<<<", ">>>")
}
object ImplicitParamDemo {
  // 使用分隔符将想要引用的字符串括起来
  def quote(what:String)(implicit delims:Delimiters) = delims.left + what + delims.right
  def main(args: Array[String]): Unit = {
    println(quote("hello, world")(Delimiters("<<", ">>")))
    // 手动导入
    import MyPredef1._
    println(quote("hello, world"))
  }
}
  1. 和隐式转换一样,可以使用import手动导入隐式参数
  2. 如果在当前作用域定义了隐式值,会自动进行导入

3 Akka RPC框架入门

Akka简介

Akka是一个用于构建高并发、分布式和可扩展的基于事件驱动的应用的工具包。Akka是使用scala开发的库,同时可以使用scala和Java语言来开发基于Akka的应用程序。

3.1 Akka特性

  • 提供基于异步非阻塞、高性能的事件驱动编程模型
  • 内置容错机制,允许Actor在出错时进行恢复或者重置操作
  • 超级轻量级的事件处理(每GB堆内存几百万Actor)
  • 使用Akka可以在单机上构建高并发程序,也可以在网络中构建分布式程序。

3.2 Akka Actor并发编程模型


以下图片说明了Akka Actor的并发编程模型的基本流程:

  1. 学生创建一个ActorSystem
  2. 通过ActorSystem来创建一个ActorRef(老师的引用),并将消息发送给ActorRef
  3. ActorRef将消息发送给Message Dispatcher(消息分发器)
  4. Message Dispatcher将消息按照顺序保存到目标Actor的MailBox中
  5. Message Dispatcher将MailBox放到一个线程中
  6. MailBox按照顺序取出消息,最终将它递给TeacherActor接受的方法中

20210708145332473.png

3.3 入门案例


案例:基于Akka创建两个Actor,Actor之间可以互相发送消息。

实现步骤:

  1. IDEA创建Maven项目
  2. 选择Maven项目,添加scala支持
  3. 导入Akka Maven依赖
  4. 创建两个Actor
  1. 创建、发送消息
  2. 启动测试

具体实现:

  1. 创建Maven项目,添加scala支持

打开pom.xml文件,导入akka Maven依赖和插件

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>UTF-8</encoding>
        <scala.version>2.11.8</scala.version>
        <scala.compat.version>2.11</scala.compat.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_2.11</artifactId>
            <version>2.3.14</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_2.11</artifactId>
            <version>2.3.14</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <args>
                                <arg>-dependencyfile</arg>
                                <arg>${project.build.directory}/.scala_dependencies</arg>
                            </args>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>reference.conf</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass></mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  1. 创建两个Actor
  • SenderActor:用来发送消息
  • ReceiveActor:用来接收,回复消息
  1. 创建、发送消息
  • 使用样例类封装消息
  • SubmitTaskMessage——提交任务消息
  • SuccessSubmitTaskMessage——任务提交成功消息
  • 使用类似于之前学习的Actor方式,使用!发送异步消息
  1. 启动测试
目录
相关文章
|
并行计算 Scala
175 Scala 项目案例(Akka简介)
175 Scala 项目案例(Akka简介)
105 0
|
4月前
|
Scala 开发者
Scala中的模式匹配与高阶函数:探索强大的编程范式
【7月更文挑战第11天】Scala中的模式匹配和高阶函数是两种极其强大的特性,它们不仅提升了代码的表达力和可读性,还使得开发者能够编写出更加灵活和可重用的解决方案。通过
|
6月前
|
SQL 分布式计算 编译器
Scala:高阶函数、隐式转换(四)
Scala:高阶函数、隐式转换(四)
54 0
|
Scala
172 Scala 高阶函数例子
172 Scala 高阶函数例子
30 0
|
算法 Scala
171 Scala 高阶函数
171 Scala 高阶函数
37 0
|
分布式计算 前端开发 Java
Scala高阶函数与akka 2
Scala高阶函数与akka
65 0
Scala快速入门-9-高阶函数
作为值的函数 创建匿名函数 带函数参数的函数 闭包 柯里化
|
分布式计算 Shell API
scala函数式编程与高阶函数
谈到编程方式有指令式编程、过程化编程、函数式编程,函数式编程在使用时主要的思想就是把运算过程写成一些列嵌套的函数调用。把一组数据通过函数实现运算转化成另一组数据。函数式编程这种方式在我们将来开发Spark、Flink的业务代码时会大量使用。下面列出了一些我们经常使用的进行函数式编程的Iterable trait(可迭代特质)方法。
87 0
|
大数据 网络安全 Scala
大数据Scala系列之高阶函数
大数据Scala系列之高阶函数1.1. 概念如果一个函数的传入参数为函数或者返回值是函数,则该函数即为高阶函数。 1.2. 传入参数为函数Scala中,函数是头等公民,和数字一样。不仅可以调用,还可以在变量中存放函数,也可以作为参数传入函数,或者作为函数的返回值。
1038 0