JAVA中最核心的概念是类、对象和方法,是完全面向对象的;而对于Scala来说则完全不同,Scala即支持面向对象,也支持面向过程编程。也就意味着Scala中的函数可以脱离类和对象对立存在。Scala中的函数式编程是其面向过程的重要特征,相对JAVAScala具备了更强大的功能特性。接下来分析一下Scala中的函数式编程。

 

函数赋值

Scala中的函数其地位与类、对象等价,是可以独立存在的。因此可以直接将函数赋值给变量。根据语法约定,函数赋值时在函数后面必须加上空格和下划线。

var data = "<html><h1class='detail'>proxy list</h1></html>"

// 定义函数,函数不依赖类与对象

def xpathSelect(data: String) {

    println("xpathSelect: " + data)

} // 返回xpathSelect: (data: String)Unit

// 将函数赋值给变量

val xpfuc = xpathSelect _ 

// 返回xpfuc: String => Unit = <function>

// 直接通过变量名调用函数

xpfuc(data) 

// 返回xpathSelect: <html><h1>proxylist</h1></html>

 

匿名函数

Scala中的函数也可以不命名,这类函数也称为匿名函数。跟许多现代语言比如Go非常类似,可以将定义的匿名函数赋值给变量,也可以将匿名函数传入其他函数中。Scala中定义匿名函数语法规则为:(参数名参数类型) => { 函数体 }

val convertMsgFmt = (msg: String) => {

//此处通过=>定义匿名函数,打印消息内容

    printf("Content: %s", msg)

} // convertMsgFmt: String => Unit =<function>

convertMsgFmt("Short Message forTest") 

// Content: Short Message for Test

 

高阶函数

Scala中可以将函数作为入参传入其他函数。而这类接收函数作为入参的函数,一般称作高阶函数(Higher-order Function),形式如下:

def execute(func: (String) => Unit,args: String) { 

// 第一个入参为函数,第二个为String

    func(args)  // 执行传入的函数

}

execute(convertMsgFmt, data) 

 

高阶函数也可以将函数作为返回值:

def generateFunc(funcName: String) =(args: String) => {

    println(funcName + ":" + args)

}

val g = generateFunc("g") 

// 此处返回值即为函数,赋值给变量g

g("String")

 

高阶函数的类型推断

Scala支持参数的自动类型推断,不需要明确类型;对于单一参数的函数,可以省略小括号;对于单一参数且在函数体中只使用一次的,则可以将接收参数省略并用”_”符号来替代。

def execute(func: (String) => Unit,args: String) { func(args) }

execute((args: String) =>println("args:" + args), "method1")

// 此行省略参数类型,由编译器自动推断

execute((args) =>println("args:" + args), "method2")

// 此行简化匿名函数形式

execute(args =>println("args:" + args), "method3")

def triple(func: (Int) => Int) = {func(9) }

triple(9 * _) // 此行用”_”符号代替入参

 

常用高阶函数

map函数:对传入的每个元素都进行映射并返回处理后的结果。

Array(2, 3, 5, 8, 13).map(7 * _)

 

foreach函数:对传入的每个元素进行遍历(无返回值)。

(5 to 10).map("*" *_).foreach(println _)

 

filter函数:对传入的每个元素作过滤,如条件判断为true则保留该元素,否则过滤掉。

(11 to 99).filter(_ % 3 == 0)

 

reduceLeft函数:从集合左侧元素开始执行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推。

(3 to 6).reduceLeft( _ * _)

// 这个操作就相当于3 * 4 * 5 * 6

 

sortWith函数:对集合中的元素执行排序

Array(11, 2, 5, 7, 19, 31).sortWith(_ <_)

 

闭包

闭包是指某变量不处于其有效作用域时,函数仍能够对变量进行访问,即为闭包。

def getExecuteFunc(key: String) =

    (value:String) => println(key + ":" + value)

val executeDownload =getExecuteFunc("Download")

val executeCallback =getExecuteFunc("Callback")

// Download:http://github.com/5677 

executeDownload("http://github.com/5677")

// Callback:10.224.17.1:5500

executeCallback("10.224.17.1:5500")


代码中两次调用getExecuteFunc函数,通过传入不同的key来创建不同的函数返回。例子中的key只是局部变量,但在getExecuteFunc执行完之后,仍继续存在于创建的函数之中并且反复调用。这个变量key超出作用域仍可使用的情况,即为闭包的含义。Scala中通过为每个函数创建对象来实现闭包,实际上对于getExecuteFunc创建的函数,key是作为函数对象的变量隐式存在的,因此每个函数会拥有不同的key变量。Scala通过编译器会确保闭包机制的实现。

 

SAM转换

JAVA中不支持直接将函数传入方法中作为入参,如果有类似需求,那么唯一的解决方案是定义一个实现了某接口的类的实例,该实例中仅包含单一方法;这些实现的接口都只包含单一抽象方法,也就是Single Abstract MethodSAM)。由于ScalaJAVA都是基于JVM实现,底层可以调用JAVA,因此调用Java方法时可能就不得不创建SAM传递给对应方法。但Scala本身是支持传递函数的,因此可以使用Scala提供的隐式SAM转换功能,将SAM转换为Scala函数。

 

Curring函数

Curring函数是指对函数变形,将接收两个入参的函数,转换为两个关联的函数;第一个函数接收第一个入参,返回接收第二个入参的第二个函数。因此在函数调用的过程中,其形式就变化为两个函数连续调用的形式。

def sum(begin: Int, end: Int)

    = (begin toend).reduceLeft( _ + _)

sum(1, 10) // Int = 55

 

def sumRange(begin:Int):Int=>Int

    =(end:Int) => (begin to end).reduce(_ + _)

sumRange(5)(10) // Int = 45

 

def sumSeprate(begin: Int)(end: Int)

    =  (begin to end).reduce(_ + _)

sumSeprate(10)(20) // Int = 165

 

return关键字

Scala语法中不使用return返回函数值,默认函数最后一行即为返回值。一般来说return通常用于匿名函数中返回给包含匿名函数的命名函数,并作为命名函数的返回值。使用return的匿名函数必须明确给出返回类型,否则将编译失败。参考以下示例:

def invoke(method: String) = {

 def execute(method: String):String = {

     // 使用return匿名函数须明确给出返回类型

     return "Execute " + method

  }

  execute(method)

}

invoke("callback()")

// String =Execute callback()