专栏首页SmartSiScala 学习笔记之高阶函数

Scala 学习笔记之高阶函数

Scala混合了面向对象和函数式的特性.在函数式编程语言中,函数可以像任何其他数据类型一样被传递和操作.如果想要给算法传入明细动作时,只需要将明细动作包在函数当中作为参数传入即可.

1. 作为值的函数

在Scala中,函数就和数字一样,可以在变量中存放:

import scala.math._

val num = 3.14 // num: Double = 3.14
val fun = ceil _  // fun: Double => Double = <function1>
println(num) // 3.14
println(fun(num)) // 4.0

上述代码将num设置为3.14,将fun设置为 ceil 函数.num的类型为 Double,fun的类型为 (Double) => Double (即接受并返回Double的函数)

备注

ceil函数后的 _ 表示确实指的是ceil这个函数,而不是碰巧忘记了给它传递参数

可以对函数做如下两件事:

  • 调用它
  • 传递它 存放在变量中,或者作为参数传递给另一个函数

Example:

// 调用
fun(num) // 4.0
// 传递
Array(3.14, 2.14, 1.14).map(fun) // Array(4.0, 3.0, 2.0)

备注 map方法接受一个函数参数,将它应用到数组中的所有值,然后返回结果的数组

2. 匿名函数

在Scala中,不需要给每一个函数命名,就像不用给每个数字命名一样:

(x: Double) => 3 * x

上述代码表示该函数将传递给它的参数乘以3.

对与上述匿名函数我们可以如下操作:

(1) 可以将函数存放在变量中:

val triple = (x: Double) => 3 * x
triple(2) // 6.0

上述代码等价于:

def triple(x:Double) = 3 * x

(2) 可以不用命名直接将函数传递给另一个函数:

Array(3.14, 2.14, 1.14).map((x: Double) => 3 * x)

3. 带函数参数的函数

下面是一个接受一个函数作为参数的函数:

def valueAtOneQuarter(fun: (Double) => Double) = fun(0.25)
valueAtOneQuarter(sqrt _) // 0.5 即 sqrt(0.25)

上述函数的参数类型为 (Double) => Double,即接受任何 Double 并返回 Double 的函数.

4. 参数类型推断

当你将一个匿名函数传递给一个函数时,Scala会尽可能帮助你推断出类型信息.不需要将代码写成如下:

valueAtOneQuarter( (x:Double) => 3 * x ) // 0.75

由于valueAtOneQuarter方法知道你会传入一个类型为(x:Double) => Double的函数,可以将上述代码改为:

valueAtOneQuarter( (x) => 3 * x )

或者

valueAtOneQuarter( x => 3 * x ) // 只有一个参数的函数 可以省略括号

如果参数在=>右侧只出现一次,可以使用 _ 替换,因此进一步改写:

valueAtOneQuarter(3 * _)

5. 柯里化

柯里化是指将原来接受两个参数的函数变成一个新的接受一个参数的函数的过程.新的函数返回一个以原有第二个参数作为参数的函数.

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

以下函数接受一个参数,生成另一个接受单个参数的函数:

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

要计算两个数的乘积,需要调用:

mulOneAtATime(6)(7)

细分说明一下,mulOneAtATime(6)返回的是一个函数 (y: Int) => 6 * y.而这个函数又被应用到7,因此最终的结果为42.

Scala支持如下简写来定义柯里化函数:

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

我们可以看到多参数只是个虚饰,不是什么编程语言的特质.

6. 控制抽象

在Scala中,我们可以将一系列语句组成不带参数也没有返回值的函数.如下函数在线程中执行某段代码:

def runInThread(block: ()=>Unit){
  new Thread{
    override def run(){ block() }
  }.start()
}

上述函数为带函数参数的函数,函数参数类型为()=>Unit(表示没有参数也没有返回值).但是如此一来,当你调用该函数时,需要写不美观的()=>:

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

要想在调用中省掉()=>,可以使用换名调用表示法:在参数声明和调用该函数参数的地方略去(),但保留=:

def runInThread(block: => Unit){ // (1)
  new Thread{
    override def run() { block } // (2)
  }.start()
}

这样,我们如下调用:

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

Scala程序员可以构建控制抽象:看上去像编程语言的关键字的函数.例如,下面我们定一个until语句,工作原理类似while,只不过把条件反过来用:

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

使用:

var x = 4
until (x == 0) {
  println(x)
  x = x -1
}
// 4
// 3
// 2
// 1
var x = 0
until (x == 0) {
  println(x)
  x = x -1
}
// 无输出

这样的函数参数有一个专业术语:换名调用参数.和常规的参数不同,函数被调用时,参数表达式不会被求值.毕竟,在调用until时,不希望x == 0被求值得到false.与之相反,表达式成为无参函数的函数体,而该函数被当做参数传递下去. 仔细看一下until函数的定义.注意它是柯里化的:函数首先处理掉condition,然后把block当做完全独立的另一个参数.如果没有柯里化,调用就会变成如下:

until (x == 0, {...})

7. return表达式

在Scala中,不需要使用return语句返回函数值.函数的返回值是函数体的值.不过,可以使用return来从一个匿名函数中返回值给包含这个匿名函数的带名函数.这对于抽象控制是很有用的:

def indexOf(str: String, ch: Char) : Int = {
  var i = 0;
  until (i == str.length){
    if(str(i) == ch) return i
    i += 1
  }
  return -1
}

在这里,匿名函数{ if(str(i) == ch) return i; i += 1 }被传递给until.当return表达式被执行时,包含它的带名函数indexOf终止并返回给定的值.如果要在带名函数中使用return的话,则需要给出其返回类型.例如上例中,编译器没法推断出它会返回Int,因此需要给出返回类型Int.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Stream 主流流处理框架比较(2)

    在上篇文章中,我们过了下基本的理论,也介绍了主流的流处理框架:Storm,Trident,Spark Streaming,Samza和Flink。今天咱们来点有...

    smartsi
  • 4个步骤让Flink应用程序达到生产状态

    这篇文章阐述了 Flink 应用程序达到生产状态所必须的配置步骤。在以下部分中,我们概述了在 Flink 作业达到生产状态之前技术领导、DevOps、工程师们需...

    smartsi
  • Python 字符串操作

    字符串是 Python 中最常用的数据类型。我们可以使用引号(‘或”)来创建字符串。 创建字符串很简单,只要为变量分配一个值即可。例如:

    smartsi
  • Google C++编程风格指南(二)之函数的相关规范

    定义:内联函数是指用inline关键字修饰的函数。在类内定义的函数被默认成内联函数。

    Dabelv
  • 【玩转腾讯云】万物皆可Serverless之关于云函数冷热启动那些事儿

    然后我们再来看一下腾讯云云函数文档里的简介 https://cloud.tencent.com/document/product/583/9199

    乂乂又又
  • 快速学习-Scala函数式编程

    在scala中,函数式编程和面向对象编程融合在一起,学习函数式编程式需要oop的知识,同样学习oop需要函数式编程的基础。[矛盾] 关系如下图:

    cwl_java
  • python第十四课--排序及自定义函数

    1.排序 特点: 1).升序:从小到大 2).降序:从大到小 课堂实现选择排序:参看老郭选择排序.py文件 2.函数:(方法/method) 自定义...

    hankleo
  • 函数式编程与面向对象编程[1]: Lambda表达式 函数柯里化 高阶函数函数式编程与面向对象编程[1]: Lambda表达式 函数柯里化 高阶函数.md

    For example, in Lisp the 'square' function can be expressed as a lambda expressi...

    一个会写诗的程序员
  • 浅谈javascript中的回调函数javascript中的函数匿名函数回调函数回调函数的使用回调函数实例总结

    要理解javascript中的回调函数,首先我们就要对javascript中的函数有一定的理解,所以我们先从javascript中函数谈起,讲讲它与其他语言中的...

    desperate633
  • Oracle sql语句--单行函数、组函数、分组与过滤组信息

    函数分为系统内置函数自定义函数(后期学习的plsql 中定义);了解系统内置函数(方法),重点掌握 to_date 、 to_char (字符和日期的转...

    wolf

扫码关注云+社区

领取腾讯云代金券