Scala 函数式编程的重要部分总结

一、至简原则
 
因为所有的表达式都有返回值(最后一行当返回值)
1)函数可以自动将最后一行代码的结果作为函数的返回值。
可以省略return.能推断最后一行代码作为返回值
  Scala 函数式编程的重要部分总结
2)如果返回值能确定,返回值的类型其实也可以确定。那么返回值类型就可以省略
场景1:  Scala 函数式编程的重要部分总结String 是自动提示的
场景2:自动推断返回值类型。因为Scala是强类型语言,在运行之前就得知道返回值类型。
  Scala 函数式编程的重要部分总结
 
要么返回10 就是int
要么就不返回是Unit
就得找通用的AnyVal
  Scala 函数式编程的重要部分总结
 
 
3)花括号可以省略。如果函数逻辑代码只有一行,那么大括号可以省略
  Scala 函数式编程的重要部分总结
 
4)如果参数列表没有参数。小括号可以省略
如果函数声明时省略了参数列表,那么在 调用的时候也不能加参数列表
  Scala 函数式编程的重要部分总结
在简化过程  val  和def  就体现出功能了。靠关键字就可以区分函数和变量了
 
5)如果函数的参数列表中没有参数,那么在调用该方法的时候可以不加()
直接  fun10 就是调用的意思  和使用fun10 () 的结果一样
  Scala 函数式编程的重要部分总结
 
6)Unit  和return 一起使用的时候没有报错  但是结果是()。代表不需要返回值
如果函数明确没有返回值,那么函数体中的return 关键字不起作用了
 
函数体中如果有return ,那么返回值类型是不能省略的
不想声明返回值类型,又不想让return 关键字起作用。此时可以省略等号。
Scala 中声明函数没有返回值,代表只有输入没有输出,这样的函数 叫做过程函数,因为没有结果
可以连通返回类型和等号一起省略。
7) 当不关心函数的名称的时候(只关心逻辑的时候),函数名和def 都可以省略
  Scala 函数式编程的重要部分总结
去掉def 和名字
  Scala 函数式编程的重要部分总结
相当于
  Scala 函数式编程的重要部分总结
这样有问题,运行会报错
这时候告诉Scala是一个参数列表,不是Unit 的一个对象,此时需要加一个箭头
 
箭头表示左边是参数,右边是函数体
():Unit 的一个对象叫做小括号
这样的函数叫做匿名函数。JDK1.8借鉴的就是
匿名函数的方式:()=>{}
=> 左边是参数列表 
=>右边是方法体
怎么调用这个匿名函数???没名字怎么调用?
想要调用就得把它给别人,需要赋值给其他的变量调用。
此时赋值给f 变量
  Scala 函数式编程的重要部分总结此时把函数给一个变量
  Scala 函数式编程的重要部分总结和上面的类比
调用 f()   f是名字,括号是参数列表
运行结果
  Scala 函数式编程的重要部分总结
 
 
 
 
二、函数作为变量:
在函数传递给变量过程的注意事项:
1、函数结果传递给变量(函数的结果赋值给变量)
此时可以查看到f变量的类型是String,和函数fun10的返回值类型刚好一样 
  Scala 函数式编程的重要部分总结
上面fun10 和fun10()  赋值给f 结果是一样的
 
2、将函数整体赋值给变量(两种方式)
1)将函数整体赋值给变量的 第一种方式
  Scala 函数式编程的重要部分总结
上面可以查看到变量的ff 类型已经变成了函数类型 ()=>String, 代表函数整体已经赋值给了ff 变量
val 变量=函数名 _
此时ff 已经变成了函数,注意在调用ff 函数的时候,是需要在ff 后面加上小括号的  
调用函数的语法: ff()
调用ff:注意调用函数( 变量 )的时候一定要加()否则 打印结果如下
当变量后没加小括号的打印结果:
  Scala 函数式编程的重要部分总结
 
对比调用原始函数,原始函数如果没有参数,可以直接 函数名    Scala 函数式编程的重要部分总结  进行调用,也可以参数名小括号   Scala 函数式编程的重要部分总结 进行调用
然而在函数通过变量来调用的时候,变量(函数)后是需要加小括号的。
当变量后加了小括号后的打印结果:
  Scala 函数式编程的重要部分总结
 
2)将函数整体赋值给变量的 第二种方式
  Scala 函数式编程的重要部分总结
小案例:
  Scala 函数式编程的重要部分总结
 
三、函数作为参数
函数式编程普通函数作为参数的总结
 
object Scala_Function1 {
def main ( args: Array [ String ]) : Unit = {
 
// TODO: 函数作为参数,传递给其他的函数 ( 非匿名函数 )
// 封装好的 逻辑 給函 可以 行另外一段 逻辑 。把 逻辑传过来 ,在函 这个逻辑
def f1 ( i: Int ) : Int = {
i * 2
}
 
def tes1 ( f: Int => Int ) : Int = {
f ( 10 )
}
 
val f = f1 _
println ( tes1 ( f ))
/*
整体的 逻辑 如下(非匿名函 为参数 ):
为参数 首先需要 两个
1 )第一 是普通的函
自己定 逻辑
例如 f1 int 型的 出一 int 型的
2 )第二 。需要把自己定义了逻辑的函数 传递进来 。需要把 int int 的函 数传递进来
要用 这个参数 ,那 这个 参数
 
参数 是一
首先 参数 名 比如 f
型是 入一 Int 型的 参数 出一 Int 型的
 
3) 把普通函 为变 使用,然后把 量再 传递给 第二
 
*/
 
}
 
}
 

函数式编程匿名函数作为参数的总结
一、匿名函数作为参数用的非常多
object Scala_Function1 {
def main ( args: Array [ String ]) : Unit = {
 
// TODO: 函数作为参数,传递给其他的函数 ( 非匿名函数 )
// 封装好的 逻辑 給函 可以 行另外一段 逻辑 。把 逻辑传过来 ,在函 这个逻辑
def f1 ( i: Int ) : Int = {
i * 2
}
 
def tes1 ( f: Int => Int ) : Int = {
f ( 10 )
}
 
 
val f = f1 _
println ( tes1 ( f ))
 
// TODO: 将函数 作为参数的时候一般是不关心函数的名称的,所以一般使用匿名函数
// TODO: 匿名函数使用规则:(参数列表) =>{ 代码逻辑 }
/*
第二 中的 f 就是匿名函
第二 需要的就是一 入一 Int 型的 出一 Int 型的
test 中的匿名函 规则 就是按照 test 参数 规则来
*/
println ( tes1 (( i: Int ) => i * i ))
}
}
 
 
 
二、匿名函数使用的时候使用至简原则
 
println ( test (( i: Int ) => i * 2 ))
// 省略 参数类 型,因 能自
println ( test (( i ) => i * 2 ))
// 省略小括 ,因 只有一 个参数
println ( test ( i => i * 2 ))
// 省略 i=> 这个 整体,因 匿名函 参数 只使用了一次
// 但是 会报红 这时 候需要使用下 划线 代替 i
//println(test(i * 2))
println ( test ( _ * 2 ))
 
// TODO: 至简原则
/*
1 :如果 逻辑 只有一行,那 花括 可以不要
2 :如果匿名函 参数类 型可以推 ,那 么类 型可以省略( )
test 中明确要求 入的函 参数 Int 型,所以匿名函 型是可以推 的。
且只能是 Int
3 :如果匿名函 参数 列表只有一 或者 有,小括 是可以省略的
4: 如果匿名函 中的 参数 逻辑 中只使用了一次,那 么 参数列表 => 这个 整体可以省略。
参数列表中一个或者多个参数,只要使用了一次。  整个参数列表=>  这个整体可以直接去掉
使用下 划线 代替 参数
 
*/
 
三、匿名函数至简原则(匿名函数中两个参数举例)
 
 
object Scala_Function2 {
def main ( args: Array [ String ]) : Unit = {
 
def calAnalysis ( f: ( Int, Int ) => Int ) : Int = {
val boyCount = 20 ;
val grilCount = 30 ;
f ( boyCount, grilCount )
}
 
// 使用匿名函
println ( calAnalysis (( x: Int, y: Int ) => {
x + y
}))
//
// 逻辑 就一行。省略大括
println ( calAnalysis (( x: Int, y: Int ) => x + y ))
// 参数数 型能 参数 型可以省略
println ( calAnalysis (( x, y ) => x + y ))
// 匿名函 中的 参数 只使用一次, 参数 列表和 => 可以省略。但是 会报红
// 可以使用下 划线 代替 参数,第一个下划线代替第一个参数,第二个下划线代替第二个参数
println ( calAnalysis ( _+ _ ))
}
 
四、函数至简原则(函数中两个参数,其中一个任意类型,另一个函数类型 )
// 外部函数的参数中有值类型,也有函数类型
def transform ( num: Int, f: ( Int ) => Int ) : Int = {
f ( num )
}
println ( transform ( 10 , ( num: Int ) => {
num * 2
}))
// 简化版本,对匿名函数的简化
println ( transform ( 10 , _ * 2 ))
 
特点是外部函数中的内部函数f可以对内部参数num进行操作
如何操作的逻辑是外部传递进来的。
针对上面,外部需要传递两个参数,一个是数值,一个是函数的操作逻辑(匿名函数,又可以做简化)

函数式编程 之函数作为返回值
一般的场合是将内部的逻辑给外部使用,改变了函数的作用域
如果将函数返回,那么一般省略返回值类型,因为能自动推断
object Scala_Function3 {
def main ( args: Array [ String ]) : Unit = {
// TODO: 返回
def test ( i: Int ) : Int = {
i * 2
}
def fun () = {
test _
}
/*
第一种方式:需要一个中间变量。函数的返回结果传递给变量,
此时变量成为了函数
*/
val f = fun () // 当前 fun 函数的执行结果为函数,此时 f 就是函数
println ( f ( 10 ))
/*
第二种方式:
fun() 函数的返回结果就是一个函数,函数后面可以直接加括号,括号里面加参数
这样就少了中间的变量。
*/
// TODO: 注意下面的 fun() 的小括 不能省略, 会产 生歧
// 能省则省的前提是能推断
println ( fun ()( 10 ))
}
}
执行结果是:20
 
嵌套函数:函数里面有函数,将里面的函数返回
 
// TODO: 返回 使用 ,一般 使用嵌套函
// 嵌套函数就是函数里面有函数,将里面的函数返回
// 场景一:使用下划线返回函数整体
def fun1 () = { // 此时的返回值类型省略了
def test ( i: Int ) : Int = {
i * 3
}
test _ // 最后一行作为返回值
}
 
println ( fun1 ()( 10 )) // 结果是 30
// 场景二:不使用下划线返回函数整体
// 类比和之前的函数赋值给变量。需要告诉函数明确返回一个类型的函数
def fun2 () : ( Int ) =>Int = { // 此时明确返回值类型
def test ( i: Int ) : Int = {
i * 3
}
test // 因为能推断返回值的类型所以可以去掉下划线
}
println ( fun2 ()( 10 )) // 返回结果是 30
 
// TODO: 返回 进阶 版(嵌套函
def fun3 ( i: Int ) = {
def test ( j: Int ) : Int = {
i + j
}
test _
}
/*
fun3(3) 的返回结果是 test, 然后执行 test(5) 。会执行 3+5
*/
println ( fun3 ( 3 )( 5 )) // 返回值是 8
 
 

将函数作为返回值再进阶(有难度):
1、需求:  Scala 函数式编程的重要部分总结
以前是:  Scala 函数式编程的重要部分总结
为了降低耦合性。某些情况 参数列表需要分开。
此时就需要多个函数嵌套,一个函数的执行结果还是函数,所以可以一直调用。
注意不要去写返回值类型,让编译器去自动推断,因为自己写的麻烦。
图片:
  Scala 函数式编程的重要部分总结
 
/*
这道题的大体逻辑(这道题有点乱,所以需要一步步分析):
1 、先分析结果,是执行函数返回一个函数,执行一个函数再返回一个函数。
2 、针对返回的最后一个函数 test()
需要我们自己去定义一个逻辑,定义之后传递给 test, 作为 test 的参数
3 test() 需要这样的函数: 传递两个 Int 类型的参数,并且返回 Int 的值 :    Scala 函数式编程的重要部分总结
test() 的功能是对 oper() mid() 的两个参数做计算,也就是:     Scala 函数式编程的重要部分总结
做什么样的计算,怎么计算。这个逻辑是需要将两个参数进行相加。(此时就用到了匿名函数):  Scala 函数式编程的重要部分总结
4 、这个逻辑是对两个参数做相加。( _+_ )是对 两个参数分别进行相加
*/
关于下滑线使用的注意点:以“ ( _ + _ ) ”举例
当使用有两个参数的匿名函数的时候,要注意在简化的过程中,
注意参数列表中参数的先后顺序,需要与执行逻辑中的参数的使用顺序保持一致
通俗讲就是 第一个下划线代表第一个参数,第二个下划线代表第二个参数
比如:  Scala 函数式编程的重要部分总结这样的逻辑,就不能使用两个下划线分别代替参数了

 
函数式编程小练习
如果设计一个用于过滤的函数,你会如何做?
要求:对传递的包含多个单词的字符串参数,根据指定的规则对word进行过滤
例子:"hello world scala spark" => 首写字母为s => "scala, spark"  
第一种方法:使用了循环守卫,把满足条件的for 循环结果(集合)赋值给一个变量
  Scala 函数式编程的重要部分总结
代码:
def filter(s: String)(f: (String) => Boolean) = {
  //对字符串按照空格切分,拿到多个单词
  val words = s.split(" ") //注意双引号中加空格,别手误没加
  //在这里拼接字符串,用于结果返回
  //for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。
  //循环守卫,把满足条件放到for 循环的列表中,表示只有满足条件的才走循环
  val result =
  for (word <- words if (f(word))) yield {
    word
  }
  result //最后一行作为返回值,返回满足条件的结果
}
//至简原则
val array = filter("hello word scala spark")(_.startsWith("s"))
for (a <- array) {
  println(a)
}
 
 
打印结果:  Scala 函数式编程的重要部分总结
 
第二种方法:传统的方式(没有上面的花里胡哨)
  Scala 函数式编程的重要部分总结
 
代码:
def filter(s: String)(f : (String) => Boolean) = {
  //对字符串按照空格切分,拿到多个单词
  val words = s.split(" ")//注意双引号中加空格,别手误没加
  //在这里拼接字符串,用于结果返回
  val ss = new StringBuilder()
  for (word <- words) {
    if (f(word)) { //如果f函数操作得到的为true ,这个单词就留着
      ss.append(word + ",")
    }
  }
  ss.toString()
}
 
//至简原则
val newString = filter("hello word scala spark")(_.startsWith("s"))
println(newString)
打印结果:
  Scala 函数式编程的重要部分总结