scala(五) 函数式编程

2022-04-12 15:53:27 浏览数 (1)

  1. 面向对象编程 解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。 对象:用户; 行为:登录、连接jdbc、读取数据库 属性:用户名、密码 Scala语言是一个完全面向对象编程语言。万物皆对象
  2. 函数式编程 解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题 例如:请求->用户名、密码->连接jdbc->读取数据库 Scala语言是一个完全函数式编程语言。万物皆函数
  3. 在Scala中函数式编程和面向对象编程融合在一起了。

方法基本语法

语法:

scala语法scala语法

说明:

  1. def : 关键字,表示声明方法
  2. add:方法名,根据业务进行取名
  3. 参数名称在前 : 参数类型在后,多个参数用 , 分割
  4. 返回类型:若无返回类型 可定义为 Unit 或 无需定义
  5. 函数体:用于编写的业务逻辑的语句块

与java方法的区别:

  1. scala 可以将函数定义到任意位置,甚至在一个函数内定义一个函数。
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def sayHello(){
      print("hello")
    }
    sayHello // hello
}
  1. java 只能将方法定义在类中,不能定义在另一个方法中

方法的定义:

  1. 函数1:无参,无返回值
代码语言:javascript复制
  def sayHello(){
      print("hello")
  }
  1. 函数2:无产,有返回值
代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    
    println(sayHello) // hello
  }

  def sayHello():String={
      "hello"
  }
  1. 函数3:有参,无返回值
代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    
    sayHello("张三")
  }

  def sayHello(name:String){
      println(s"你好,$name 童鞋") // 你好,张三 童鞋
  }
  1. 函数4:有参,有返回值
代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    
    println(sum(10,20)) // 30
  }

  def sum(x:Int,y:Int):Int={
     x y
  }
  1. 函数5:多参,无返回值
代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    
    sum(10,20) // 30
  }

  def sum(x:Int,y:Int){
     println(x y) //30
  }
  1. 在scala中函数可以不用加 return 用来标识返回值,在scala中会以最后一个参数或表达式的结果作为返回值。

方法参数

可变参数 无论是 scala 还是 java 他们都支持 可变参数 java :使用 ... 来表示 scala:使用 * 来表示 案例:

代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    info("坤坤","唱","跳","Rap","篮球")
  }

  def info(name:String,hobby:String*):Unit={
     println(s"我叫$name,我的爱好有${hobby.toList}") // 我叫坤坤,我的爱好有List(唱, 跳, Rap, 篮球)
  }

可变参数底层实现都是一个数组或集合,所以直接遍历可变参。

代码语言:javascript复制
 def info(name:String,hobby:String*):Unit={
    for (h <- hobby){
      println(h)
    }
}
代码语言:javascript复制
唱
跳
Rap
篮球

使用 可变参数需要注意:

  1. 被定义成可变参数的参数,需要放在参数列表最后。
  2. 一个方法中只能有一个可变参数。
  3. 可变参数不能与带名参数一起使用(后面会讲)

带参默认值 在程序开发中,往往有这样的需求,当某些值为null或0时,我们需要判断,然后给它指定一个默认值。 如:不指定性别,就会使用默认的性别 'M'

代码语言:javascript复制
  def main(args: Array[String]){
    info("坤坤",60)
  }

  def info(name:String,age:Int,sex:Char='M'){
    println(s"姓名:$name,年龄:$age,性别:$sex") // 姓名:坤坤,年龄:60,性别:M
  }

我们手动指定一个性别 'F'

代码语言:javascript复制
  def main(args: Array[String]){
    info("坤坤",60,'F')
  }

  def info(name:String,age:Int,sex:Char='M'){
    println(s"姓名:$name,年龄:$age,性别:$sex") // 姓名:坤坤,年龄:60,性别:F
  }

至于语法:以这样的形式sex:Char='M'定义的参数,就是表示给参数一个默认值。

常见错误写法:

  • sex='M' :Char:我有时候也不会搞错,直到运行报错才反应过来。
  • sex='M':这种形式的写法也是错误的,必须指定类型。

带名参数 以上面的案例说明;第一个参数为姓名,第二个参数为年龄,第三个参数为性别,一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参。

info(age=60,sex='F',name="坤坤")

代码语言:javascript复制
  def main(args: Array[String]){
    info(age=60,sex='F',name="坤坤")
  }
  def info(name:String,age:Int,sex:Char='M'){
    println(s"姓名:$name,年龄:$age,性别:$sex")
  }
}
代码语言:javascript复制
姓名:坤坤,年龄:60,性别:F

方法至简原则(重点)

  1. return 可以省略,scala 会使用函数体的最后一行代码作为返回值
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String):String = {
      context
    }
    print(say("hello")) // hello
  }
  1. 如果函数体只有一行代码,可以省略花括号
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String):String = context
    print(say("hello")) //hello
}
  1. 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String)= context
    print(say("hello")) // hello
}
  1. 如果有 return,则不能省略返回值类型,必须指定。
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String):String = return context
    print(say("hello")) //hello
}
  1. 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String):Unit = return context
    print(say("hello")) // ()
}
  1. scala 如果期望时无返回值类型,可以省略等号
代码语言:javascript复制
def main(args: Array[String]): Unit = {
    def say(context:String) { print(context)}
    say("hello") // hello
}

7.如果参数无参,但是声明了参数列表,那么调用时,小括号,可以不加。

代码语言:javascript复制
def sayHello{print("hello")}
sayHello
  1. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
代码语言:javascript复制
定义函数:def sayHello{print("hello")}
掉用函数:必须省略小括号 sayHello
  1. 如果不关心名称,只关心逻辑处理,那么函数名称(def)可以省略。这种声明方式叫做 匿名函数(lambda 表达式)。
代码语言:javascript复制
()=>{print("hello")}

总结:虽然很多简化原则,但是不用去记,多敲就完了(我也记不住,没事的时候,回顾一下笔记,知道个大概就行了。

函数的定义

语法 val 函数名 =(参数名:类型,...) => {函数体}

代码语言:javascript复制
    // 计算两个数的和
    val sum=(x:Int,y:Int)=>{
      x y
    }

    println(sum(2,3)) // 5
  1. 并不需要写返回值,函数的返回值就是块表达式最终返回代码结果。
  2. 调用函数和调用方法是一样的。

函数的简化

  1. return 可以省略,scala 会使用函数体的最后一行代码作为返回值
代码语言:javascript复制
val sum=(x:Int,y:Int)=>{
    x y
}
  1. 如果函数体只有一行代码,可以省略花括号
代码语言:javascript复制
// 计算两个数的和
val sum=(x:Int,y:Int)=> x y
println(sum(2,3)) // 5
  1. 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
代码语言:javascript复制
// 计算两个数的和
val sum=(x:Int,y:Int)=> x y
println(sum(2,3)) // 5
  1. 如果有 return,则不能省略返回值类型,必须指定。
代码语言:javascript复制
函数中无法指定返回值,至少我试了好几次没成功
  1. 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
代码语言:javascript复制
上面的都不行,这个也自然没用了
  1. scala 如果期望时无返回值类型,可以省略等号
代码语言:javascript复制
没有等号,哈哈哈
  1. 如果参数无参,但是声明了参数列表,那么调用时,小括号,可以不加。
代码语言:javascript复制
    val sayHello=()=>{
      println("hello")
    }

不行,无法调用;函数就是一个对象,sayHello 这是这个函数的引用,并不是调用。

代码语言:javascript复制
sayHello 

打印一下该引用,输入的其实就内存地址。

代码语言:javascript复制
println(sayHello) // Demo02$$$Lambda$1/764977973@1fbc7afb

正确写法

代码语言:javascript复制
    sayHello()

函数与方法的区别

官方函数的定义

  1. 函数的定义:(x: Int) => x 1
  2. 方法的定义:def add(x: Int, y: Int): Int = x y println(add(1, 2)) // 3

函数与方法概念

  1. 方法就是函数,函数就是一个对象
代码语言:javascript复制
scala> val sum=(x:Int,y:Int)=>{x y}
sum: (Int, Int) => Int = $$Lambda$1032/841090268@778a1250 

$$Lambda$1032/841090268@778a1250  就是 sum 函数的内存地址。

方法存在方法区,对象存在堆内存中。

  1. 函数调用必须带上(),否则无法调用
  2. 方法写在类中,方法是可以被重载,函数无法被重载 方法:
代码语言:javascript复制
  def main(args:Array[String]):Unit={
    println(sum(1, 2)) // 3
    println(sum(1, 2,4)) // 7
  }

  def sum(x:Int,y:Int):Int={
    x y
  }
  def sum(x:Int,y:Int,z:Int):Int={
    x y z
  }

但是在方法中不能重载

代码语言:javascript复制
  def main(args:Array[String]):Unit={
    def sum(x:Int,y:Int):Int={
      x y
    }
    def sum(x:Int,y:Int,z:Int):Int={
      x y z
    }

    println(sum(1, 2))
    println(sum(1, 2,4))
    
  }
代码语言:javascript复制
Error:(7, 9) method sum is defined twice;
  the conflicting method sum was defined at line 4:9
    def sum(x:Int,y:Int,z:Int):Int={

对于函数,无论是在类中,还在方法中,都是不行的,因为函数需要定义变量名;变量名不能重复

代码语言:javascript复制
 val sum=(x:Int,y:Int)
 val sum=(x:Int,y:Int,z:Int)
  1. 方法转函数:方法名 _
代码语言:javascript复制
  def main(args:Array[String]):Unit={

    val a=sum _
    println(a(1,2,3))

  }
  
  def sum(x:Int,y:Int,z:Int):Int={
    x y z
  }

函数的另一种写法

FunctionNumNum 表示0-22个数字。最多只有23种 Function0:表示0个参数 Function1:表示1个参数 Function10:表示10个参数 Function22:表示22个参数

依旧拿 sum为例

代码语言:javascript复制
val sum=(x:Int,y:Int)=>{
   x y
}

它有两个参数;所以使用 Function2

代码语言:javascript复制
    val sum2=new Function2[Int,Int,Int] {
      override def apply(v1: Int, v2: Int) = v1 v2
    }

Function2[Int,Int,Int] : 第一个Int:表示第一个参数的类型 第二个Int:表示第二个参数的类型 第三个Int:表示返回值的类型

需要重写它的 apply 方法;在内部做运行

代码语言:javascript复制
override def apply(v1: Int, v2: Int)

如果代码很多,是可以指定{}将代码包起来

代码语言:javascript复制
override def apply(v1: Int, v2: Int) = {
   v1 v2
}

对比一下原来的形式

代码语言:javascript复制
    // 计算两个数的和
    val sum1=(x:Int,y:Int)=>{x y}

    //  Function* 的形式
    val sum2=new Function2[Int,Int,Int] {
      override def apply(v1: Int, v2: Int) = {
        v1 v2
      }
    }

调用

代码语言:javascript复制
    println(s"sum1=${sum1(2,3)}") // 5
    println(s"sum2=${sum2(4,5)}") // 9

结语:

对于scala目前处于学习阶段,以上内容都是我学习的总结,至于内容质量,仁者见仁,不好的大家提出来,相互进步,能帮助你的,我也很高兴。

0 人点赞