五、Scala的函数


1.方法

1.1 语法格式

def 方法名(参数名:参数类型, 参数名:参数类型) : [return type] = {
	//方法体
}

注意:

  • 参数列表的参数类型不能省略
  • 返回值类型可以省略,由scala编译器自动推断
  • 返回值可以不写return,默认就是{}块表达式的值

示例:
定义一个方法getMax,用来获取两个整型数字的最大值,并返回结果。

//写法一:普通版
def getMax(a:Int, b:Int):Int = {
	return if(a >= b) a else b
}

//写法二:精简版
//省略的地方:1.返回值的类型;2.返回值不写return;3.因为方法体只有一行代码,大括号也可以去掉
def getMax(a:Int, b:Int) = if(a >= b) a else b

//调用
val max = getMax(22, 11)
println(max) //22

1.2 返回值类型判断

Scala定义方法可以省略返回值的数据类型,由scala自动推断。但当定义递归方法时,不能省略返回值类型

示例:
定义递归方法,求阶乘

def factorial(n:Int):Int = if(n == 1) 1 else n * factorial(n - 1)

如果省略了呢?
在这里插入图片描述

1.3 惰性方法

当记录方法返回值的变量被声明为 lazy 时,方法的执行将被推迟,直到我们再次使用该值时方法才会执行,像这样的方法就叫做惰性方法。

注意:

  • Java中并没有提供原生态的“惰性”技术,但是可以通过特定的代码结构实现。这种结构称之为:懒加载/延迟加载
  • lazy不能修饰var类型的变量

使用场景:

  • 打开数据库连接 - 由于表达式执行代价昂贵,因此我们希望能推迟该操作,直到我们确实需要表达式结果值时才执行它
  • 提升某些特定模块的启动时间 - 为了缩短模块的启动时间,可以将当前不需要的某些工作推迟执行
  • 确保对象中的某些字段优先初始化 - 为了确保对象中的某些字段能优先初始化,我们需要对其他字段进行惰性化处理

示例:
定义一个方法用来获取两个整数和,通过“惰性”技术调用该方法,然后打印结果。

def getSum(a:Int, b:Int) = a + b
val sum1 = getSum(10, 20) //sum1: Int = 30
lazy val sum2 = getSum(10, 20) //sum2: Int = <lazy>
println(sum2) //结果为: 3

1.4 方法参数

1.4.1 默认参数

在定义方法时可以给参数定义一个默认值。

示例:
定义一个计算两个整数之和的方法,两个值默认是10和20;调用该方法,不传任何参数。

def getSum(x:Int = 10, y:Int = 20) = x + y
val sum = getSum()
println(sum)

1.4.2 带名参数

在调用方法时,可以指定参数的名称来进行调用。

示例:
定义一个计算两个整数之和的方法,两个值默认是10和20;调用该方法,只设置第一个参数的值。

def getSum(x:Int = 10, y:Int = 20) = x + y
val sum = getSum(x=1)
println(sum)

1.4.3 变长参数

如果方法的参数时不固定的,可以将该方法的参数定义成变长参数。

格式:

def 方法名(参数名:参数类型*):返回值类型 = {
	//方法体
}

注意:

  • 在参数类型后面加一个*号,表示参数可以是0个或多个
  • 一个方法有且只能有一个变长参数,并且变长参数要放到参数列表的最后边

示例:
定义一个计算若干值相加的方法;调用方法,传入以下数据:1,2,3,4,5

def getSum(numbers:Int*) = numbers.sum
val sum1 = getSum() //sum1: Int = 0
val sum2 = getSum(1, 2, 3, 4, 5) //sum2: Int = 15

1.5 方法调用方式

在编写spark、flink程序时,会经常用到这些方法调用方式。

1.5.1 后缀调用法

这种跟Java没区别。
语法:

对象名.方法名(参数)

示例:
调用Math.abs ,用来求绝对值。

Math.abs(-1) //Scala自动用res0去接收,即res0: Int = 10
println(res0) //10

1.5.2 中缀调用法

后缀调用法里如果小括号过多,容易混乱,所以引出中缀调用法
语法:

对象名 方法名 参数

注意 :如果有多个参数,使用括号括起来。

示例:
调用Math.abs ,用来求绝对值。

Math abs -1

扩展操作符即方法
在Scala中,所有操作符都是方法,操作符时一个方法名字是符号的方法。

1.5.3 花括号调用法

语法:

Math.abs{
	//表达式1
	//表达式2
}

注意 :方法只有一个参数,才能使用花括号调用法。

示例:
调用Math.abs ,用来求绝对值。

Math.abs{
	println("求绝对值!")
	-40
}
//结果为:
//求绝对值!
//res0: Int = 40

1.5.4 无括号调用法

如果方法没有参数,可以省略方法名后面的括号。

示例:
定义一个无参数的方法,打印“Hi”;使用无括号调用法调用该方法。

def sayHi() = println("Hi") //sayHi: ()Unit
sayHi() //结果为:Hi
sayHi //结果为:Hi

注意:

  • 在Scala中,如果方法的返回值类型是Unit类型,这样的方法称之为过程(procedure)
  • 过程的等号(=)可以省略不写
def sayHi2()  { println("Hi") } //sayHi: ()Unit
sayHi2() //结果为:Hi
sayHi2 //结果为:Hi

2.函数

scala支持函数式编程,将来编写Spark/Flink程序会大量使用函数。(Java里方法和函数是一回事,但Scala里面不是的)

2.1 定义函数

语法:

val 函数变量名 = (参数名:参数类型,参数名:参数类型...) => 函数体

注意:

  • Scala中函数是一个对象(变量)
  • 类似于方法,函数也有参数列表和返回值
  • 函数定义不需要使用def定义
  • 无需指定返回值类型

示例:
定义一个计算两个整数之和的函数,并调用该函数。

val getSum = (a:Int, b:Int) => a + b //getSum: (Int, Int) => Int = <function2>
val sum = getSum(11, 22) //sum: Int = 33

2.2 方法和函数的区别

Scala中函数和方法的具体区别如下:

  • 方法是隶属于类或者对象的,在运行时,它是加载到JVM的方法区中;函数对象赋值给一个变量,在运行时,它是加载到JVM的堆内存中
  • 函数是一个对象,继承自FunctionN,函数对象有apply, curried, toString, tupled这些方法;方法则没有
  • 结论:Scala中函数是对象,而方法是属于对象的,所以理解为:方法归属于函数

示例:
演示方法无法赋值给变量

//1.定义方法
def add(x:Int, y:Int) = x + y

//2.尝试将方法赋值给变量
//val a = add(1, 2) //错误,这样是在“调用方法”,而不是把方法赋值给变量
val a = add

//3.上述代码会报错
<console>:12: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
       val a = add
//原因是:方法不是一个对象,不能直接赋值给变量a

2.3 方法转换为函数

有时需要将方法转换为函数,比如:作为变量传递。

格式:

val 变量名 = 方法名 _ //格式为:方法名+空格+下划线

示例:
定义一个方法用来计算两个整数之和;将该方法转换为一个函数,并赋值给变量。

//1.定义一个方法用来计算两个整数之和
def add(x:Int, y:Int) = x + y
//2.将该方法转换为一个函数,并赋值给变量
val a = add _ //a: (Int, Int) => Int = <function2>
//3.调用函数,用来获取两个整数之和
val result = a(1, 2)

3. 案例:打印nn乘法表

方式一:通过方法实现

//1.定义一个方法,接受一个整型参数
def printMT(n:Int) = { //Multiplication Table
	//2.通过for循环嵌套实现,根据传入的整数,打印对应的乘法表
	for(i <- 1 to n; j <- 1 to i) {
		print(s"${j} * ${i} = ${j * i}\t")
		if(j==i) println()
	}
}
//3.调用方法
printMT(5)

//优化版
def printMT(n:Int) = for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}\t" + (if(j==i) "\r\n" else "\t"))

方法二:通过函数实现

val printMT = (n:Int) => {
	for(i <- 1 to n; j <- 1 to i) {
		print(s"${j} * ${i} = ${j * i}\t")
		if(j==i) println()
	}
}
printMT(5)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值