Scala基础高阶函数隐式转换AKKA编程
Posted 杀智勇双全杀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala基础高阶函数隐式转换AKKA编程相关的知识,希望对你有一定的参考价值。
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编程的主要内容,如果未能解决你的问题,请参考以下文章