已复制
全屏展示
复制代码

Scala函数与方法总结


· 5 min read

一. 函数调用

Scala 中使用 val 语句可以定义函数,def 语句定义方法。但是在实际应用中,往往会忽略函数和方法的区别而混用了。

class Test{
  def m(x: Int) = x + 3     // 方法
  val f = (x: Int) => x + 3 // 函数
}

概念: 当⼀个函数⽆需输⼊参数时,我们称这个函数为 “0参函数“,定义⼀个0参函数时,你可以加⼀对空括号,也可以省略掉括号,直接写函数体。

方法调⽤(注意:是方法,不是函数):

  • 如果你在定义0参方法时加了括号,则在调⽤时可以加括号或者省略括号。
  • 但当你在定义0参方法时没加括号,则在调⽤时不能加括号,只能使⽤函数名。
def m() = {
  3
}
println(m()) // 3
println(m)   // 3

def f = {
  3
}
println(f)   // 3

函数调用:定义时有括号,调用是必须有括号。定义时没有括号,调用时必须没有括号。

val fu1 = () => {
  100
}
println(fu1()) // 100

val fu2 = {
  100
}
println(fu2) // 100

二. 函数定义

  • 函数(或方法)内定义函数(或方法),和Python一样,也可以在函数内部定义新的函数,新函数的作用域只在当前函数有效
def calcSalary() = {
    def getSalary() = {
        10000
    }
    getSalary * 1.1
}

println(calcSalary)
  • 定义函数的方式
// 最简单的写法(指定参数、返回值的类型)
val add6: (Int, Int) => Int = (x: Int, y: Int) => x + y


// 最简单的写法(省略参数、返回值的类型)
val add7 = (x: Int, y: Int) => x + y


// 简版的函数定义,这里的待填空下划线必须给定类型,否则报错。
val add8 = (_: Int) + (_: Int)
add8(3, 4)
res4: Int = 7
  • 定义方法的方式
// 标准写法
def add1(x: Int, y: Int): Int = {
return x + y
}


// 返回 x+y
def add2(x: Int, y: Int): Int = {
x + y
}


// 返回 Int
def add3(x: Int, y: Int) = {
x + y
}


// 返回 Unit
def add3(x: Int, y: Int) {
x + y
}


// 无参函数
def getNumber(): Int = 3
def getNumber: Int = 3
def getNumber = 3


// 函数只有一行的写法
def add4(x: Int, y: Int): Int = x + y


// 函数只有一行的写法
def add5(x: Int, y: Int) = x + y

三. 函数作为参数

val n = List(-11, -10, -5, 0, 5, 10)

// 返回大于 0 的数据
n.filter((x: Int) => x > 0)

// 返回大于 0 的数据(简写版本)
n.filter((x) => x > 0)

// 返回大于 0 的数据(简写版本)
n.filter(_ > 0)

四. 部分应用函数

部分应用函数(partially applied function),简单的来说,其实就是先给函数提前确定一些参数的值,这样一来就形成了新的函数,新函数里面会去调用原来的函数,传入原来格式的参数。

部分应用的函数是一个表达式,在这个表达式中,并不给出函数需要的所有参数,而是给出部分,或完全不给。举例来说,要基于 sum创建一个部分应用的函数,假如你不想给出三个参数中的任何一个,可以在"sum"之后放一个下画线,这将返回一个函数,可以被存放到变量中。

def sum(a: Int, b: Int, c: Int) = a + b + c
val a = sum _
a(1, 2, 3)
res1: Int = 6

部分应用函数之所以叫作部分应用函数,是因为你并没有把那个函数应用到所有入参。拿sum _来说,你没有应用任何入参。不过,完全可以通过给出一些必填的参数来表达一个部分应用的函数,比如

val b = sum(1, _: Int, 3)
b: Int => Int = $$Lambda$796/914175168@4ce25e47
b(5)
res2: Int = 9

五. 可变参数列表

要表示这样一个重复参数,需要在参数的类型之后加上一个星号(*)。在函数内部,这个重复参数的类型是一个所声明的参数类型的 Array。因此,在 echo 函数内部,args 的类型其实是 Array[String]

// 这样定义以后,echo可以用到零到多个String参数调用
def echo(args: String*) = for (arg <- args) println(arg)

// 尽管如此,如果你有一个合适类型的数组,并尝试将它作为重复参数传入时,你将得到一个编译错误
var arr = Array("What", "are", "you", "doing?")
echo(arr)

// 要完成这样的操作,你需要在数组实参的后面加上冒号和一个_*符号
echo(arr: _*)


// java 中定义可变参数方法
public static void f(int... args) {
    for (int i : args) {
        System.out.println("i = " + i);
    }
}

六. 带默认值的命名参数

带默认值的参数让你可以用不同的顺序将参数传给函数。其语法是简单地在每个实参前加上参数名和等号。位置参数在前,其他的命名参数可以随意调换位置。和Python类似。

def goto_classroom(datetime: String, address: String="room1", username: String="yourself") = {
    println(datetime, address, username)
}

goto_classroom("now")
(now,room1,yourself)

goto_classroom("now", username="xiaohong", address="bju")
(now,bju,xiaohong)

七. Scala 函数柯里化(Currying)

函数的柯里化可以固定一些参数,让函数的调用更简单。

首先我们定义一个函数如下:

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

那么我们应用的时候,应该是这样用:add(1,2),现在我们把这个函数变一下形,那么我们应用的时候,应该是这样用:add(1)(2),最后结果都一样是3,这种方式(过程)就叫柯里化。

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

固定函数参数的方式示例

def add(x: Int)(y: Int)(z: Int) = x + y + z
val add1 = add(4)_  // 固定了x为4
val add2 = add1(5)  // 固定了x为4, y为5

println(add(4)(5)(6))  // 返回 15
println(add1(5)(6))    // 返回 15
println(add2(6))       // 返回 15
🔗

文章推荐