Scala基础高阶函数隐式转换AKKA编程

Posted 杀智勇双全杀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala基础高阶函数隐式转换AKKA编程相关的知识,希望对你有一定的参考价值。

Scala高阶函数

函数

FP函数式编程中,函数是头等公民。函数的本质都是对象,都是FunctionN的实例(N是输入参数的个数)。函数可以像String、Int对象一样,作为参数传递给方法。

method1(x:Int,y:Int)  //需要两个参数 类型都为Int 调用的时候传入两个Int method1(2,3)

method2(p:(Int) =>String) //需要一个参数 类型是函数式类型  输入一个Int返回一个String的函数

作为参数传递给方法

函数作为参数传递给方法:

scala> val a1 =Array(11,22,33)
a1: Array[Int] = Array(11, 22, 33)

scala> val f1 = (x:Int) =>{x * 10}
f1: Int => Int = <function1>

scala> a1.map(f1)
res0: Array[Int] = Array(110, 220, 330)

scala> val f1 = (x:Int) =>x * 10  //由于方法体只有1行,可以省略{}
f1: Int => Int = <function1>

scala> val f1 = (x) =>x * 10   //脱离方法的调用 编译器推导不出数据类型
<console>:11: error: missing parameter type
       val f1 = (x) =>x * 10

匿名函数

函数可以省去函数名,作为匿名函数(省去了给函数命令的过程,直接把函数定义在方法中):

scala> a1.map((x:Int) =>x * 10)
res1: Array[Int] = Array(110, 220, 330)

scala> a1.map(x =>x * 10)   
res2: Array[Int] = Array(110, 220, 330)

scala> a1.map(_ * 10)  //使用_占位符代表传入的参数
res3: Array[Int] = Array(110, 220, 330)

scala> a1.map(element =>element * 10)  //对应函数的形参名称自定,见面知意可以提高代码可读性
res4: Array[Int] = Array(110, 220, 330)

scala> a1.map(zz =>zz * 10)//只要符合语法,形参名称无所谓。。。都能正常运行
res5: Array[Int] = Array(110, 220, 330)

柯里化函数

没写错,就是柯里化(不是颗粒化!!!)。柯里化是指将原先接受多个参数的方法转换为多个只有一个参数的参数列表的过程。

m1(x,y,z)---->m2(x)(y)(z)

从现象上看,柯里化有多个参数列表,有多个小括号。好处是实现分步调用函数,提高了代码的灵活性

scala> :quit

C:\\Users\\killer>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_241).
Type in expressions for evaluation. Or try :help.

scala> def m1(x:Int,y:Int) = x + y
m1: (x: Int, y: Int)Int

scala> m1(2,3)
res0: Int = 5

scala> def m2(x:Int)(y:Int) = x + y  //柯里化的格式
m2: (x: Int)(y: Int)Int

scala> m2(2)(3)  //柯里化完整调用
res1: Int = 5

scala> val tmp = m2(2)	//末尾要+_下滑线占位符,代表还未结束,否则会报错
<console>:12: error: missing argument list for method m2
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m2 _` or `m2(_)(_)` instead of `m2`.
       val tmp = m2(2)
                   ^

scala> val tmp = m2(2)_  //柯里化的分步调用 首先输入x  返回结果是关于y的一个函数
tmp: Int => Int = <function1>

scala> tmp(3)  //柯里化的最终调用
res2: Int = 5

scala> def m3(x:Int) = {   //使用普通的方法定义格式模拟柯里化的过程
     |     (y:Int) =>x + y
     | }
m3: (x: Int)Int => Int

scala> m3(2)   // (y:Int) =>2 + y
res3: Int => Int = <function1>

scala> re		//使用tab补全,发现之前用到的res都是已经能使用的全局变量
readBoolean   readChar     readFloat   readLine   readShort   readf1   readf3   refArrayOps   remote    res0   res2
readByte      readDouble   readInt     readLong   readf       readf2   ref      reflect       require   res1   res3

scala> res3(2)
res4: Int = 4

闭包函数

闭包首先是个函数,但是函数的返回值依赖于声明在函数外部的变量。闭包就是指在定义函数的时候 对外部自由变量捕获过程。

scala> def m1(x:Int,y:Int) = x + y  //普通函数  其返回值都是通过入参来决定的
m1: (x: Int, y: Int)Int

scala> def m2(x:Int) = x + y		//未定义y就先构建需要使用y的闭包函数,会报错
<console>:11: error: not found: value y
       def m2(x:Int) = x + y
                           ^

scala> val y:Int = 10		//先定义外部变量,才能定义需要使用该外部变量的方法
y: Int = 10

scala> def m2(x:Int) = x + y  //闭包函数,其返回值除了依赖入参x之外,还依赖一个外部变量y
m2: (x: Int)Int				//定义方法时,成功捕获了外部变量y,实现了闭包定义

scala> m2(3)
res5: Int = 13

scala> val y:Int =11		//闭包引用的是外部变量的指针,改变外部变量的数值后闭包函数的结果也会变化
y: Int = 11

scala> m2(3)
res6: Int = 13

Scala隐式转换

先举个普通栗子:

package com.aa.implicitDEMO

object ImplicitCase1{
  def main(args: Array[String]): Unit = {
    val a:Int = 0

    println(a to 5)  //中缀调用形式
    println(a.to(5)) //后缀调用形式
  }
}

运行后:

Range(0, 1, 2, 3, 4, 5)
Range(0, 1, 2, 3, 4, 5)

Process finished with exit code 0

ctrl+鼠标左键定位Int,发现:

package scala
final abstract class Int() extends scala.AnyVal {
  def toByte : scala.Byte
  def toShort : scala.Short
  def toChar : scala.Char
  def toInt : scala.Int
  def toLong : scala.Long
  def toFloat : scala.Float
  def toDouble : scala.Double
  def unary_~ : scala.Int
  //后边还有一大坨方法,都不是to方法
}

跳转到了Int.class

在这里插入图片描述

然而,该类内部并没有任何to方法(按首字母排序的,为了节省篇幅,后边都没有)。。。改变主意,定位to方法,发现跳转到RichInt.class

在这里插入图片描述

package scala.runtime
final class RichInt(val self : scala.Int) extends scala.AnyVal with scala.runtime.ScalaNumberProxy[scala.Int] with scala.runtime.RangedProxy[scala.Int] {  
    //这里有很多无用内容,节省篇幅
	def to(end : scala.Int) : scala.collection.immutable.Range.Inclusive = { /* compiled code */ }
	def to(end : scala.Int, step : scala.Int) : scala.collection.immutable.Range.Inclusive = { /* compiled code */ }
}

在开启Scala的命令行:

scala> :implicit -v
/* 69 implicit members imported from scala.Predef */
  /* 7 inherited from scala */
  //省略一大坨

  /* 40 inherited from scala.Predef */
  //省略一大坨

  /* 22 inherited from scala.LowPriorityImplicits */
  //省略一大坨
  implicit def intWrapper(x: Int): runtime.RichInt
  //省略一大坨

scala>

说明Scala编译器背后悄悄地进行了类型转换。。。这就像C#和Java中在合适的场景下自动int→double(C#和Java是强类型语言。C和C++可以直接操作寄存器,非0→true这种也可以,按照指针进行越界访问也可以,和C#与Java有点不同。。。)。隐式转换本质是scala编译器提供的一种代码纠错的功能。

类调用一个不属于自己的方法(调用其他类的方法)或者调用方法时不给参数,程序代码铁定报错。。。但是,如果有了隐式转换。在编译期间,scala会通过隐式转换让程序可以继续执行,实现纠错。核心就是:定义、定义方法、定义参数时使用implicit修饰。

用法

隐式参数

调用方法时没有参数列表不再报错,会自动寻找作用域中的隐式变量,尽量填充,实现纠错。但是参数过多、参数过少还是会报错。

scala> :quit

C:\\Users\\killer>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_241).
Type in expressions for evaluation. Or try :help.

scala> def m1(x:Int) = x +1
m1: (x: Int)Int

scala> m1(1,2)							//传参过多会报错
<console>:13: error: too many arguments for method m1: (x: Int)Int
       m1(1,2)
         ^

scala> m1()								//缺少参数会报错
<console>:13: error: not enough arguments for method m1: (x: Int)Int.
Unspecified value parameter x.
       m1()
         ^

scala> m1								//没有参数列表会报错
<console>:13: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m1`.
       m1
       ^

scala> m1(2)							//正常的用法
res3: Int = 3

scala> def m2(implicit x:Int) = x +1	//定义隐式方法
m2: (implicit x: Int)Int

scala> implicit val a:Int = 10086		//定义隐式参数
a: Int = 10086

scala> m2(5)
res4: Int = 6

scala> m2								//没有参数列表不报错
res5: Int = 10087

scala> m2(5,6)							//参数过多报错
<console>:14: error: too many arguments for method m2: (implicit x: Int)Int
       m2(5,6)

scala> m1()								//参数过少报错
<console>:14: error: not enough arguments for method m1: (x: Int)Int.
Unspecified value parameter x.
       m1()

隐式方法

实现类型转换

scala> :quit

C:\\Users\\killer>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_241).
Type in expressions for evaluation. Or try :help.

scala> def m1(x:String) = println()
m1: (x: String)Unit

scala> m1("haha")


scala> def m1(x:String) = println(x)
m1: (x: String)Unit

scala> m1("haha")
haha

scala> m1(12345)
<console>:13: error: type mismatch;
 found   : Int(12345)
 required: String
       m1(12345)
          ^

scala> implicit def intToString(x:Int) = x.toString		//定义转换方法,不用管警告
warning: there was one feature warning; re-run with -feature for details
intToString: (x: Int)String

scala> m1(12345)									//自动调用隐式方法,不需要手动调用
12345

scala> m1("haha")
haha

调用其它类的方法

package com.aa.implicitDEMO

class Dog {}

class RichDog {
  //必须定义在main方法前,否则main方法中找不到该方法,不能使用
  def canLearnSkill(skillName: String) = println(s"狗狗能学会${skillName}技能")
}

object HahaImplicit {
  //定义个隐式转换方法 让dog变成RichDog
  //隐式方法也必须定义在main方法前,否则main方法找不到该方法,会爆红
  implicit def dogWrapper(dog: Dog) = new RichDog
}

object ImplicitCase2 {
  def main(args: Array[String]): Unit = {
    //局部导包
    import com.aa.implicitDEMO.HahaImplicit.dogWrapper
    val dog = new Dog
    dog.canLearnSkill("浑水摸鱼")
  }
}

当然必须先定义好隐式方法及相关方法才能在后方的main方法中调用!!!main方法在前会爆红!!!这点和C语言中方法前后位置无所谓的情况不一样!!!

运行后:

狗狗能学会浑水摸鱼技能

Process finished with exit code 0

显然成功调用了其它类的方法。

隐式类

package com.aa.implicitDEMO

import java.io.File

import scala.io.Source

object implicit_Class {

  //隐式类
  implicit class ImpInt(temp1: Int) {
    //隐式类的方法
    def add(temp2: Int) = temp1 + temp2
  }

  implicit class FileLoad(file: File) {
    def read() = Source.fromFile(file.getPath).mkString
  }

}

object Test {
  //局部导包,_代表全部导入

  import com.aa.implicitDEMO.implicit_Class._

  def main(args: Array[String]): Unit = {
    //在当前作用域中寻找,将Int(1)作为变量的类同时具有add方法的类,如有,则执行
    println(1.add(2))

    //在当前作用域中寻找 将File 作为变量的类同时具有read的方法的类,如有,则执行
    println(new File("D:\\\\datasets\\\\scala\\\\1.txt")
      .read()) 
  }
}

注意事项

无歧义原则

scala> :quit

C:\\Users\\killer>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_241).
Type in expressions for evaluation. Or try :help.

scala> def m1(implicit x:Int) = x+1
m1: (implicit x: Int)Int

scala> implicit val a:Int =10086
a: Int = 10086

scala> implicit val b:Int =10010
b: Int = 10010

scala> m1	//作用域中有多个同类型的隐式变量,有歧义,为了保证安全性不能隐式转换
<console>:15: error: ambiguous implicit values:
 both value a of type => Int
 and value b of type => Int
 match expected type Int
       m1
       ^

scala>

显式操作先行原则

scala> def m1(implicit x:Int) =x+1
m1: (implicit x: Int)Int

scala> implicit val a:Int = 10086
a: Int = 10086

scala> m1(2)	//隐式变量不允许显式调用方法
res2: Int = 3

集中定义原

以上是关于Scala基础高阶函数隐式转换AKKA编程的主要内容,如果未能解决你的问题,请参考以下文章

Scala:基础知识04

Scala基础:隐式转换与隐式参数

Scala快速入门--异常处理泛型高阶函数隐式转换

大数据学习:Scala隐式转换和并发编程(DT大数据梦工厂)

第6节 Scala中的高阶函数:123

Scala函数编程和隐式转换