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 = <function2>

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

简化
当赋值给高阶函数的时候
可以省略参数类型,通过类型推断得到 (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 = <function3>
//调用函数值的apply方法
scala> a(1, 2, 3)
res11: Int = 6

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

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

用下划线表示整个参数列表,可以理解为把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 = <function1>
scala> val inc9999 = makeIncreaser(9999)
inc9999: Int => Int = <function1>

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方法进行比较
在一个函数内部多次使用传名调用值时,每次都会重新计算一次表达式的值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342