详解scala中函数和方法
Posted YaoYong_BigData
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解scala中函数和方法相关的知识,希望对你有一定的参考价值。
一、scala中的函数
Scala中,函数上升和变量同等的位置,或者说函数也是一种变量;
Scala中的函数可以作为实参传递给另一个函数;
函数可以作为返回值;
函数可以赋值给变量(这个变量需符合函数的类型的变量);
函数可以存储在数据结构之中;
函数如同普通变量一样,也具有类型。
函数的定义可以有很多种,因此需要掌握最基本的定义方法:
1.函数的基本定义
语法:
1.1 完整语法
val 函数名 :(参数类型)=>返回值类型 = (参数名称:参数类型)=>函数体
val add1:(Int,Int)=>Int =(a:Int,b:Int)=>a +b
1.2 简写语法
val 函数名 = (参数名称:参数类型)=>函数体
val add2 =(a:Int,b:Int)=>a +b
函数是一个对象(变量)
类似于方法,函数也有输入参数和返回值
函数定义不需要使用
def
定义无需指定返回值类型
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
res0: (Int, Int) => Int = <function2>
二、scala中的方法
一个类可以有自己的方法,scala中的方法和Java方法类似。但scala与Java定义方法的语法是不一样的。
1.定义方法
语法:
def methodName (参数名:参数类型, 参数名:参数类型) : [return type] =
// 方法体:一系列的代码
参数列表的参数类型不能省略;
返回值类型可以省略,由scala编译器自动推断;
返回值可以不写return,默认就是块表达式的值。
“=”并不只是用来分割函数签名和函数体的,它的另一个作用是告诉编译器是否对函数的返回值进行类型推断!如果省去=,则认为函数是没有返回值的!
例如:
##缺少等号时,表名该方法是没有返回值的
scala> def myFirstMethod() "exciting times ahead"
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
def myFirstMethod() "exciting times ahead"
myFirstMethod: ()Unit
scala> def myFirstMethod()= "exciting times ahead"
myFirstMethod: ()String
2.返回值类型推断
scala定义方法可以省略返回值,由scala自动推断返回值类型。这样方法定义后更加简洁。
定义递归方法,不能省略返回值类型。
示例
定义递归方法(求阶乘)
10 * 9 * 8 * 7 * 6 * ... * 1
参考代码:
scala> def m2(x:Int) =
| if(x<=1) 1
| else m2(x-1) * x
|
<console>:13: error: recursive method m2 needs result type
else m2(x-1) * x
应该定义为:
def m2(x:Int):Int = if(x<=1) 1 else m2(x-1) * x
3.方法参数
scala中的方法参数,使用比较灵活。它支持以下几种类型的参数:
-
默认参数
-
带名参数
-
变长参数
3.1 默认参数
在定义方法时可以给参数定义一个默认值。
示例
1. 定义一个计算两个值相加的方法,这两个值默认为0
2. 调用该方法,不传任何参数
参考代码:
// x,y带有默认值为0
scala> def add(x:Int = 0, y:Int = 0) = x + y
add: (x: Int, y: Int)Int
scala> add()
res1: Int = 0
3.2 带名参数
在调用方法时,可以指定参数的名称来进行调用。
示例
1. 定义一个计算两个值相加的方法,这两个值默认为0
2. 调用该方法,只设置第一个参数的值
参考代码:
scala> def add(x:Int = 0, y:Int = 0) = x + y
add: (x: Int, y: Int)Int
scala> add(x=1)
res2: Int = 1
3.3 变长参数
如果方法的参数是不固定的,可以定义一个方法的参数是变长参数。
语法格式:
def 方法名(参数名:参数类型*):返回值类型 =
方法体
在参数类型后面加一个
*
号,表示参数可以是0个或者多个 。
示例
1. 定义一个计算若干个值相加的方法
2. 调用方法,传入以下数据:1,2,3,4,5
参考代码:
scala> def add(num:Int*) = num.sum
add: (num: Int*)Int
scala> add(1,2,3,4,5)
res1: Int = 15
4.方法调用方式
在scala中,有以下几种方法调用方式,
-
后缀调用法
-
中缀调用法
-
花括号调用法
-
无括号调用法
4.1 后缀调用法
这种方法与Java没有区别。
语法:
对象名.方法名(参数)
示例
使用后缀法Math.abs求绝对值
参考代码:
scala> Math.abs(-1)
res3: Int = 1
4.2 中缀调用法
语法
对象名 方法名 参数
例如:1 to 10
如果有多个参数,使用括号括起来。
示例
使用中缀法Math.abs求绝对值
scala> Math abs -1
res4: Int = 1
4.3 操作符即方法
来看一个表达式:
1 + 1
大家觉得上面的表达式像不像方法调用?
在scala中,+ - * / %等这些操作符和Java一样,但在scala中
-
所有的操作符都是方法;
-
操作符是一个方法名字,是符号的方法。
4.4 花括号调用法
语法:
Math.abs
// 表达式1
// 表达式2
方法只有一个参数,才能使用花括号调用法 。
示例
使用花括号调用法Math.abs求绝对值
参考代码:
scala> Math.abs-10
res13: Int = 10
4.5 无括号调用法
如果方法没有参数,可以省略方法名后面的括号。
示例
-
定义一个无参数的方法,打印"hello"
-
使用无括号调用法调用该方法
scala> def m3()=println("hello")
m3: ()Unit
scala> m3
hello
三、scala中的函数和方法区别
方法是隶属于类或者对象的,在运行时,它是加载到JVM的方法区中;
可以将函数对象赋值给一个变量,在运行时,它是加载到JVM的堆内存中;
函数是一个对象,继承自FunctionN,函数对象有apply,curried,toString,tupled这些方法。方法则没有;
函数声明在方法体中,方法声明在类中方法外;
1.区别一
有参方法可以作为表达式的一部分出现(调用函数并传参),但带参方法不能作为最终的表达式出现;但是函数可以作为最终的表达式出现。
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
2.区别二
参数列表对于方法是可选的,但是对于函数参数列表是强制的。
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>
即参数列表对于函数来说是必须的,哪怕这是一个空的参数列表。
3.区别三
方法名意味着方法调用;但函数名只是代表函数自身。
因为方法不能作为最终的表达式存在,所以如果你写了一个方法的名字并且该方法不带参数(没有参数列表或者无参) 该表达式的意思是:调用该方法得到最终的表达式。
因为函数可以作为最终表达式出现,如果你写下函数的名字,函数调用并不会发生,该方法自身将作为最终的表达式进行返回,如果要强制调用一个函数,你必须在函数名后面写()。
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
4.区别四
在期望出现函数的地方使用方法,该方法自动转换成函数;手动强制转换可以使用 "方法名 _" 转换成函数。
在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> //期望出现函数的地方,我们可以使用方法
scala> val f3:(Int)=>Int = m4
f3: Int => Int = <function1>
scala> //不期望出现函数的地方,方法并不会自动转换成函数
scala> val v3 = m4
<console>:8: error: missing arguments for method m4;
follow this method with `_‘ if you want to treat it as a partially applied function
val v3 = m4
利用这种自动转换,我们可以写出很简洁的代码,如下面这样:
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> def add(x:Int,y:Int)=x+y
add: (x: Int, y: Int)Int
scala> val a = add _
a: (Int, Int) => Int = <function2>
注意:val a = add _ : add与_之间有空格。
五、补充:函数的return
即时函数的return是返回到调用这个函数的块外部,而不是返回到函数返回点。
如果我们直接在一个继承于App的类中定义:
scala> val f = (x: Int) => return x; 2
<console>:11: error: return outside method definition
val f = (x: Int) => return x; 2
会提示我们:return outside method definition,现在我们把它放在一个方法中:
def outter: Int =
val f = (x: Int) => return x; 2
println("before.")
f(1)
println("after.")
3
println(outter)
输出:
before.
1
也就是f(1)后面的任何代码都没有执行,到了f(1)中的return 已经返回到它外部的outter而不是返回(x:Int)的返回点。
以上是关于详解scala中函数和方法的主要内容,如果未能解决你的问题,请参考以下文章