Scala-函数式编程

函数式编程

// scala语言是函数式编程语言,所以,万物皆函数,所以方法其实就是函数
// def 函数名(参数1:参数类型1, 参数2:参数类型2):函数返回值类型 = {函数体}
        def test1(): Unit = {
            println("test function...")
        }

函数与方法(区分)

函数()

java本质是将函数编译成一个新的方法

函数无法重载&重写

可嵌套使用

方法()

方法可重载&重写

// 函数作用域比较窄
// 方法的作用域比较大

函数的本质

编译后,函数会被编译为一个新的方法 private static final Unit test$1()

如果函数名与方法名相同,调用时默认为函数调用

参数与返回值

package test_funcation

object scala_testfuncation01 {

  def main(args: Array[String]): Unit = {
    def test()  : Unit = {
      println("test funcation")
    }
    def function_00(): Unit = {
      println("无入参无返回值")
    }
    def function_01(): String ={
      return ("无入参有返回值")
    }
    // 参数声明方式 -> 参数名 : 参数类型
    def function_02(parameter:String): Unit = {
      println("有参无返回值 :"+parameter)
    }
    def function_03(parameter1:String,parameter2:String): Unit = {
      println(s"parameter1:${parameter1}\nparameter2:${parameter2}")
    }

    function_00()
    function_00    // 无参时可省略小括号
    println(function_01())
    println(function_01)
    function_02("参数")
    function_03("参数1","参数2")

    test()
    scala_testfuncation01.test()
  }

  def test() : Unit = {
      println("test method")
  }
}

java中所有方法都是值传递 而非引用传递

函数的参数没有限制

可变参数

可变参数在使用时,都是集合对象

// 可变参数:参数类型加*
def test(names:String*)  : Unit = {
  for (name <- names){
    println(name)
  }
  println(names)
}
// 可变参数应放在最后一个
def test2(age: Int,names: String*): Unit = {
  for (name <- names) {
    println(name)
  }
  println(names)
}

默认参数

// 设置参数默认值
// 底层为java封装的方法 当不传参数时 java自动调用该方法
def test3(password:String = "000000"): Unit = {
  println(password)
}

def test4(password: String = "000000",name :String): Unit = {
  println(name+password)
}
test()
test("张三")
test("张三","李四")
test("张三","李四","王五")

test3("1111111")
// 当多参输入时 可使用带名参数输入
test4(name = "张三")
test3()
test3()
// 调用时,参数如果想用使用默认值,可以不传递参数
// 调用时,如果不想使用默认值,注解传值即可

带名参数

// TODO 4. 带名参数:传递参数时,增加参数的名称,用于改变传参的顺序
def fun4( password:String = "000000", name:String  ): Unit = {

}

// 参数在传递时默认为顺序匹配。
// scala可以通过特殊的语法改变传值的顺序
fun4(name="zhangsan")

可变参数和默认参数不能联合声明的

def test( password:String = "000000", name:String* ): Unit = {

}
// 可变参数和参数默认值是不能联合声明
// test()
test("000000")
test("000000", "zhangsan")
test("000000", "zhangsan", "lisi")

函数至简原则

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

def test1(): String = {
  return "1"
}
println(test1())

// 如果只有一个返回值 可以省略return
def test2(): String = {
  "2"
}
println(test2())

// 如果返回值类型可以确定 可以省略函数类型
def test3()={
  "3"
}
println(test3())

def test3_2()={
  val num = 20
  if (num > 30) {
    "大于30"
  } else {
    1
  }
}
println(test3_2())

// 如果函数逻辑代码只有一条 可以省略花括号
def test4() = "4"
println(test4())

// 如果函数无参,省略小括号
def test5 = "5"
println(test5)

// TODO 过程函数 不会返回任何值
// 省略Unit ,同时不希望return起作用 可将等号同时省略
def test6{
  "6"
}
println(test6)

// 关键字与函数名也可省略
// 省略的同时需将返回类型一同省略 并将=转换为 =>
// TODO 匿名函数
val  f = () => {"null"}
println(f())

高阶函数编程

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

函数也是个对象

def test1(): Unit = {
  println("1")
}
val f1 = test1()
val f2 = test1 _ // 如果将函数作为一个整体赋值给变量 需要采用特殊符号:下划线 _
f2()
// 万物皆对象,但是对象都有类型,就意味着函数对象也有类型
// 函数独立使用时,参数声明没有个数限制的,但是如果将函数作为对象给别人使用,那么函数的参数声明的个数最多为22个
val f2 : Function0[Unit] = fun1 _
//val f22 : Function22[] = fun1 _
// 函数类型还有另外一种写法 : (输入参数类型)=>输出类型
val f3 : () => Unit = fun1 _
// 之所以使用下划线让函数作为对象使用,因为代码中没有明确变量的类型,所以需要通过取值类推断
// 如果变量声明的类型为函数类型,那么可以不使用下划线让函数作为对象
val ff : (String, Int) => String = test1
ff
// (String) => String
// (String, Int) => String
// String => String

将函数作为参数来使用

        def test( f : (Int, Int) => Int ): Unit = {
            val r = f(10, 20)
            println(r)
        }
//
//        def fun2(x:Int, y:Int): Int = {
//            x + y
//        }
//        def fun3(x:Int, y:Int): Int = {
//            x - y
//        }
//        def fun4(x:Int, y:Int): Int = {
//            x * y
//        }
//        def fun5(x:Int, y:Int): Int = {
//            x + y
//        }

//test(fun2) // 与test函数中的f命名无关 调用时只研究test与fun
//test(fun3)
//test(fun4)
        // TODO 匿名函数主要应用于函数作为参数使用
//        test(
//            (x:Int, y:Int) => {
//                x - y
//            }
//        )
        // TODO 匿名函数在使用时也可以存在至简原则
        // 1. 如果函数体的逻辑代码只有一行,大括号可以省略,代码写在一行中
//        test(
//            (x:Int, y:Int) => x - y
//        )
        // 2. 如果参数的类型可以推断出来,那么参数类型可以省略的
//        test(
//            (x, y) => x - y
//        )
        // 3. 如果参数只有一个的话,参数列表的小括号可以省略
        // 4. 如果参数在使用时,按照顺序只使用了一次,那么可以使用下划线代替参数,
        //test(_ * _)
        def test( f : (String)=>Unit ): Unit = {
            f("zhangsan")
        }

        def fun( name:String ): Unit = {
            println(name)
        }

        test(fun)
//        test(
//            ( name:String ) => {
//                println(name)
//            }
//        )
//        test(
//            ( name:String ) => println(name)
//        )
//        test(
//            ( name ) => println(name)
//        )
//        test(
//            name => println(name)
//        )
        test(println(_)) // 匿名函数的至简原则的最终版
        //test(println)
        //test(fun)
def test( f : (Int, Int)=>Int ) = {
    f(10, 20)
}
println(
    test(
        (x, y) => 2 * x + y
    )
)

println(test(2*_ + _)) //运算符也是方法 所以也可以至简原则
def test( x :Int, y:Int, f : (Int, Int)=>Int ): Unit = {
    val r = f(x, y)
    println(r)
}

//test(10, 20, _+_)
test(10, 20,
    (x:Int, y:Int) => {
        x + y
    }
)

将函数作为返回值返回

一般应用于将内部的函数在外部使用

def test(): Unit = {
    println("function...")
}
def fun() = {
    test _
}
//val f = fun _
//val ff = f()
//ff()
fun()()
test()
def outer() = {
    def mid() = {
        def inner(): Unit = {
            println("inner...")
        }
        inner _
    }
    mid _
}
outer()()()
// TODO 闭包
// 一个函数使用了外部的变量,把这个变量包含到了它的内部来使用,改变了这个变量的生命周期
// 将当前的代码形成了一个闭合的环境,这个环境称之为闭包环境,简称为闭包

// Scala2.12版本前闭包功能采用的是匿名函数类型实现
// Scala2.12版本闭包功能采用的是更改函数声明实现

def outer( a : Int ) = {
    def inner( b : Int ): Int = {
        a + b
    }
    inner _
}
//println(outer(10)(20)) 与下面功能相同
val f = outer(10)
val ff = f(20)
println(ff)

控制抽象---暂不了解

// TODO : 控制抽象
// 抽象
// 函数类型:() =>Unit
def myWhile(op: => Boolean) = {
  op
}
// 参数类型不完整,那么在传递参数时,也是不完整:只有传递代码就可以,不需要完整的声明
// 可以采用控制抽象设计语法
Breaks.breakable {
  for ( i <- 1 to 5 ) {
    Breaks.break()
  }
}

柯里化(Curry)

// 将无关的参数分离开
def test( a:Int, b:Int ): Unit = {
    for ( i <- 1 to a ) { // 10min
        println(i)
    }
    for ( j <- 1 to b ) { // 20min
        println(j)
    }
}

def test1(a:Int)(b:Int): Unit = {
    for ( i <- 1 to a ) { // 10min
        println(i)
    }
    for ( j <- 1 to b ) { // 20min
        println(j)
    }
}

val a = 10 // 10min
val b = 20 // 20min
test(a, b) // 60min
test1(a)(b)

递归

StackOverflowError

多次进栈,导致栈溢出

//   1. scala中要求递归函数必须明确声明返回值类型
//   2. 函数内部调用自身
//   3. 一定要有跳出递归的逻辑
//   4. 递归函数在调用时传递的参数之间有关系
// 阶乘
// 5!
// StackOverflowError
// Overflow : 滚动
//        def myRecursion(num:Int):Int = {
//            if ( num <= 1 ) {
//                1
//            } else {
//                num * myRecursion(num-1)
//            }
//        }
//
//        println(myRecursion(5))
def myRecursion(num:Long):Long = {
  if ( num <= 1 ) {
    1
  } else {
    num + myRecursion(num-1)
  }
}
println(myRecursion(10000))

尾递归

当前一个进栈后运行才调用下个进栈,不会导致栈溢出

def test(): Unit = {
    test()
    println("test")
}
//test()

// Scala中尾递归不是真正的递归,是由编译器进行了优化,形成了while循环。所以应该称之为伪递归
// java中也有尾递归。但是不会优化为while循环。
// 尾递归也会出现
// 伪递归
// 尾递归
def test1(): Unit = {
    println("test")
    test1()
}
test1()

惰性函数

// 函数结果没使用,那么这个函数就不会执行
def fun9(): String = {
    println("function...")
    "zhangsan"
}
lazy val a = fun9() // Load 10000User  //lazy 关键字
println("----------") // 2Min
println(a) // 10000 User
posted @ 2022-09-15 18:32  POCOPOCOPOCO  阅读(27)  评论(0编辑  收藏  举报