【转】Scala学习——高阶函数

原文链接 http://nerd-is.in/2013-09/scala-learning-higher-order-functions/

原文发表于:http://nerd-is.in/2013-09/scala-learning-higher-order-functions/

在函数式编程语言中,函数是“头等公民”,可以像任何其他数据类型一样被传递和操作。

因为Scala混合了面向对象和函数式的特性,所以对Scala来说,函数是“头等公民”。

作为值的函数

1

2

3

import scala.math._

val fun = ceil _   // _将ceil方法转成了函数

在Scala中,无法直接操纵方法,只能直接操纵函数,所以需要使用_。

fun的类型是(Double)=>Double,意为接受Double参数并返回Double的函数。

能够对fun做的有:调用,传递。

1

2

3

4

5

val num = 3.14

fun(num)  // 返回4.0,调用fun

Array(3.14, 1.42, 2.0).map(fun)  //返回Array(4.0, 2.0, 2.0),将fun作为变量传递

匿名函数

函数不一定需要名称:

1

(x: Double) => 3 * x  // 该匿名函数将传给它的参数乘3

可以将匿名函数赋值给变量,也可以当参数传递

带函数参数的函数

如何实现一个接受另一个函数为参数的函数:

1

def valueAtOneQuarter(f: (Double) => Double) = f(0.25)

该函数的类型是: ((Double) => Double) => Double。

还有可以返回一个函数的函数:

1

2

3

4

5

def mulBy(factor: Double) = (x: Double) => factor * x

// mulBy可以产出任何两个数相乘的函数

val quintuple = mulBy(5)  // (x: Double) => 5 * x

quintuple(20)  // 5 * 20

这样接受函数参数,或者是返回函数的函数,被称为高阶函数(higher-order function)。

参数(类型)推断

前面有定义高阶函数 def valueAtOneQuarter(f: (Double) => Double) = f(0.25),

因为已知参数的类型,所以Scala会尽可能推断出类型,在传入参数时,可以省掉一些内容。

1

2

3

4

valueAtOneQuarter((x: Double) => 3 * x)  // 完整写法

valueAtOneQuarter((x) => 3 * x)  // 已知参数类型,可以省掉Double

valueAtOneQuarter(x => 3 * x)  // 只有一个参数时,可以省去()

valueAtOneQuarter(3 * _)  // 参数只在右侧出现一次,可以用_替换

闭包

闭包(closure)这个概念,虽然差不多能懂,但解释不清楚的感觉,参考一下闭包的维基百科

SAM转换

在Scala中,要某个函数做某件事时,会传一个函数参数给它。

而在Java中,并不支持传送参数。通常Java的实现方式是将动作放在一个实现某接口的类中,

然后将该类的一个实例传递给另一个方法。很多时候,这些接口只有单个抽象方法(single abstract method),

在Java中被称为SAM类型。

举例,点击一个按钮时,增加一个计数器:

1

2

3

4

5

6

7

8

var counter = 0

val button = new JButton("Increment")

button.addActionListener(new ActionListener {

override def actionPerformed(event: ActionEvent) {

count += 1

}

})

这是非常常见的,给按钮添加监听器的代码。

其实只要给addActionListener传一个函数参数,也就能够实现一样的功能了。

1

button.addActionListener((event: ActionEvent) => counter += 1)

为了使这个语法真的生效,需要提供一个隐式转换。

隐式转换将在21章详述。下面是简单的示例:

1

2

3

4

implicit def makeAction(action: (ActionEvent) => Unit) =

new ActionListener {

override def actionPerformed(event: ActionEvent) { action(event) }

}

将这个函数和界面代码放在一起,就可以在所有预期ActionListener对象的地方,传入(ActionEvent)=>Unit函数参数。

从上面的代码可以看出,隐式转换就是将一种类型自动转换成另外一种类型,是个函数。

因为在Scala中,函数是头等公民,所以隐式转换的作用也大大放大了。

柯里化(Currying)

柯里化的概念也请参考维基百科

柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。

新的函数返回一个以原有第二个参数作为参数的函数。

1

2

3

4

5

6

def mulOneAtATime(x: Int) = (y: Int) => x * y

// 计算两个数的乘积

mulOneAtATime(6)(7)

// 多参数的写法

def mul(x: Int, y: Int) = x * y

mulOneAtATime(6)返回的是函数(y: Int)=>6*y,再将这个函数应用到7,最终得到结果。

柯里化函数可以在Scala中简写:

1

def mulOneAtATime(x: Int)(y: Int) = x * y

多参数是个虚饰,不是编程语言的根本性的特质。

可以利用柯里化把某个函数参数单独拎出来,提供更多用于类型推断的信息。

1

2

3

val a = Array("Hello", "World")

val b = Array("hello", "world")

a.corresponds(b)(_.equalsIgnoreCase(_))

corresponds的类型声明如下:

1

def corresponds[B](that: GenSeq[B])(p: (T, B) ⇒ Boolean): Boolean

方法有两个参数,that序列和f函数,其中f函数有两个参数,第二个参数类型是与that序列一致的

因为使用了柯里化,我们可以省去第二个参数中B的类型,因为从that序列中推断出B的类型。

于是,_equalsIgnoreCase(_)这个简写就符合参数的要求了。

控制抽象

Scala中,可以将一系列语句归组成不带参数也没有返回值的函数。

1

2

3

4

5

6

7

8

def runInThread(block: () => Unit) {

new Thread {

override def run() { block() }

}.start()

}

// 调用

runInThread { () => println("Hi"); Thread.sleep(10000); println("Bye") }

可以去掉调用中的()=>,在参数声明和调用该函数参数的地方略去(),保留=>。

1

2

3

4

5

6

7

8

def runInThread(block: => Unit) {

new Thread {

override def run () { block }

}.start()

}

// 调用

runInThread { println("Hi"); Thread.sleep(10000); println("Bye") }

Scala程序员可以构建控制抽象:看上去像是编程语言关键字的函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

def until(condition: => Boolean)(block: => Unit) {

if (!condition) {

block

until(condition)(block)

}

}

// 使用

var x = 10

until (x == 0) {

x -= 1

println(x)

}

这样的函数参数专业术语叫做换名调用参数(常规的参数叫换值调用参数)。函数在调用时,换名调用参数的表达式不会被求值,表达式会被当做参数传递下去。

return表达式

一般不需要使用return来返回函数值。但是return可以用来从一个匿名函数中返回值给包含这个匿名函数的带名函数,对于控制抽象来说是很有用的。如果要在带名参数中使用return,需要在定义中给出返回类型。

1

2

3

4

5

6

7

8

def indexOf(str: String, ch: Char): Int = {

var i = 0

util (i == str.length) {

if ( str(i) == str.length) return i

i += 1

}

return -1

}

在这里,util这个抽象控制中的return语句,会使外部的带名函数indexOf终止并且返回i的值。

这里控制流程的实现依赖于在匿名函数中return表达式抛出的特殊异常。如果这个异常在被送往带名函数前被捕获,那么就无法为带名函数返回值了,这一点需要注意。



本章练习参考

时间: 2024-08-07 21:17:27

【转】Scala学习——高阶函数的相关文章

scala学习-高阶函数

/**  * 什么是高阶函数,高阶函数就是将函数作为参数传递给函数.  * 函数可以和值一样被定义给变量以及被传递  * 下面对王老师授课的代码进行了理解和研读  */ package com.fish import scala.math._ //下面这种写法是错误的 //import scala.math.* /**  * auth=fish  * qq=16616365  */ object HigherFuction {         /*       * 代码阅读       * 1 

Thinking in scala (6)----高阶函数----返回一个函数

在Thinking in scala (5)----高阶函数* 里面,我们演示了如何把一个函数作为参数传递给另外一个函数. 在本文里面,我们来演示函数式编程另外一个重要的特性:返回一个函数.首先来看这么一段代码: code piece 1: def sum(f:Int=>Int):(Int,Int)=>Int={ def sumF(a:Int,b:Int):Int= if(a>b) 0 else f(a)+sumF(a+1,b) sumF } 一点点来看,f:Int=>Int 是s

【Scala】高阶函数和柯里化

高阶函数 在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: - 接受一个或多个函数作为输入 - 输出一个函数 在数学中它们也叫做算子(运算符)或泛函.微积分中的导数就是常见的例子,因为它映射一个函数到另一个函数. 高阶函数的例子 假设有一个函数对给定两个数区间中的所有整数求和: def sumInts(a: Int, b: Int): Int = if(a > b) 0 else a + sumInts(a + 1, b) 如果现在要求连续整数的平方和: def square(x:

快学Scala(12)--高阶函数

在函数式编程语言中,你只需要将明细动作包在函数当中作为参数传入即可. 作为值的函数 import scala.math._ class TestFun { val num = 3.14 val fun = ceil _ } 这段代码将num设为3.14,fun设为ceil函数.ceil函数后面的_意味着你确实指的是这个函数,而不是碰巧忘记了给他送参数 Array(3.14, 1.42, 2.0).map(fun) //Array(4.0, 2.0, 2.0) map方法接受一个函数参数,将它应用

每天学一点Scala之 高阶函数 map

要清楚,map函数的本质是?或者说map函数到底是用来干什么的? 可以参考下面的例子: marathon源码,spark源码中大量使用了高阶函数map, 如下面是marathon的源码

Python 学习——高阶函数 filter 和 sorted

filter filter函数顾名思义,筛选,通过调用函数进行筛选序列中的满足函数的子项 以实例来说话: 过滤一个序列中所有的偶数,保留奇数 另如下,过滤掉一个序列中的所有空格以及空字符等信息 可以知道,filter函数传入了两个参数,第一个为函数,第二个为序列 sorted 排序也是在程序中经常用到的算法.无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小.如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽

每天学一点Scala之 高阶函数 flatten

flatten 就是将多个集合展开,组合成新的一个集合. 下面是marathon源码用到flatten的场景

Python学习十二:高阶函数

在学习高阶函数之前需要搞清一个概念: 变量可以指向函数 意思就是:把函数名赋值给另一个变量,然后使用这个变量完成跟函数一样的功能. 如下代码所示: 将求绝对值函数abs赋值给f,然后调用f完成跟abs一样的功能. >>> f = abs >>> f(-100) 100 >>> 好了接下来看:高阶函数 定义为:一个函数接收另一个函数作为参数,这种函数就称之为高阶函数. 如下代码所示: def add(x , y , f): return f(x) + f

2016.3.3(Spark框架预览,Scala部分应用函数、闭包、高阶函数,关于语义分析的一些心得)

一.Spark框架预览 主要有Core.GraphX.MLlib.Spark Streaming.Spark SQL等几部分. GraphX是进行图计算与图挖掘,其中主流的图计算框架现在有:Pregal.HAMA.Giraph(这几部分采用超步即同步的方式),而GraphLab与Spark GraphX采用异步的方式进行.它与Spark SQL进行协作时,一般是用SQL语句来进行ETL(Extract-Transform-Load数据仓库技术)然后交给GraphX处理. Spark SQL的前身