Scala 函数

最常见的是object内部的成员函数,直接通过object名称调用
此外Scala还有内嵌在函数中的函数,函数字面量和函数值

函数嵌套 Nested Functions

可以在函数内部嵌套定义别的函数,叫做局部函数local function,只在包含它的代码块中可见,可以访问包含函数的参数

   def factorial(i: Int): Int = {
      def fact(i: Int, accumulator: Int): Int = {
         if (i <= 1)
            accumulator
         else
            fact(i - 1, i * accumulator)
      }
      fact(i, 1)
   }

匿名函数Anonymous Functions

也叫作叫做函数字面量function literals,Scala函数是头等函数first class function,可以用匿名函数字面量作为值传递
函数字面量被编译到类中,并在运行时实例化为函数值function value,常常用作高阶函数的输入

实质

每个函数值都是某个扩展scala包中FunctionN特质 类的实例,每个FunctionN特质都有一个用于调用函数的apply方法

 object Main extends App {
   val succ = (x: Int) => x + 1
   val anonfun1 = new Function1[Int, Int] {
     def apply(x: Int): Int = x + 1
   }
   assert(succ(0) == anonfun1(0)) // succ匿名类是anonfun1的简写
}
scala> val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int = 

scala> var increase = (x: Int) => x + 1
increase: Int => Int = 

简化
当赋值给高阶函数的时候
可以省略参数类型,通过类型推断得到 (x) => x + 1
参数只有一个的时候,可以去掉输入参数的括号x => x + 1
值函数参数在右边只出现一次,可以使用占位符_ + 1

someNumbers.filter((x: Int) => x > 0)
someNumbers.filter((x) => x > 0) // 目标类型化 target typing,可以使用目标类型推测表达式的类型
someNumbers.filter(x => x > 0) //如果参数可以推测,省略圆括号

占位符

把下划线当做一个或多个参数(只要参数在函数字面量内只出现一次)的占位符,进一步简写函数字面量

someNumbers.filter(x => x > 0)
//等价于
someNumbers.filter(_ > 0)

多个下划线指代的是多个参数,不是单个参数的重复使用

val add = (_: Int) + (_: Int) //(a: Int, b: Int) => a + b
var increment=(_:Int)+1

部分应用函数 Partially Applied Functions

部分应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数

例如:

//定义函数
scala> def sum(a: Int, b: Int, c: Int) = a + b + c
sum: (a: Int, b: Int, c: Int)Int
//通过部分应用表达式 sum _ 自动生成类,实例化为函数值并赋值
scala> val a = sum _
a: (Int, Int, Int) => Int = 
//调用函数值的apply方法
scala> a(1, 2, 3)
res11: Int = 6

提供部分参数,Scala编译器生成一个新的函数类,其apply方法对应缺失的参数

scala> val b = sum(1, _: Int, 3)
b: Int => Int = 

用下划线表示整个参数列表,可以理解为把def函数转换为函数值,因为不能把成员函数或者嵌套函数直接赋值给变量或者是函数参数,但函数值是可以直接赋值的

someNumbers.foreach(println _) // someNumbers.foreach(x => println(x))
someNumbers.foreach(println) // 当函数参数需要一个函数时,可以进一步省略,例如 def foreach[U](f: A => U): Unit

闭包

闭包是一个函数,内部包含对外部的一个或多个变量的引用

函数makeIncreasermore为自由变量(free variable),x为绑定变量(bound variable),带自由变量的函数字面量称为开放项(open term),内部包含指向捕获变量的引用

var more = 1
def makeIncreaser(more: Int) = (x : Int) => x +more

外部变量在闭包创建后改变,闭包会受到影响;闭包对捕获变量的改变,变量在闭包之外同样受到影响

scala> val inc1 = makeIncreaser(1)
inc1: Int => Int = 
scala> val inc9999 = makeIncreaser(9999)
inc9999: Int => Int = 

scala> inc1(10)
res20: Int = 11
scala> inc9999(10)
res21: Int = 10009

特殊函数调用的形式

Scala支持重复参数,命名实参,默认参数

重复参数 Repeated parameters

函数的最后一个参数可以是重复的,在参数的类型之后放一个星号来指明重复的参数,参数数目可以从0到任意多

printStrings("Hello", "Scala", "Python")

def printStrings( args:String* ) = {}

函数内部重复参数的类型是对应类型的数组,但是不能直接传递数组,而是在数组名称后加上: _*,指示编译器把数组的每个元素作为参数传递

printString(args: _*)

命名实参 Named Arguments

正常情况下,参数按被调用函数的参数顺序依次匹配
命名参数允许以不同的顺序将参数传递给函数,每个实参前面加上形参名称和等号

object Demo {
   def main(args: Array[String]) {
      printInt(b = 5, a = 7);
   }
   
   def printInt( a:Int, b:Int ) = {
      println("Value of a : " + a ); //7
      println("Value of b : " + b );//5
   }
}

默认参数 Default Parameter

Scala 可以为函数参数指定默认参数值,Java语言不支持该功能,必须通过重载才能间接实现
有默认值的实参可以选择忽略,设为的默认值,可以和命名实参结合使用

def addInt( a:Int = 5, b:Int = 7 ) : Int = {
   var sum:Int = 0
   sum = a + b
   return sum
}

addInt() //12
addInt(a = 3) //10
addInt(b = 15) //20

高阶函数 Higher-Order Functions

使用其他函数作为参数,或其返回值是一个函数

object Files {
  object FileMatcher {
    private def filesHere = (new java.io.File(".")).listFiles
  
    private def filesMatching(matcher: String => Boolean) =
      for (file <- filesHere; if matcher(file.getName))
        yield file
  
    def filesEnding(query: String) =
      filesMatching(_.endsWith(query))
  
    def filesContaining(query: String) =
      filesMatching(_.contains(query))
  
    def filesRegex(query: String) =
      filesMatching(_.matches(query))
  }
}

柯里化 Currying

函数定义多个参数列表,使用部分参数列表调用某个方法时,产生一个将缺少的参数列表作为其参数的函数

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int, y: Int)Int

scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)

scala> curriedSum(1)(2) //实质上是接连进行了两次函数调用,中间的函数值是 curriedSum(1)_
res5: Int = 3

传名参数 by-name parameters

在函数内部进行参数表达式的值计算,传名类型中空的参数列表被省略,只用写 =>,仅用于形参

var assertionsEnabled = true
def myAssert(predicate: () => Boolean) =
  if (assertionsEnabled && !predicate())
    throw new AssertionError

//正确调用
myAssert(() => 5 > 3) 
// 错误调用
myAssert(5 > 3) // 缺少() =>

//传名调用
def byNameAssert(predicate: => Boolean) =
  if (assertionsEnabled && !predicate)
    throw new AssertionError
//正确调用
byNameAssert(5 > 3)

byNameAssert(5 > 3)括号中的表达式不是直接计算的,而是创建一个函数值,在它的apply方法进行比较
在一个函数内部多次使用传名调用值时,每次都会重新计算一次表达式的值

你可能感兴趣的:(Scala 函数)