二、Scala的方法与函数

97 阅读3分钟

零、匿名方法的简化逻辑

val ints = Array[Int](0, 2, 3, 4, 5)
ints.foreach(i=>{print(i)})
ints.foreach(i=>print(i))// 如果方法中只有一行语句,大括号可以省略
ints.foreach(print(_)) //如果参数只使用类一次,参数可以省略,使用_代替
ints.foreach(print)// 如果内部方法中只使用了一个参数,内部方法可以只写方法名

ints.foreach(i=>{
  if(i>10){
    print(i)
  }else{
    print(i+1)
  }
})
ints.foreach(i=>if(i>10){print(i)}else{print(i+1)})// 由于只有一行,所以可以省区大括号
ints.foreach(i=>if(i>10){print(i)}else{print(i+1)})//由于i参数不止使用一次,所以不能使用_代替

一、方法的定义

def 方法名(带有类型的参数):返回值类型={

方法内容

}

image.png

注意点:

  1. 方法的定义使用def
  2. 可以定义传入的参数,要制定参数的类型
  3. 方法的返回值类型一般情况下可写可不写,不写的时候可以自动推断。但是在递归方法中或者方法的返回值是函数类型的时候不能省略,方法中使用return的时候不能省略,可以查看下方代码解释👇
  4. 方法有返回值的时候,可以写return,也可以不写。不写的时候会把方法的最后一行当作返回值;写了return,必须要写方法的返回值类型,可以查看下方代码解释👇
  5. 如果方法可以一行搞定,可以将{}省略 ,可以查看下方代码解释👇
  6. scala规定方法传过来的参数是val的,不是var;并且方法中的参数不能使用var和val再修饰
  7. 如果去掉方法体前边的等号,那么默认这个方法的返回值是Unit,上述3情况下也可以不用写返回值类型,因为只能是Unit返回值类型。并且注定了不会有返回值,即使你指定返回string类型的数据,也无法返回出去 可以查看下方代码解释👇
//代码解释上述注意点3:递归方法不写返回值类型会报错
def fun3(age:Int):Unit ={
  val agex=age+1
  if(age<10){
    fun3(agex)
  }else{
    print(age)
  }
}
//代码解释上述注意点4:带有return的时候,方法必须写明返回值类型
def fun1(name:String,sex:Int):String={
  return "haha"
}
//代码解释上述注意点5
def fun1(name:String,sex:Int):String = return "haha"
def fun1(name:String,sex:Int):String= return "haha"
def fun2(name:String,sex:Int) =  print(s"$name is haha")
//代码解释上述注意点7 方法体前边不使用=,就认定返回值为Unit类型,也就是无数据返回,即使使用了return,也无法返回数据
def fun4(name:String){
  return "jaja"
}

二、方法与函数

其实方法就是函数,函数就是方法,非要区分的话,可以按照下边的说法

方法:类似于Java中对方法的认知,规规矩矩的按照规则写出的方法,生下来就是被调用执行的。

def fun1(name:String,sex:Int):String={
  return "haha"
}

函数:可以被赋值给一个变量,也可以说就是匿名函数

//定义一个函数
val f1=(x:Int,y:String)=>{print(s"$x is y")}
//使用函数的变量调用函数
f1(1,"haha")

在函数式编程中,函数是最牛的,他可以像任何其他数据类型一样被传递和操作,函数也可以在方法中传递。 我自己阐述一遍:在函数式编程中,函数可以像一个数据(比如1,“abc”,3等等)一样被赋值给变量或者被传递给一个方法。

三、递归方法

递归方法如果有返回值,一定要写明返回值类型;如果没有返回值,也要写明返回值类型,除非连=一起不写。

def fun3(age:Int):Unit ={
  val agex=age+1
  if(age<10){
    fun3(agex)
  }else{
    print(age)
  }
}

四、参数有默认值的方法

如果有多个参数,含有默认值的参数在第一个,那么调用的时候又不想覆盖默认值参数,那么就需要指定参数名称。

def fun4(name:String="hahan",b:Int){
  print(s"$b is name")
}
def fun5(name:String="hahan"){
  print(s" is name")
}
def fun6(name:String="hahan",age:Int=6){
  print(s" is name")
}
fun4("1",2) //第二个参数没有默认值
fun4(b=0) //不覆盖第一个参数,调用的时候需要指定第二个参数的名字
fun5()//不覆盖默认值
fun6(age=0) //只想覆盖第二个参数的默认值,调用的时候需要指定参数名称

五、可变参数的方法

在参数后边添加 * 号,说明该类型的参数可以写多个

def funSeq(s:String*)={
  val s1: Seq[String] = s
  s.foreach(e=>{print(e)})
  //等同于
  s.foreach(e=>print(e)) //如果方法体中只有一行代码,可以省略大括号
  //等同于
  s.foreach(print(_)) // 当匿名函数中只有一个参数的时候,并且这个参数只在一个地方用的时候,可以用下划线表示
  //等同于
  s.foreach(print)//当方法体中的方法中只用到一个参数的时候,下划线也可以省略
}

funSeq("a","b","c")

六、匿名函数

匿名函数可以作为一个方法的参数;匿名函数可以使用在方法的内部

三种写法

//第一种写法:后边的匿名函数赋值给了funn,类似于一种映射,匿名函数被映射给了funn函数变量
def funn:(Int,Int)=>Int=(a:Int,b:Int)=>{
  a+b
}
funn(2,3)
//第二种写法:在方法中使用匿名方法
def funSeq(s:String*)={
  val s1: Seq[String] = s
  s.foreach(e=>{print(e)}) //匿名函数放到了foreach方法中当作参数使用
}
//第三种方式:把方法赋值给变量
val xxx:(String,Int)=>Int=(s:String,i:Int)=>{
  0
}

(Int,Int)=>Int 这段代码就是一个匿名方法的类型,就像Int,String一样表示一个变量的类型,这里是方法的类型,(Int,Int)中表示匿名函数有两个参数,=>Int表示返回值是Int类型

(a:Int,b:String)=>{} 这段代码是匿名函数的实体,不是类型。(a:Int,b:String)匿名函数的两个参数名是a,b

=>是匿名函数的标志,这是匿名函数的固定写法

个人认为这些是函数式变成很重要的一个部分,也是函数式编程的一个代表特点

七、嵌套方法

嵌套方法是方法中定义方法

def tt3(s:String)={
  def tt2(s1:String,x1:String)={
    s1+";"+s+":"+x1
  }
  tt2("2","3")
}

八、偏应用函数

这里又有一个固定写法,偏应用函数未被固定写的那一个参数使用_下环线代替

def showLog(data:Date,log:String)={
  print(s"$data is date,log is $log")
}
val date=new Date()
showLog(date,"a")
showLog(date,"b")
showLog(date,"c")
showLog(date,"d")
//上边的showlog方法被调用了四次,其中每次调用只是该表了第二个方法,这种情况下可以使用偏应用方法

def fun12=showLog(date,_:String)// 赋值的时候,第二个参数不做改动,就用_下划线代替,固定写法
fun12("a")
fun12("b")
fun12("c")
//这样调用fun的时候,date参数就默认有了,只需要改变第二个参数

九、高阶函数

1、方法的参数是方法

使用方法的参数传递方法,其实传递的是一段逻辑的名称,或者就是一段逻辑

//方法的参数是匿名函数,参数名是s1
def fun1(s1:(Int,Int)=>Int,a:Int):Int={
  s1(a,0)
  a
}
fun1(
  (a:Int,b:Int)=>{a+b}, //传递进去一个匿名方法
  0)

不同的写法

def add(a:Int,b:Int)={
  a+b
}

def hi(fun:(Int,Int)=>Int,s:String): Unit ={
  fun(100,200)+"*"+s
}

val unit: Unit = hi(add, "sd") //传进去一个方法,相当于传递了一段逻辑的名称,这个名称就叫add

2、方法的返回是方法

def fun2(a:Int):(Int,Int)=>String={ //方法类型作为返回值类型
  val fun=(a:Int,b:Int)=>{
    a+":"+b
  }
  fun //返回方法变量
}

注意事项:方法的返回是方法,必须显示的写出方法的返回值类型是方法,或者在返回的方法后边加上下划线

//a方法的返回值是方法,必须在a方法中表明返回值类型
def fun3(a:Int):(Int,Int)=>String={
  def innerFun(a:Int,b:Int)={
    a+":"+b
  }
  innerFun
}
//等同于
def fun4(a:Int)={//未写返回值类型,则必须在最后一行方法名后边添加 _
  def innerFun(a:Int,b:Int):String={
    a+":"+b
  }
  innerFun _  //方法名后边写_ ,表示将innerFun强制转换为方法类型。
}
//调用
fun4(2)(2,3)
如果只写fun4(2) ,那么只会执行fun4的内容,不会涉及到内部的innerFun方法内容

3、方法的参数和参数是方法

前两种的结合

  def fun5(a:Int,fun1:(String,String)=>Int):(Int,Int)=>String={
    def innerFun(a:Int,b:Int):String={
//      val c=fun1("2","33") //将犯法的返回值赋值给变量
      val c=fun1 //将方法参数赋值给一个变量
      val sc=c("2","3")//通过变量调用方法
      println(s"$sc")
      a+":"+b+c
    }
    innerFun
  }

  fun5(0,(s:String,l:String)=>{
    s.length+l.length
  })(0,0)
//也可以省去匿名方法体参数的类型,类型推断可以推断出来参数类型
fun5(0,(a,b)=>{
  a.length+b.length
})

十、柯里化函数

方法的返回值是方法的另外一种调用方式

  def kelihua(a:Int,b:Int)(c:Int,d:Int)={
    a+b+c+d
  }
  kelihua(2,2)(3,4)
//  上述就是柯里化函数的写法,是方法的返回值是方法的另外一种写法

十一、闭包

闭包是一个函数,返回值依赖于声明在外部的一个或多个变量 简单来说就是可以访问不在当前作用于范围内的一个函数

def main(args:Array[String]):Unit={
  val a=100 //变量a不处于其有效作用于时,函数还能够对变量进行访问
  fun5(0,(s:String,l:String)=>{
    s.length+l.length+a //a不在方法中,还可以被访问,a是一个自由变量
  })(1,2)
}