【Scala】Scala核心知识点总结之函数式编程

  • 在之前Java课程的学习中,我们一直学习的就是面向对象编程,所以解决问题都是按照面向对象的方式来处理的。比如用户登陆等业务功能,但是接下来,我们会学习函数式编程,采用函数式编程的思路来解决问题。scala编程语言将函数式编程和面向对象编程完美地融合在一起了。
  • 面向对象编程
    分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题
  • 函数式编程
    将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的功能按照指定的步骤,解决问题。

⌘ 基础函数编程

– 基本语法

[修饰符] def 函数名 ( 参数列表 ) [:返回值类型] = {
    函数体
}
  • 一般是返回值类型不写
private def test( s : String ) : Unit = {
    println(s)
}

– 函数&方法

  • 方法概念来自于Java,函数概念来自于Scala
  • Scala又是完全函数式编程语言。所以方法也都可以看做函数,可以赋值给一个变量。一般情况下,可以理解为方法是组成类的一部分,函数则是一个完整的对象。
    Scala中的方法和函数从语法概念上来讲,一般不好区分,所以简单的理解就是:方法也是函数。只不过类中声明的函数称之为方法,其他场合声明的就是函数了。类中的方法是有重载和重写的。而函数可就没有重载和重写的概念了,但是函数可以嵌套声明使用,在类中的方法不能嵌套声明。

– 函数定义

  1. 无参,无返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun1(): Unit = {
            println("函数体")
        }
        fun1()
    }
}
  1. 无参,有返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun2(): String = {
            "zhangsan"
        }
        println( fun2() )
    }
}
  1. 有参,无返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun3( name:String ): Unit = {
            println( name )
        }
        fun3("zhangsan")
    }
}
  1. 有参,有返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun4(name:String): String = {
            "Hello " + name
        }
        println( fun4("zhangsan") )
    }
}
  1. 多参,无返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun5(hello:String, name:String): Unit = {
            println( hello + " " + name )
        }
        fun5("Hello", "zhangsan")
    }
}
  1. 多参,有返回值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun6(hello:String, name:String): String = {
            hello + " " + name
        }
        println( fun6("Hello", "zhangsan"))
    }
}

– 函数参数

— 函数参数的个数

  • 最大个数为22,声明的时候可以超过22,但是如果超过22个将函数作为对象赋值给变量时会报错

— 可变参数

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun7(names:String*): Unit = {
            println(names)
        }
        fun7()
        fun7( "zhangsan" )
        fun7( "zhangsan", "lisi" )
    }
}
  • 可变参数不能放置在参数列表的前面,一般放置在参数列表的最后
oobject ScalaFunction {
    def main(args: Array[String]): Unit = {
        // Error
        //def fun77(names:String*, name:String): Unit = {
            

        //}
        def fun777( name:String, names:String* ): Unit = {
            println( name )
            println( names )
        }
    }

}

— 参数默认值,

  • Scala中函数的参数使用val声明,无法进行修改,Scala提供了参数默认值的语法来解决参数默认值的问题。
  • 参数默认(初始)值: 参数声明时进行初始化
  • 如果函数存在默认值的参数,调用时可以不用传递,不传参数便会使用默认值
  • 如果调用函数时,提供了参数值,那么参数默认值不起作用,被覆盖了
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun8( name:String, password:String = "000000" ): Unit = {
            println( name + "," + password )
        }
        fun8("zhangsan", "123123")
        fun8("zhangsan")
    }
}

— 带名参数

  • 带名参数 : 传递参数时添加参数名,明确指定参数赋值
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun9( password:String = "000000", name:String ): Unit = {
            println( name + "," + password )
        }
        fun9("123123", "zhangsan" )
        fun9(name="zhangsan")
    }
}

– 函数至简原则

  • 所谓的至简原则,其实就是Scala的作者为了开发人员能够大幅度提高开发效率。通过编译器的动态判定功能,帮助我们将函数声明中能简化的地方全部都进行了简化。也就是说将函数声明中那些能省的地方全部都省掉。所以这里的至简原则,简单来说就是:编译器可以自动推断的场合,能省则省。

— 省略return关键字

  • 当函数需要返回值时,将函数体中最后一行执行的代码作为返回结果,所以可以省略return关键字
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun1(): String = {
            return "zhangsan"
        }
        def fun11(): String = {
            "zhangsan"
        }
    }
}

— 省略花括号

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun2(): String = "zhangsan"
    }
}

— 省略返回值类型

  • 不一定能省,调用的时候()可加可不加

  • 如果编译器可以推断出函数的返回值类型,那么返回值类型可以省略

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun3() = "zhangsan"//编译器会自动推断,要求会看懂!
    }
}

— 省略参数列表

  • 调用时()也不能加

  • 如果函数没有提供参数,那么声明时,小括号也可以省略, 省略后调用时,也不能使用小括号

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun4 = "zhangsan"
    }
}

— 省略等号

  • 如果函数体中有明确的return语句,那么返回值类型不能省略
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun5(): String = {
            return "zhangsan"
        }
        println(fun5())
    }
}
  • 如果函数体返回值类型明确为Unit, 那么函数体中即使有return关键字也不起作用,白写
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun5(): Unit = {
            return "zhangsan"
        }
        println(fun5())
    }
}
  • 如果函数体返回值类型声明为Unit, 但是又想省略,那么此时就必须连同等号一起省略,省略了返回值则肯定为Unit(无返回值),这种函数的声明方式称之为过程函数, 不省略花括号
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun5() {
            return "zhangsan"
        }
        println(fun5())
    }
}
object Scala04_Function3 {
  def main(args: Array[String]): Unit = {
    test() //xxxxx
    test //xxxxx
    test _
    test1 //yyyyy
    //test1()方法定义时已省略括号,则调用时不能加()
    test1 _
  }
  def test(): Unit = {
    println("xxxxx")
  }
  def test1 {
    println("yyyyy")
  }

}

— 省略名称和关键字

  • 当只关心代码逻辑,不关心函数名称时,函数名和def关键字可以省略,将没有名称和def关键字的函数称之为匿名函数
    匿名函数规则:(参数列表) => { 代码逻辑 }
  • =>常表示方法
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        () =>  println("zhangsan")
    }
}
// 声明匿名函数
val a = (i:String) => println(i)

// 调用
a("匿名函数被调用")//匿名函数被调用

⌘ 高阶函数编程

  • 所谓的高阶函数,其实就是将函数当成一个类型来使用,而不是当成特定的语法结构。

– 函数作为值

  • 将函数赋值给变量,那么这个变量其实就是函数,可以调用
  • 函数如果没有参数列表,那么调用时可以省略小括号
  • 如果此时希望将函数不执行,而是当成一个整体赋值给变量,那么需要使用下划线
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun1(): String = {
            "zhangsan"
        }
        val a = fun1
        val b = fun1 _
        println(a)//不加_,编译器会以为执行这个函数并赋值,输出zhangsan!!!
        println(b)
    }
}

重要!!!

val ff1 = test11 _等于
val ff1:(Int)=>Int =test1 等于
val ff1:Int=>Int =test1 如果只有一个输入参数,则()可省略

(Int)=>Int即表示函数类型,标明函数的输入输出类型

val b = fun1 _等于 
val b :()=>String= fun1 

– 函数作为参数

  • 函数可以作为参数传递给其他的函数
  • 函数名( 参数名1 :函数类型)
  • 将函数作为参数使用时,一般不关心函数的名称,所以一般使用匿名函数
  • 函数名((参数列表) => { 代码逻辑 })
def fun2( i:Int ): Int = {
    i * 2
}
//( f : Int => Int )表示传一个输入为Int输出也为Int的函数
def fun22( f : Int => Int ): Int = {
    f(10)
}
//不用加下划线是fun22指定了参数为函数,能确定是函数了,加下划线也是为了确定是函数
println(fun22(fun2))//20

即:
def fun2( i:Int ): Int = {
  i * 2
}
def fun22( f : Int => Int ): Int = {
  f(10)
}
//将函数赋值给不可变变量
val f222 = fun2 _
println(fun22(f222))//20
  • ( f : (Int) => Int )表示传一个输入为Int输出也为Int的函数,就是一个函数的类型,就像String一样,如果只有一个输入参数,则()可省略,所以 Int => Int
// TODO 至简原则
//val result = test2( (i:Int) => {i * 2} )
// 如果逻辑代码只有一行,大括号可以省略
//val result = test2( (i:Int) => i * 2 )
// 如果匿名函数的参数类型可以推断出来,那么类型可以省略
//val result = test2( (i) => i * 2 )
// 如果匿名函数的参数列表只有一个或没有,那么小括号可以省略
//val result = test2( i => i * 2 )
// 如果匿名函数中的参数在逻辑代码中只使用了一次,那么,参数和=>可以省略
//  使用下划线代替参数
val result = test2(  _ * 2 )
println(result)

– 函数作为返回值

  • 函数可以作为函数的返回值返回,当函数作为返回值使用时,一般会使用嵌套函数
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun3( i:Int ): Int = {
            i * 2
        }
        def fun33( ) = {
            fun3 _
        }
        println(fun33()(10))//20,fun33()返回fun3 _
    }
}

 def main(args: Array[String]): Unit = {
    def test(i:Int)={
      def sum(j:Int)={
        i+j
      }
      sum _
    }
    println(test(10)(20))//30,test(10)返回sum _,且闭包使i成为sum方法的参数
  }
}
  • 问: test方法都执行完失效了,i已经回收了,为什么还能用在sum方法中能用?
  • 函数在使用外部变量时,如果外部变量失效时,会将这个变量包含到当前的函数内部,形成闭合的使用效果,改变变量的生命周期,讲这种操作称之为闭包。

– 匿名函数

  • 将两数作为参数使用时,一般不关心函数的名称,所以一般使用匿名函数
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun4( f:Int => Int ): Int = {
            f(10)
        }
        println(fun4((x:Int)=>{x * 20}))
        //如果匿名函数的参数类型可以推断出来,那么类型可以省略
        println(fun4((x)=>{x * 20}))
        //如果逻辑代码只有一行,大括号可以省略,重要!!多行复杂不能省而且是{}
        println(fun4((x)=>x * 20))
        //如果匿名函数的参数列表只有一个或没有, 那么小括号可以省略
        println(fun4(x=>x * 20))
        //如果匿名函数中的参数在逻辑代码中只使用了一次,那么,
        //参数和=>可以省略,使用下划线代替参数
        println(fun4(_ * 20))
    }
}
  • 如果匿名函数的参数在代码中只使用了一次,那么,参数和=>可以省略,使用下划线代替参数

  • 原则:编译时能推断出来的东西才能省,不然就不是静态语言了

  • 经典练习:
    如果设计一个用于过滤的函数,你会如何做?
    要求:对传递的包含多个单词的字符串参数,根据指定的规则对word进行过滤
    例子:“hello world scala spark” => 首写字母为s => “scala, spark”

def +filter(s:String,fun:(String)=>Boolean):String={
  val words :Array[String]= s.split(" ")
  var result=""
  for(word <- words){
    if(fun(word))
      result += "," + word
  }
  result.substring(2)
}

filter("hello world scala spark",(word:String)=>{word.startsWith("s")})
filter("hello world scala spark",_.startsWith("s"))

– 闭包

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun5() = {
            val i = 20
            def fun55() = {
                i * 2
            }
            fun55 _
        }
        fun5()()
    }
}
  • 内部函数用到了外部变量,会有闭包效果。匿名函数肯定为闭包,将函数赋值给变量使用也是闭包,嵌套函数的使用(使用内部函数)会有闭包。

  • 反编译后发现编译器重新声明了内部函数的参数,将外部变量在内部函数参数列表进行重新声明使用。在早起的Scala版本(2.11),闭包是会编译成匿名函数类

  • 在早些版本:闭包会被编译为匿名函数类,如果使用外部变量,会将外部变量作为类的属性。

  • 在早些版本:即使没有使用外部的变量,也会有闭包的效果,只是没有包含外部变量。
    在spark怎么判断闭包:判断类名中是否有匿名内部类

  • 没有使用外部变量还能称之为闭包吗?

  • 也是有可能的,只要改变了变量的生命周期就是闭包,编译后会产生一个匿名内部类,后面编译器升级了就没有用匿名内部类的方式了。
    在这里插入图片描述

– 函数柯里化

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun6(i:Int)(j:Int) = {
            i * j
        } 
    }
}


def test2(i:Int)(j:Int)(f:(Int,Int)=>Int):Int={
    f(i,j)
  }
  println(test2(1)(2)(_+_))
}

看到两个独立的括号就考虑是函数柯里化

– 控制抽象

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun7(op: => Unit) = {
            op
        }
        fun7{
            println("xx")
        }
    }
}
  • op: => Unit没有(),代码也可以当成参数传进来,此处传的println(“xx”)代码。控制抽象可以用来封装固定代码,传业务代码,实现框架时用的非常多。

– 递归函数

object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun8(j:Int):Int = {
            if ( j <= 1 ) {
                1
            } else {
                j * fun8(j-1)
            }
        }
        println(fun8(5))
    }
}

1.方法执行过程中调用自身
2.存在可以跳出递归的逻辑,可能会出现Stack0verflowError(栈溢出)
3.方法调用时,传递的参数之间应该存在规律。
4.尾递归压一个弹一个,编译会优化成while循环,但是如果压栈速度远大于出栈速度也会出现OOM
在这里插入图片描述
在这里插入图片描述

– 惰性函数

  • 当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,用上了该函数时才会执行。这种函数我们称之为惰性函数。
object ScalaFunction {
    def main(args: Array[String]): Unit = {
        def fun9(): String = {
            println("function...")
            "zhangsan"
        }
        lazy val a = fun9()
        println("----------")
        println(a)
    }
}

在这里插入图片描述
在这里插入图片描述

  • 函数到底是什么?实现原理?

  • Scala都是对象,函数也是对象,类也是函数(构造函数)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值