scala函数使用--学习笔记

来源:互联网 发布:chart.js 使用xml 编辑:程序博客网 时间:2024/06/07 22:45

Scala里,当你调用函数,传入任何需要的参数,实际是把函数应用到参数上

1. 函数先单独在代码中定义,然后在其他代码中单独调用

// 第一种定义样式:

scala> def max(x:Int, y:Int):Int={    

  |    if(x>y)  x    

  |     else y

}

max: (x: Int, y: Int)Int

// 第二种定义样式:如果函数体代码只有一行,则花括号可以省略;且函数返回类型可以由scala推断得出,则返回类型可以省略

scala> def max2(x:Int,y:Int)=if(x>y) x else y

max2: (x: Int, y: Int)Int

scala> max(3,5)

res0: Int = 5

scala> max2(5,3)

res1: Int = 5

// 第三种定义样式,函数不带返回值和参数:

scala> def greet()=println("Hello,world")

greet: ()Unit

scala> greet

Hello,world

scala> greet()

Hello,world

//第四种定义样式,函数不返回值(结果类型为Unit的函数才可以用此种定义样式),省略返回类型和等于号,保留花括号

scala> def greet3(){println("Hello")}

greet3: ()Unit

scala> greet3Hello

如果此种样式函数体的最后一句话返回的是有值的,那么函数会将值丢弃,并把返回类型转换为Unit。想要函数有返回值的话,千万不能省略等号

2. 函数作为参数,使用函数字面量

以下三种样式,arg为函数字面量的参数,println语句为字面量函数体

//第一种样式:使用函数字面量,参数类型可推断

args.foreach(arg=>println(arg))

//第二种样式:使用函数字面量,写明参数类型

args.foreach((arg:String)=>println(arg))

//第三种样式:使用函数字面量,函数体只有一行且只带一个参数

args.foreach(println)

综上,函数字面量的语法构成为:(x:Int, y:Int)=>x+y    参数列表+右箭头+函数体

函数的参数是 val 的,是不可变的,在函数体内给参数复制都会编译失败

如果没有发现任何显示的返回语句,Scala方法将返回方法中最后一次计算得到的值

3. 若方法只有一个参数,调用时可以省略点和括号。

val greetStrings = new Array[String](3)

for( i <- 0 to 2 ){

println(greeting(i))

}

to实际上是只带有一个Int参数的方法,代码 “0 to 2”可以转换成“(0).to(2)”,返回了可以让for表达式遍历的含有 0,1,2 的序列。如下:

scala> (0).to(2)

res8: scala.collection.immutable.Range.Inclusive = Range(0, 1, 2)

scala> 2 to 5

res33: scala.collection.immutable.Range.Inclusive = Range(2, 3, 4, 5)

从技术层面上说,Scala没有操作符重载,因为它根本没有传统意义上的操作符。取而代之的是,+-*/加减乘除这样的字符,可以用来做方法名。

1+2可以转换成(1).+(2)

scala> 1+2

res10: Int = 3

scala> 1.+(2)

res11: Int = 3

scala> (1).+(2)

res12: Int = 3

4. 无返回值的函数的结果类型是 Unit 的,unit value是存在且唯一存在类型为 Unit 的值,写成 ()

()是scala的Unit不同于Java的void的地方
scala> def greet() {println("Hello")}
greet: ()Unit
scala> greet()==()
<console>:16: warning: comparing values of types Unit and Unit using `==' will always yield true
       greet()==()              ^warning: there was one deprecation warning; re-run with -deprecation for detailsHello
res6: Boolean = true
Scala中,循环语句没有返回值,是Unit类型的。对var赋值语句本身也是 unit 的
var line=""
while ( (line=readLine())!="" )  //这个判断式是不起作用的
 println("Read: "+line)
5.可以定义本地函数(可以理解为嵌套函数)
def processFile(filename: String, width: Int): Unit ={
  //定义本地函数,外部无法访问本地函数,本地函数可以使用外层函数的输入参数
def processLine(line:String): Unit ={
    if (line.length>width)
      println(filename+": "+line)
  }
  val source=Source.fromFile(filename)
  for(line <- source.getLines())
    processLine(line)
  //for 循环也可以用以下的 foreach 方法代替
  source.getLines().foreach(processLine)
}
6. 可以用字面量定义函数,并把它们作为值进行传递
scala> var increase=(x: Int)=>x+1
increase: Int => Int = <function1>

scala> increase(10)
res26: Int = 11

scala> increase = (x:Int)=>{
     | println("We are")
     | println(" family")
     | x+1
     | }
increase: Int => Int = <function1>

scala> increase(10)
We are
 family
res27: Int = 11

scala> List(1,2).foreach(increase)  //在foreach里使用指向函数的变量作为输入参数
We are
 family
We are
 family

scala> List(1,2).foreach((x:Int)=>{    //在foreach里使用 函数字面量定义的函数 作为输入参数
     | println(x+1)
     | println("We are family")
     | })
2
We are family
3
We are family

scala> List(1,2).filter(x=>x>1)    //在filter过滤器里使用 函数字面量定义的函数 作为过滤条件的输入参数。且当可以推断出函数的参数类型时,省略类型和括号
res31: List[Int] = List(2)

函数字面量被编译进类,并在运行期实例化函数值。
函数字面量存在于源代码,函数值作为对象存在于运行期。
7. 占位符语法
用函数字面量定义函数时,可以把下划线当做一个或多个参数的占位符,只要每个参数的函数字面量内仅出现一次
下划线只能在函数字面量定义函数时使用
scala> List(1,2).filter(_>1)
res32: List[Int] = List(2)

但当编译器无法推断出函数参数的类型时,则需要明确数据类型
scala> val f=(_:Int)+(_:Int)
f: (Int, Int) => Int = <function2>

scala> val f2=println(_)
<console>:14: error: missing parameter type for expanded function ((x$1) => println(x$1))
       val f2=println(_)
                      ^
下划线代表多个输入参数
scala> def sum(a:Int,b:Int)=a+b
sum: (a: Int, b: Int)Int

scala> val a=sum
<console>:15: error: missing argument list for method sum
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `sum _` or `sum(_,_)` instead of `sum`.
       val a=sum
             ^

scala> val a=sum _  //下划线代表sum函数定义时的参数 a和b。这条语句让编译器产生的类中有一个apply方法带了2个参数
a: (Int, Int) => Int = <function2>

scala> a(1,2)  //这条语句执行时实际上调用的是 a.apply(1,2)
res33: Int = 3

scala> a.apply(1,2)
res34: Int = 3

scala> val a2=sum(1, _:Int)  //提供部分参数来表示一个偏函数
a2: Int => Int = <function1>

scala> a2(5)
res35: Int = 6

scala> List(1,2).foreach(println _) 
1
2
8.闭包
scala> var more=1
more: Int = 1

scala> val addMore= (x:Int)=>x+more  //函数创建时绑定的是其外层变量的索引,而不是当时变量的值。more称作“自由变量”
addMore: Int => Int = <function1>

scala> addMore(2)
res37: Int = 3

scala> more=3
more: Int = 3

scala> addMore(2)
res38: Int = 5
带有外部变量的函数字面量在运行时创建的函数值(对象)被称为闭包(closure)。
名称源自于通过捕获自由变量的绑定,从而对函数字面量执行的“关闭”行动。
不带自由变量的函数字面量,如:(x:Int)=>x+1,被称作封闭项,由封闭项创建的函数值严格来说不是闭包。

scala> var sum=0
sum: Int = 0

scala> List(1,2,3).foreach(sum += _) //自由变量在函数体内被改变

scala> sum
res42: Int = 6

如果闭包访问了某些在程序运行时有若干不同备份的变量,例如闭包使用了某个函数的本地变量(或输入参数),且这个函数被多次调用。
这种情况下,scala编译器重新布局使捕获的函数本地变量的值继续存在堆中,而不是堆栈中,因此可以保留在创建它的方法调用之外。
scala> def m1(more:Int) = (x:Double)=>{
     | println("x="+x)
     | (x+more).toString
     | }
m1: (more: Int)Double => String

scala> val inc1=m1(1)
inc1: Double => String = <function1>

scala> val inc2=m1(10)
inc2: Double => String = <function1>

scala> inc1(10)
x=10.0
res7: String = 11.0

scala> inc2(20)
x=20.0
res1: String = 30.0
9. 重复参数
scala> def echo(args: String*)=for(arg<-args) println(arg)  //星号代表重复参数。实际上args的类型是Array[String],但不能直接传递数组,必须传递数组的每个元素
echo: (args: String*)Unit

scala> echo()
scala> echo("One")
One
scala> echo("One","Two")
One
Two

scala> echo(Array("A","B"))
<console>:13: error: type mismatch;
 found   : Array[String]
 required: String
       echo(Array("A","B"))
                 ^

scala> echo(Array("A","B"): _*)  // :_* 告诉编译器把数组里的每个元素当做参数,而不是当做单一参数传给 echo
A
B
10.尾递归
如果函数的递归调用自己是函数体执行的最后一件事情,就被称为尾递归。
Scala编译器检测到尾递归就用新值更新函数参数,然后把它替换成一个回到函数开头的跳转。
这样无须付出任何运行期的开销。
scala> def boom(x:Int):Int = {
     | if (x==0) throw new Exception("boom!")
     | else boom(x-1)+1
     | }
boom: (x: Int)Int

scala> boom(3)
java.lang.Exception: boom!   //没有尾递归则增加了函数调用的开销
  at .boom(<console>:12)
  at .boom(<console>:13)
  at .boom(<console>:13)
  at .boom(<console>:13)

  ... 32 elided

scala> def boom2(x:Int):Int = {
     | if (x==0) throw new Exception("boom2!")
     | else boom2(x-1)
     | }
boom2: (x: Int)Int

scala> boom2(3)
java.lang.Exception: boom2!
  at .boom2(<console>:14)
  ... 32 elided
使用 -g:notailcalls 的scala参数可以关闭尾递归

0 0