【Scala】Scala中的函数

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/gongxifacai_believe/article/details/81915350

1、函数的定义与调用

在Scala中定义函数时,需要定义函数的函数名、参数、函数体。
我们的第一个函数如下所示:

def sayHello(name: String, age: Int) = {
  if (age > 18) { printf("hi %s, you are a big boy\n", name); age } 
  else { printf("hi %s, you are a little boy\n", name); age 
}
sayHello("leo", 30)

Scala的函数包含:def关键字、函数名、参数列表、返回值类型、函数体。
注意:返回类型可以是任何有效的scala数据类型,参数列表将是用逗号和参数, 返回值类型列表分离变量是可选的。非常类似于Java。
Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,只要右侧的函数体中不包含递归的语句,Scala就可以自己根据右侧的表达式推断出返回类型。

2、在代码块中定义包含多行语句的函数体

单行的函数:

def sayHello(name: String) = print("Hello, " + name)

如果函数体中有多行代码,则可以使用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值。与Java中不同,不是使用return返回值的。
比如如下的函数,实现累加的功能:

def sum(n: Int) = {
  var sum = 0;
  for(i <- 1 to n) sum += i
  sum
}

3、递归函数与返回类型

如果在函数体内递归调用函数自身,则必须手动给出函数的返回类型。
例如,实现经典的斐波那契数列:
9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4; ….

def fab(n: Int): Int = {
  if(n <= 1) 1
  else fab(n - 1) + fab(n - 2)
}

4、带默认参数的函数

在Scala中,有时我们调用某些函数时,不希望给出参数的具体值,而希望使用参数自身默认的值,此时就定义在定义函数时使用默认参数。

def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName 

如果给出的参数不够,则会从左往右依次应用参数。

5、Java与Scala实现默认参数的区别

Java:

public void sayHello(String name, int age) {
  if(name == null) {
    name = "defaultName"
  }
  if(age == 0) {
    age = 18
  }
}
sayHello(null, 0)

Scala:

def sayHello(name: String, age: Int = 20) {
  print("Hello, " + name + ", your age is " + age)
}
sayHello("leo")

6、带名参数

在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。

sayHello(firstName = "Mick", lastName = "Nina", middleName = "Jack") 

还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数前面。

sayHello("Mick", lastName = "Nina", middleName = "Jack")

7、变长参数

在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数。Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用”*”来指明该参数为重复参数。

def sum(nums: Int*) = {
  var res = 0
  for (num <- nums) res += num
  res
}

sum(1, 2, 3, 4, 5)

8、使用序列调用变长参数

如果想要将一个已有的序列直接调用变长参数函数,是不对的。比如val s = sum(1 to 5)。此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别。这种语法非常有用!一定要好好注意,在spark的源码中大量地使用到了。

val s = sum(1 to 5: _*)

案例:使用递归函数实现累加

def sum2(nums: Int*): Int = {
  if (nums.length == 0) 0
  else nums.head + sum2(nums.tail: _*)
}
// String* 表示接收一系列的String类型的值,类似于Java中的可变参数
def printCourses(c:String*)={
    c.foreach(x=>println(x))
}
printCourses("hadoop","hive","hbase","spark")
printCourses("spark","storm")

// 在函数内部,变长参数的类型,实际为一个数组,比如上例的String*,实际为Array[String]。然而,如果你试图直接传入一个数组类型给这个参数,编译器会报错
val arr = Array("hadoop","hive","hbase","spark")
printCourses(arr)

// 为了避免这种情况,可以通过在变量后面添加_*来解决,这个符号告诉Scala编译器在传递参数时逐个传入数组的每个元素,而不是数组整体。
printCourses(arr:_*)

9、嵌套函数(内部函数)

def fun1(a: Int){
    def fun2(b: Int){
    println("fun1...fun2..." + (a + b))
    }
    fun2(100)
}

10、匿名函数

匿名函数没有函数名称,语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数会使代码变的更简洁。
匿名函数写法:

scala> (x: Int) => x + 1 
res5: Int => Int = <function1> 

标准函数写法:

def add2 = new Function1[Int,Int]{  
    def apply(x:Int):Int = x+1;  }

将函数作为参数进行传递,这种定义的方式我们叫做:高阶函数。

def add = (x: Int,y: Int) => x + y

def min = (x: Int , y: Int) => {
if(x < y)
x
else
y
}

匿名函数不但可以赋值给函数,也可以赋值给变量

val xx = (x: Int) => x + 1

匿名函数:

(x => println(x))

等同于这样的写法:

(x: String) => println(x)

11、过程

在Scala中,定义函数时,如果函数体直接包裹在了花括号里面,而没有使用=连接,则函数的返回值类型就是Unit。这样的函数就被称之为过程。过程通常用于不需要返回值的函数。过程还有一种写法,就是将函数的返回值类型定义为Unit。

def sayHello(name: String) = "Hello, " + name
def sayHello(name: String) { print("Hello, " + name); "Hello, " + name }
def sayHello(name: String): Unit = "Hello, " + name

12、lazy值

在Scala中,提供了lazy值的特性,也就是说,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用,比如打开文件进行IO,进行网络IO等。

import scala.io.Source._
lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

即使文件不存在,也不会报错,只有第一次使用变量时会报错,证明了表达式计算的lazy特性。

val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString
lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString
def lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

13、异常处理

在Scala中,异常处理和捕获机制与Java是非常相似的。

try {
  throw new IllegalArgumentException("x should not be negative")
} catch {
  case _: IllegalArgumentException => println("Illegal Argument!")
} finally {
  print("release resources!")
}
try {
  throw new IOException("user defined exception")
} catch {
  case e1: IllegalArgumentException => println("illegal argument")
  case e2: IOException => println("io exception")
}

猜你喜欢

转载自blog.csdn.net/gongxifacai_believe/article/details/81915350