Scala笔记--函数式编程

Posted 幼儿园园草

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala笔记--函数式编程相关的知识,希望对你有一定的参考价值。

1 函数式编程的优点

纯函数的行为表现出与上下文无关的透明性和无副作用性,即函数的调用结果只与输入值有关,而不会受到调用时间和位置的影响,另外,函数的调用也不会改变任何全局对象,这些特性使得多线程的并发应用中最复杂的状态同步问题不复存在。正是这一巨大优势,使得函数式编程在大数据应用和并发需求的驱动下,成为越来越流行的编程范式。

2 函数的定义

2.1 函数基本语法

def 方法名(参数列表):结果类型={方法体}

2.2 函数与方法区别

package com.lrm.demo05

/**
 1. @author RuiMing Lin 
 2. @date 2021-05-20 0:17
 3. @description 函数的定义
 */
object demo1 {
  def main(args: Array[String]): Unit = {
    // 定义函数f1
    def f1(name: String): Unit = {
      println("hi, " + name)
    }

    // 调用函数f1
    f1("RM")

    // 调用对象方法
    demo1.m1("LRM")

    // 获取方法返回值
    val result = demo1.m2("solo")
    println(result)
  }

  // 定义方法m1,参数为String类型,返回值为空
  def m1(name: String): Unit = {
    println("Hi, " + name)
  }

  // 定义方法m2,参数为String类型,返回值为String类型
  def m2(name: String): String = {
    println("Hello, " + name)
    return "Hello"
  }
}

函数和方法大体上是一样的,只有小部分的区别。

  1. 为完成某一功能的程序语句的集合,称为函数。
  2. 类中的函数称之方法。(或者说定义在类中方法外的可以称之为方法或者函数,但定义在方法内的只能称之为函数)。

2.3 函数参数和返回值

函数的参数和返回值大体上与Java的方法并没有区别,大体上可以分为6类。

package com.lrm.demo05

/**
 * @author RuiMing Lin
 * @date 2021-05-28 14:41
 * @description 函数的参数和返回值
 */
object demo2 {
  def main(args: Array[String]): Unit = {
    // (1)函数1:无参,无返回值
    def f1(): Unit = {
      println("1. 无参,无返回值")
    }

    f1()
    println(f1()) // 先执行f1、再输出空返回值
    println("=========================")

    // (2)函数2:无参,有返回值
    def f2(): Int = {
      println("2. 无参,有返回值")
      return 27
    }

    f2()
    println(f2())
    println("=========================")

    // (3)函数3:有参,无返回值
    def f3(name: String): Unit = {
      println("3:有参,无返回值 " + name)
    }

    println(f3("solo"))
    println("=========================")

    // (4)函数4:有参,有返回值
    def f4(name: String): String = {
      println("4:有参,有返回值 " + name)
      return "hi, " + name
    }

    println(f4("solo"))
    println("=========================")

    // (5)函数5:多参,无返回值
    def f5(name1: String, name2: String): Unit = {
      println("5:多参,无返回值")
      println(s"${name1}和${name2}都是我的好朋友")
    }

    f5("alice", "bob")
    println("=========================")

    // (6)函数6:多参,有返回值
    def f6(a: Int, b: Int): Int = {
      println("6:多参,有返回值")
      return a + b
    }

    println(f6(12, 15))
  }
}

2.4 函数的可变参数

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-05-28 14:55
 * @description 可变参数
 */
object demo3 {
  def main(args: Array[String]): Unit = {
    //  (1)可变参数
    def f1(str: String*): Unit = {
      println(str)
    }

    f1("alice")
    f1("aaa", "bbb", "ccc")
    println("=========================")

    // (2)可变参数放置在最后(同Java)
    def f2(str1: String, str2: String*): Unit = {
      println("str1: " + str1 + " str2: " + str2)
    }

    f2("alice")
    f2("aaa", "bbb", "ccc")
    println("=========================")

    // (3)参数默认值,一般将有默认值的参数放置在参数列表的后面
    def f3(name: String = "幼儿园"): Unit = {
      println("My school is " + name)
    }

    f3("第一小学")
    f3()
    println("=========================")

    // (4)带名参数
    def f4(name: String = "solo", age: Int): Unit = {
      println(s"${age}岁的${name}在第一小学学习")
    }

    f4("alice", 20)
    f4(age = 23, name = "bob")
    f4(age = 21)
    println("=========================")

    // 4 带名参数
    def f5(name: String = "solo", age: Int): String = {
      s"${age}岁的${name}在第一小学学习"
    }
    println(f5(name = "小明",age = 20))
  }
}

3 函数简化

  1. return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
  2. 如果函数体只有一行代码,可以省略花括号
  3. 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
  4. 如果有 return,则不能省略返回值类型,必须指定
  5. 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
  6. Scala 如果期望是无返回值类型,可以省略等号
  7. 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
  8. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
  9. 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-05-28 15:03
 * @description 函数简化
 */
object demo4 {
  def main(args: Array[String]): Unit = {

    // 没有经过简化的函数
    def f0(name: String): String = {
      return name
    }

    println(f0("solo"))
    println("==========================")

    // (1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
    def f1(name: String): String = {
      name
    }

    println(f1("solo"))
    println("==========================")

    //(2)如果函数体只有一行代码,可以省略花括号
    def f2(name: String): String = name

    println(f2("solo"))
    println("==========================")

    //    (3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    def f3(name: String) = name

    println(f3("solo"))
    println("==========================")

    //    (4)如果有return,则不能省略返回值类型,必须指定,否则报错
    //        def f4(name: String) = {
    //          return name
    //        }

    //        println(f4("solo"))
    println("==========================")

    //    (5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
    def f5(name: String): Unit = {
      return name
    }

    println(f5("solo"))
    println("==========================")

    //    (6)Scala如果期望是无返回值类型,可以省略等号
    def f6(name: String) {
      println(name)
    }

    println(f6("solo"))
    println("==========================")

    //    (7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    def f7(): Unit = {
      println("solo")
    }

    f7()
    f7
    println("==========================")

    //    (8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def f8: Unit = {
      println("solo")
    }

    //    f8()
    f8
    println("==========================")

    //    (9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
    def f9(name: String): Unit = {
      println(name)
    }

    // 匿名函数,lambda表达式
    (name: String) => {
      println(name)
    }
      println("==========================")
  }
}

4 高阶函数

函数在 Scala 中是与其他对象处于同等级别的“头等公民”,可以像任何其他数据类型的值一样被传递和操作,因此,也可以作为其他函数的参数或者返回值。当一个函数包含其他函数作为其参数或者返回结果为一个函数时,该函数被称为高阶函数。可以说,支持高阶函数是函数式编程最基本的要求,高阶函数可以将灵活、细粒度的代码块集合成更大、更复杂的程序。为了实现与高阶函数类似的功能,C++语言需要采用复杂的函数指针;Java 8以前则需要通过繁琐的接口来实现。相比之下,Scala中实现高阶函数则显得非常直观。

4.1 高阶函数基础

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-05-28 16:15
 * @description 高阶函数
 */
object demo6 {
  def main(args: Array[String]): Unit = {
    // 0.定义两个函数
    def fun1(n: Int): Int = {
      n + 1
    }

    def fun2(): Int = {
      -999
    }

    // 1. 函数作为值进行传递,赋给另外一个变量
    val f1: Int => Int = fun1
    val f2 = fun1 _ // 省略变量的类型的话,需要加下划线是为了区分返回方法还是方法的返回值
    println(f1) // 输出函数的地址
    println(f1(12))
    println(f2)
    println(f2(15))
    println("========================")

    val f3: () => Int = fun2
    val f4 = fun2 _
    println(f3)
    println(f4)
    println(f4())
    println("========================")

    // 2. 函数作为参数进行传递
    // 定义二元计算函数:二元操作op,操作数1:a,操作数2:b
    def dualEval(op: (Int, Int) => Int, a: Int, b: Int): Int = {
      op(a, b)
    }

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

    println(dualEval(add, 12, 35))
    println(dualEval((a, b) => a + b, 12, 35))
    println(dualEval(_ + _, 12, 35))
    println("========================")

    // 3. 定义函数f5,该函数的返回值是类型为 Int => Unit 的函数
    def f5(): Int => Unit = {
      def f6(a: Int): Unit = {
        println("f6调用 " + a)
      }

      f6
    }

    println(f5()(25))
  }
}

4.2 高阶函数例子

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-06-02 14:11
 * @description
 */
object demo14 {
  def main(args: Array[String]): Unit = {
    // 定义二元计算函数:二元操作op,操作数num1和num2
    def dualEval(op: (Int, Int) => Int, num1: Int, num2: Int): Int = {
      op(num1, num2)
    }

    val num1: Int = 12
    val num2: Int = 15

    // 实现加法操作
    def add(a: Int, b: Int): Int = {
      a + b
    }

    val addResult: Int = dualEval(add, num1, num2)
    println(addResult)

    // 实现减法操作,传入匿名函数
    val minuteResult: Int = dualEval((num1, num2) => num1 - num2, num1, num2)
    println(minuteResult)

    // 实现乘法操作,传入匿名函数
    val multiResult: Int = dualEval((num1, num2) => num1 * num2, num1, num2)
    println(multiResult)
  }
}

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-05-28 17:46
 * @description
 */
object demo7 {
  def main(args: Array[String]): Unit = {
    val arr: Array[Int] = Array(12, 45, 75, 98)

    // 对数组进行处理,将操作抽象出来,处理完毕之后的结果返回一个新的数组
    def arrayOperation(array: Array[Int], op: Int=>Int): Array[Int] = {
      for (elem <- array) yield op(elem)
    }

    // 定义一个加一操作
    def addOne(elem: Int): Int = {
      elem + 1
    }

    // 调用函数
    val newArray: Array[Int] = arrayOperation(arr, addOne)
    println(newArray.mkString(","))

    // 传入匿名函数,实现元素翻倍
    val newArray2 = arrayOperation(arr, _ * 2)
    println(newArray2.mkString(", "))
  }
}

5 偏应用函数

有时候一个函数在特殊应用场景下部分参数可能会始终取相同的值,为了避免每次都提供这些相同的值,我们可以用该函数来定义一个新的函数。比如有一个三元加法操作,但在某个场景下,其中一个加数总是为1,重复的传入1会稍显麻烦,这时候就可以使用偏应用函数了。

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-06-02 14:32
 * @description
 */
object demo15 {
  def main(args: Array[String]): Unit = {
    // 定义三元加法操作
    def sum(a:Int, b:Int, c:Int):Int = {
      a+b+c
    }

    // 使用偏应用函数
    val sumByAddOne = sum(1,_,_)
    println(sumByAddOne)
    println(sumByAddOne(2,3))
  }
}

6 函数Curry化

Curry化的函数是指那种带有多个参数列表且每个参数列表只包含一个参数的函数

6.1 多层函数嵌套

要了解函数科里化,可以从多层函数嵌套了解先,使用函数嵌套是一个比较繁琐的过程,对于初学者很不友好。如下列所示,定义一个三层嵌套的函数。

package com.lrm.demo05

/**
 * @author RuiMing Lin 
 * @date 2021-06-02 14:40
 * @description
 */
object demo16 {
  def main(args: Array[String]): Unit = {
    // 定义三元操作
    def 以上是关于Scala笔记--函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

scala学习笔记-函数式编程(14)

Scala笔记--函数式编程

Scala笔记整理:scala基本知识

理解Scala的函数式编程思想

Scala函数式编程函数式的数据结构 下

scala入门笔记