Scala基础

Stella981
• 阅读 491

1. 介绍

柯里化(currying, 以逻辑学家Haskell Brooks Curry的名字命名)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。 在Scala中方法和函数有细微的差别,通常编译器会自动完成方法到函数的转换。如果想了解Scala方法和函数的具体区别,请参考博文Scala基础 - 函数和方法的区别

2. Scala中柯里化的形式

Scala中柯里化方法的定义形式和普通方法类似,区别在于柯里化方法拥有多组参数列表,每组参数用圆括号括起来,例如:

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

multiply方法拥有两组参数,分别是(x: Int)和(y: Int)。
multiply方法对应的柯里化函数类型是:

Int => Int => Int

柯里化函数的类型声明是右结合的,即上面的类型等价于:

Int => (Int => Int)

表明该函数若只接受一个Int参数,则返回一个Int => Int类型的函数,这也和柯里化的过程相吻合。

3. 探究柯里化函数

我们仍以上面定义的multiply方法为例探索柯里化的一些细节:

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

上面的代码定义了一个柯里化方法,在Scala中可以直接操纵函数,但是不能直接操纵方法,所以在使用柯里化方法前,需要将其转换成柯里化函数。最简单的方式是使用编译器提供的语法糖:

val f = multiply _

返回的函数类型是:

Int => Int => Int

使用Scala中的部分应用函数(partially applied function)技巧也可以实现转换,但是请注意转后后得到的并不是柯里化函数,而是一个接受两个(而不是两组)参数的普通函数:

val f = multiply(_: Int)(_: Int)

转后后得到的类型为:

(Int, Int) => Int

其实就是一个接受两个Int型参数的普通函数类型。

先传入第1个参数:

val f1 = f(1)

返回类型为:

Int => Int

即一个接受一个Int参数返回Int类型的函数。 继续传入第2个参数:

val f2 = f1(2)

返回类型为:

Int

两组参数都已经传入,返回一个Int类型结果。

4. 柯里化(currying)函数和部分应用函数(partial applied)的区别

下面代码定义一个普通方法multiply1和一个currying方法multiply2,并将其转换相应的函数类型:

def multiply1(x: Int, y:Int, z:Int) = x * y * z
val partialAppliedMultiply = multiply1 _
//类型:(x: Int, y: Int, z: Int) => Int

def multiply2(x: Int)(y: Int)(z: Int) = x * y * z
val curryingMultiply = multiply2 _
//类型:Int => (Int => (Int => Int))

在调用时,curryingMultiply可以依次传入各个参数,而partialAppliedMultiply在传入部分参数时,必须显示指定剩余参数的占位符:

val curryingMultiply1 = curryingMultiply(1)
//类型:Int => (Int => Int)

val partialAppliedMultiply1 = partialAppliedMultiply(1, _:Int, _: Int)
//类型:(Int, Int) => Int

另外,curryingMultiply1的类型仍然是currying类型,而partialAppliedMultiply1的类型仍然是普通函数类型。

5. 应用:控制抽象(Control Abstraction)

5.1 控制抽象介绍

对于一些通用的操作可以实现成控制抽象,例如像文件打开、关闭操作。实现成控制抽象的好处是,可以在使用的时候,看起来更像是语言级别提供的功能。

5.2 抽象控制的实现基础

5.2.1 无参函数

无参函数的类型是() => T,在使用时为了简化可以省略(),例如:

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

这样定义之后,在使用的时候就可以省略() =>,

  runInThread{
    println("Hi")
  }

5.2.2 使用{}替代()

如果方法只有一个参数,则可以使用{}替代(),例如:

  runInThread{
    println("Hi")
  }

5.2.3 传名参数(by-name parameter)

与传名参数相对的是传值参数。传值参数在函数调用之前表达式会被求值,例如Int,Long等数值参数类型;传名参数在函数调用前表达式不会被求值,而是会被包裹成一个匿名函数作为函数参数传递下去,例如高阶函数的函数参数就是传名参数。

5.3 控制抽象示例

withPrintWriter是一个柯里化方法,它接受两组参数,第1组参数是待操作的文件资源,第2组参数是操作文件资源的函数:

def withPrintWriter(file: File)(op: PrintWriter => Unit) {
    val writer = new PrintWriter(file)
    try {
      op(writer)
    } finally {
      writer.close()
    }
}

用法如下:

withPrintWriter(new File("date.txt")) {
    writer => writer.println(new java.util.Date)
}

withPrintWriter确保文件资源在被使用之后一定会被关闭,并且在使用的时候,看起来更像是语言内置的关键字函数。

6. 参考

  1. Programming in Scala, 2nd Edition
  2. 快学Scala
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Souleigh ✨ Souleigh ✨
3年前
初识 JS 中的柯里化
作为函数式编程语言,JS带来了很多语言上的有趣特性,比如柯里化。1.简介柯里化(Currying),又称部分求值(PartialEvaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函
菜园前端 菜园前端
11个月前
什么是函数柯里化?
原文链接:什么是函数柯里化?函数柯里化(HaskellBrooksCurry),当一个函数有多个参数的时候先传递一部分参数并且调用它(这部分参数后续不会进行改变),然后返回一个新的函数接收剩余的参数并返回结果。总结柯里化可以让我们给一个函数传递较少的参数得
Wesley13 Wesley13
2年前
mysql中时间比较的实现
MySql中时间比较的实现unix\_timestamp()unix\_timestamp函数可以接受一个参数,也可以不使用参数。它的返回值是一个无符号的整数。不使用参数,它返回自1970年1月1日0时0分0秒到现在所经过的秒数,如果使用参数,参数的类型为时间类型或者时间类型的字符串表示,则是从1970010100:00:0
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Stella981 Stella981
2年前
Python time模块 返回格式化时间
常用命令  strftimetime.strftime("%Y%m%d%H:%M:%S",formattime)第二个参数为可选参数,不填第二个参数则返回格式化后的当前时间日期201812112:00:00time.strftime('%H:%M:%S')返回当前时间的时分秒time.strftim
Wesley13 Wesley13
2年前
PHP中的NOW()函数
是否有一个PHP函数以与MySQL函数NOW()相同的格式返回日期和时间?我知道如何使用date()做到这一点,但是我问是否有一个仅用于此的函数。例如,返回:2009120100:00:001楼使用此功能:functiongetDatetimeNow(){