一:scala中的函数
Scala中,函数上升和变量同等的位置,或者说函数也是一种变量;
Scala中的函数可以作为实参传递给另一个函数;
函数可以作为返回值;
函数可以赋值给变量(这个变量需符合函数的类型的变量);
函数可以存储在数据结构之中。
函数如同普通变量一样,也具有类型;
函数的定义可以有很多种,因此需要掌握最基本的定义方法:
1,函数的基本定义
val f1 = (a: Int, b: Int) => a + b
val f2 = ((a: Int, b: Int) => a + b)
val f3 = (_: Int) + (_: Int)
val f7 = ()
val f4: (Int, Int) => Int = (_ + _)
val f5:(Int,Int)=>Int =(x,y)=>x+y
val f6:(Int,Int)=>Int=(m,n)=>if (m+n>0) m else n
2,自定义函数
scala> val f1 = new Function2[Int, Int, Int] {
| def apply(x: Int, y: Int): Int = if (x < y) y else x
| }
f1: (Int, Int) => Int = <function2>
3,匿名函数
没有名称的函数是匿名函数,也称为函数文字,或者函数字面量
定义格式:
(形参列表) => {函数体}
例如: (x: Int, y: Int) => x + y
二:scala中的方法
方法的定义:
def max(x:Int,y:Int):Int={
if(x>y)
x
else
y
}
def:开始函数定义
max:函数名
() 括号中的参数列表 (一般称之为入参)
Int: 函数返回值类型
{}: 大括号中的函数体
“=”并不只是用来分割函数签名和函数体的,它的另一个作用是告诉编译器是否对函数的返回值进行类型推断!如果省去=,则认为函数是没有返回值的!
例如:
scala> def myFirstMethod() = { “exciting times ahead” }
myFirstMethod: ()java.lang.String
##缺少等号时,表名该方法是没有返回值的
scala> def myFirstMethod(){ “exciting times ahead” }
myFirstMethod: ()Unit
def 方法名(参数名:参数类型):返回值类型={
方法体
}
注意: 方法体内的最后一行为返回值,不需要使用return (和java不同)
如果最后一行是赋值或者输出语句,则没有返回值;
赋值语句和输出语句返回值类型为: Unit (),即没有返回值;
示例:
def add(x:Int,y:Int):Int={
x+y
}
调用: add(2,4)
def tree()=2+3
调用 : tree 当一个方法没有入参,调用时可以省略括号,直接使用方法名就可以调用
方法中的参数:
默认参数
def sayName(name:String="lisi")={
println(name)
}
调用: sayName() 注意,这个小括号不能省略,否则会报错;
命名参数
就是调用一个函数时,使用参数名来调用(这样即使参数顺序不一致也能调用成功)
可变参数
def addkebian(numbers:Int*):Int={
var result=0
for(number <- numbers){
result += number
}
result
}
调用: addkebian(1,2,3,4)
三:scala中的方法和函数对比
Scala 有函数和方法,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
这两者主要区别如下:
区别一:方法可以作为一个表达式的一部分出现(调用函数并传参),但是方法(带参方法)不能作为最终的表达式,但是函数可以作为最终的表达式出现。
scala> //定义一个方法
scala> def m(x:Int) = 2*x
m: (x: Int)Int
scala> //定义一个函数
scala> val f = (x:Int) => 2*x
f: Int => Int = <function1>
scala> //方法不能作为最终表达式出现
scala> m
<console>:9: error: missing arguments for method m;
follow this method with `_‘ if you want to treat it as a partially applied function
m
^
scala> //函数可以作为最终表达式出现
scala> f
res9: Int => Int = <function1>
无参方法可以作为最终表达式出现,其实这属于方法调用,scala规定无参方法的调用可以省略括号,所以这
并不违法上面的说法;
scala> def m1()=1+2
m1: ()Int
scala> m1
res10: Int = 3
区别二:参数列表对于方法是可选的,但是对于函数是强制的。
scala> //方法可以没有参数列表
scala> def m2 = 100;
m2: Int
scala> //方法可以有一个空的参数列表
scala> def m3() = 100
m3: ()Int
scala> //函数必须有参数列表,否则报错
scala> var f1 = => 100
<console>:1: error: illegal start of simple expression
var f1 = => 100
^
scala> //函数也可以有一个空的参数列表
scala> var f2 = () => 100
f2: () => Int = <function0>
即参数列表对于函数来说是必须的,哪怕这是一个空的参数列表;
区别三:方法名意味着方法调用,函数名只是代表函数自身。
因为方法不能作为最终的表达式存在,所以如果你写了一个方法的名字并且该方法不带参数(没有参数列表或者无参) 该表达式的意思是:调用该方法得到最终的表达式。
因为函数可以作为最终表达式出现,如果你写下函数的名字,函数调用并不会发生,该方法自身将作为最终的表达式进行返回,如果要强制调用一个函数,你必须在函数名后面写()
scala> def m1()=1+2 //定义一个空参方法
m1: ()Int
scala> def m2 = 100; // 定义个无参方法
m2: Int
scala> var f2 = () => 100 // 定义一个空参函数
f2: () => Int = $$Lambda$1047/510873326@1e191150
scala> m1 //调用一个空参方法
res3: Int = 3
scala> m2 //调用一个无参方法
res4: Int = 100
scala> f2 //得到函数自身,不会发生函数调用
res5: () => Int = $$Lambda$1047/510873326@1e191150
scala> f2() // 需要使用函数名()的形式才会发生函数调用
res6: Int = 100
为什么在函数出现的地方我们可以提供一个方法?
在scala中很多高级函数,如map(),filter()等,都是要求提供一个函数作为参数。但是为什么我们可以提供一个方法呢?就像下面这样:
scala> val myList = List(3,56,1,4,72)
myList: List[Int] = List(3, 56, 1, 4, 72)
scala> // map()参数是一个函数
scala> myList.map((x) => 2*x)
res15: List[Int] = List(6, 112, 2, 8, 144)
scala> //尝试给map()函提供一个方法作为参数
scala> def m4(x:Int) = 3*x
m4: (x: Int)Int
scala> //正常执行
scala> myList.map(m4)
res17: List[Int] = List(9, 168, 3, 12, 216)
这是因为,如果期望出现函数的地方我们提供了一个方法的话,该方法就会自动被转换成函数。该行为被称为ETA expansion。
这样的话使用函数将会变得简单很多。
利用这种自动转换,我们可以写出很简洁的代码,如下面这样
scala> val list = List(1,2,10,34,56) // 定义一个list集合
list: List[Int] = List(1, 2, 10, 34, 56)
//10.< 被解释成obj.method,即整形的<的方法,所以该表达式是一个方法,会被解释成函数
scala> list.filter(10.<)
res9: List[Int] = List(34, 56)
因为在scala中操作符被解释称方法
- 前缀操作符:op obj 被解释称obj.op
- 中缀操作符:obj1 op obj2被解释称obj1.op(obj2)
- 后缀操作符:obj op被解释称obj.op
你可以写成10<而不是10.< 所以上面有多种写法:
list.filter(10<) 或者 list.filter(10.<) 或者 list.filter(10.<_)
如何强制把一个方法变成函数?
可以在方法名后面加一个下划线强制变成函数
四:方法(函数)使用过程中一些细节问题
- 函数的形参列表可以是多个, 如果函数没有形参,调用时 可以不带()
- 形参列表和返回值列表的数据类型可以是值类型和引用类型。
- Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return关键字可以省略。
- 因为Scala可以自行推断,所以在省略return关键字的场合,返回值类型也可以省略;
- 如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回类型 = ,当然如果你什么都不写,即使有return 返回值也为()
- 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值
- 如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为Any)
- Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数中可以再声明/定义函数,类中可以再声明类 ,方法中可以再声明/定义方法
示例如下:
object Demo3 {
def main(args: Array[String]): Unit = {
def f1(): Unit ={
println("main方法中的f1方法:")
}
def sayok(): Unit ={ // 编译之后是 private final sayok$1
println("main方法中的sayok方法。。。")
def sayok(): Unit ={ // 编译之后是 private final sayok$2
println("sayok方法中嵌套sayok方法")
}
}
}
def sayok(): Unit ={
println("类中的sayok方法。。。") // 编译之后是 sayok
}
}
反编译之后:
- Scala函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值。
- 如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数
- scala 函数的形参默认是val的,因此不能在函数中进行修改.
- 递归函数未执行之前是无法推断出来结果类型(即递归函数不能使用自动推导),在使用时必须有明确的返回值类型