现在spark太火了,正在学习spark,但是spark是scala开发的,使用时虽然也可以用java,但是为了更好了解spark,现在学习下scala,网上找到Scala教程|菜鸟教程,快速浏览了解了一下,看到函数的时候,感觉函数挺灵活的,现在自己总结下。
函数定义
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}
1明确的返回类型有return
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
var s = add(1,2);
println(s);
}
def add(x:Int,y:Int) : Int={
return x+y
}
}
结果输入:3
2明确的返回类型无return
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
var s = add(1,2);
println(s);
}
def add(x:Int,y:Int) : Int={
x+y
x*y;
}
}
结果输入:2
说明,默认把最后执行的代码的结果返回
3不写返回类型
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
var s = add(1,2);
println(s);
println(add(2,3));
}
def add(x:Int,y:Int) ={
if(x == 1)
x+y
else
println("x>1")
}
}
输出:
3
x>1
()
说明:println的定义是def println(x: Any): Unit,Unit相当于java的void,所以说,如果没写明确的返回值,又有=(add(x:Int,y:Int)=),那也就是说返回值类型由最后返回的类型决定
这种类型,如果函数体中的代码较少,可以简写
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
var s = add(1,2);
println(s);
println(add(2,3));
}
def add(x:Int,y:Int) = if(x == 1) x + y else x*y
}
4不写返回类型也无等号
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
var s = add(1,2);
println(s);
}
def add(x:Int,y:Int) {
x+y
}
}
结果输出:()
说明:如果不声明返回类型也无等号,就是相当于:Unit=
5函数传值调用(call-by-value)和传名调用(call-by-name)
Scala的解释器在解析函数参数(function arguments)时有两种方式:
传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
package com.fei
object Add {
def addByName(a: Int, b: => Int) = a + b
def addByValue(a: Int, b: Int) = a + b
}
语法上可以看出,使用传名调用时,在参数名称和参数类型中间有一个=》符号。
以a为2,b为2 + 2为例,他们在Scala解释器进行参数规约(reduction)时的顺序分别是这样的:
addByValue(2, 2 + 2)
->addByValue(2, 4)
->2 + 4
->6
addByName(2, 2 + 2)
->2 + (2 + 2)
->2 + 4
->6
传名调用是在函数内部进行参数表达式的值计算的。这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
delayed(time());
}
def time() = {
println("获取时间,单位为纳秒")
System.nanoTime
}
def delayed( t: => Long ) = {
println("在 delayed 方法内")
println("参数: " + t)
t
}
}
输出结果:
在 delayed 方法内
获取时间,单位为纳秒
参数: 7308678932444
获取时间,单位为纳秒
t:=>Long,有=>表明t是传名调用,在delayed内部执行t时再真正执行t内部的代码块,在delayed内部出现了2次t,所以执行了2次t的内部代码块.
搞java出身的,看到t这样的调用方式,挺不习惯,可以将代码这样修改下。
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
delayed(time);
}
def time() = {
println("获取时间,单位为纳秒")
System.nanoTime
}
def delayed( t: ()=> Long ) = {
println("在 delayed 方法内")
println("参数: " + t())
t()
}
}
6:=>Unit和():=>Unit例子
其实他们都是传名调用,:=>是说这是一个返回结果是空的表达式,所以在函数内调用它时直接参数名即可;
():=>Unit是说这是一个函数,所以在函数内部调用它时,不能直接写函数名,还需加上()。
传参时也有区别,看例子
package com.fei
object FuncTest {
def main(args: Array[String]): Unit = {
z1(x())
z2(x)
}
def x() {
println("x内部...");
}
def z1(a : => Unit){
println("z1内部start")
a
println("z1内部end")
}
def z2(a : ()=>Unit){
println("z2内部start")
a()
println("z2内部end")
}
}
我们知道scala其实也是运行在jvm中的,也就是最终编译成class文件,那我们反编译class文件看看,z1,z2有区别吗?
package com.fei;
import scala.Function0;
import scala.Predef.;
import scala.Serializable;
import scala.runtime.AbstractFunction0.mcV.sp;
import scala.runtime.BoxedUnit;
public final class FuncTest$
{
public static final MODULE$;
static
{
new ();
}
public void main(String[] args)
{
z1(new AbstractFunction0.mcV.sp() { public static final long serialVersionUID = 0L;
public final void apply() { apply$mcV$sp(); }
public void apply$mcV$sp() { FuncTest..MODULE$.x(); }
});
z2(new AbstractFunction0.mcV.sp() { public static final long serialVersionUID = 0L;
public final void apply() { apply$mcV$sp(); }
public void apply$mcV$sp() { FuncTest..MODULE$.x(); } } );
}
public void x() {
Predef..MODULE$.println("x内部...");
}
public void z1(Function0<BoxedUnit> a)
{
Predef..MODULE$.println("z1内部start");
a.apply$mcV$sp();
Predef..MODULE$.println("z1内部end");
}
public void z2(Function0<BoxedUnit> a) {
Predef..MODULE$.println("z2内部start");
a.apply$mcV$sp();
Predef..MODULE$.println("z2内部end");
}
private FuncTest$() { MODULE$ = this; }
}
看z1和z2的参数,没区别
7指定函数参数名
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数,实例如下
package com.fei
object FuncTest {
def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}
输出
Value of a : 7
Value of b : 5
8可变参数
Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:
package com.fei
object FuncTest {
def main(args: Array[String]) {
printStrings("Runoob", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
}
}
输出
Arg value[0] = Runoob
Arg value[1] = Scala
Arg value[2] = Python
9默认参数值
def main(args: Array[String]) {
println( "返回值 : " + addInt() );
}
def addInt( a:Int=5, b:Int=7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
10函数嵌套
我么可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。以下实例我们实现阶乘运算,并使用内嵌函数:
def main(args: Array[String]) {
println( factorial(0) )
println( factorial(1) )
println( factorial(2) )
println( factorial(3) )
}
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)
}
输出
1
1
2
6
11匿名函数
def main(args: Array[String]) {
println( "multiplier(1) value = " + multiplier(1) )
println( "multiplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
输出
multiplier(1) value = 3
multiplier(2) value = 6
12函数柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。首先我们定义一个函数:
def add(x:Int,y:Int)=x+y
那么我们应用的时候,应该是这样用:add(1,2)
现在我们把这个函数变一下形:
def add(x:Int)(y:Int) = x + y
那么我们应用的时候,应该是这样用:add(1)(2),最后结果都一样是3,这种方式(过程)就叫柯里化。
实现过程
add(1)(2) 实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值。
实质上最先演变成这样一个方法:
def add(x:Int)=(y:Int)=>x+y
那么这个函数是什么意思呢? 接收一个x为参数,返回一个匿名函数,该匿名函数的定义是:接收一个Int型参数y,函数体为x+y。现在我们来对这个方法进行调用。
val result = add(1)
返回一个result,那result的值应该是一个匿名函数:(y:Int)=>1+y
所以为了得到结果,我们继续调用result。
val sum = result(2)
最后打印出来的结果就是3。
完整实例
下面是一个完整实例:
object Test {
def main(args: Array[String]) {
val str1:String = "Hello, "
val str2:String = "Scala!"
println( "str1 + str2 = " + strcat(str1)(str2) )
}
def strcat(s1: String)(s2: String) = {
s1 + s2
}
}
输出结果为:
str1 + str2 = Hello, Scala!