”函数柯里化”是指将多变量函数拆解为单变量的多个函数的依次调用, 可以从高元函数动态地生成批量的低元的函数。可以看成一个强大的函数工厂,结合函数式编程,可以叠加出很BT的能力。下面给出了两个示例,说明如何使用 Currying 用一行代码计算任意指数的多项式的和; 以及使用 Currying 实现一个简单的文件处理框架。

举例一: 计算任意指数的多项式的和 sum(n, m) = 1^m + 2^m + ... + n^m

定义如下多变量的函数就可以解决

  1. def polynomialSum2(m:Int, n:Int):Long = {
  2. return 1.to(n).toList.map(pow(_,m)).sum.asInstanceOf[Long];
  3. }

为什么要使用柯里化呢? 因为柯里化不仅仅可以求出最终的值,还可以生成批量的函数,复用这些函数。比如

  1. def polynomialSum(m: Int)(list: List[Int]): Long = {
  2. return list.map(pow(_,m)).sum.asInstanceOf[Long];
  3. }
  4.  
  5. i = 2
  6. val listPolySum = polynomialSum(i)(_)

当我们对第一个变量赋值后,返回得到的是一个平方和函数 listPolySum = 1^2 + 2^2 + ... + n^2 , 可以研究这个函数的性质而不仅仅是求值。

  注意到, Currying 的过程中,参数的顺序是有讲究的。一般, 函数参数建议放在前面,按照想要调用的顺序; 数据参数放在后面;最易变的参数放在最后面。如果这个函数写成以下形式,求值还是一样的, 但是生成的函数就不一样了, 假设第一个变量赋值为 list[1:10], 那么生成的函数就变成:

  generateFunc = 1^m + 2^m + ... + 10^m , 没有研究价值了。

  1. def polynomialSumNotGood(list: List[Int])(m:Int):Long = {
  2. return list.map(pow(_,m)).sum.asInstanceOf[Long];
  3. }

举例二: Currying 实现简易的文件处理框架

  我们不是打算成为数学家的或计算机科学家的,那么 Currying 对实际的软件开发有什么用处呢? 细细想来还是有的。通过在 Currying 的参数中传入函数,可以做一些简易的微框架。假设要做一个简易的文件处理框架: 1.  路径处理器 filePathHandler: 通过解析文件路径名生成一个文件名列表; 2. 文件名过滤器 fileFilterHander: 指定条件从文件名列表中筛选出需要的文件; 3. 文件处理器 fileHandler: 处理每一个文件生成一个列表; 4. totalHandler:  汇总所有列表的值并进行处理。可以对 (2) 进行扩展,使之成为一个过滤器处理链;可以对 (3) 进行扩展, 使之成为一个文件处理器链。 代码如下:

  1. /* a simple frame for processing files */
  2. def handleFiles(filePathHandler:(String) => List[String])
  3. (fileFilterHandler: (String) => Boolean)
  4. (fileHandlerList: List[(String)=>List[Any]])
  5. (totalHandler: List[Any] => Any)
  6. (filepath:String): Any = {
  7. return totalHandler(
  8. fileHandlerList.map(
  9. (handle:(String)=>List[Any]) =>
  10. filePathHandler(filepath).filter(fileFilterHandler(_))
  11. .map(handle(_)).flatten
  12. )
  13. )
  14. }

  

  怎么理解这段代码呢? 最主要的是文件处理器链的 map 。这里为了表达形式的"简洁",牺牲了点"可读性"。最重要的是理解 map 函数: map 的参数是一个函数,该函数以列表元素为参数。list.map(func(_)) 函数是将函数 func 应用到一个列表的所有元素, 从而得到另一个列表 [func(list[0]), func(list[1]), ..., func(list[n-1])], 即: list.map(func(_)) = for (e <- list) { func(e) }; 举个更简单的例子: list.map((x:Int) => 2*x) , map 里是一个 lambda 表达式, x 表示 list 的每个元素;

  如果 list 中的元素是函数呢 ?  首先, 将函数定义抽取出来 (String) => List[Any] , 这就是文件处理器链中每个元素的类型, 针对这个元素类型写一个函数:

  (handle:(String)=>List[Any]) => do something using handle function.

确实有点大胆!连我自己都惊呆了! 怎么可以让列表中的元素存储函数, 再去 map 呢! 哪有这么用的! 列表中不是一般都是字符串或数值的吗,就像

  1. filePathHandler(filepath).map( (file:String) => dosomethingWith(file) ) ?

  不过,一旦突破了这一点,也就是将函数当成一个普通变量一样"肆意玩弄", Currying + 函数式编程的威力就发挥出来了。为简单起见,诸位请看 handleFile 的调用, 先将第一个变量赋值为读文件内容的函数, 返回一个函数, 该函数接受一个用来处理文件内容的函数作为参数以获得灵活的能力, 可以使用任意的函数对文件内容进行处理,比如在文件中查找字符串,计算非空行的行数等; 这就是函数动态组合获得的能力。

完整程序如下:

CurryDemo.scala

  1. package scalastudy.basic
  2.  
  3. import scala.math.pow
  4. import scalastudy.utils.PathConstants
  5. import scalastudy.utils.DefaultFileUtil._
  6.  
  7. /**
  8. * Created by lovesqcc on 16-4-16.
  9. */
  10. object CurryDemo extends App {
  11.  
  12. launch()
  13.  
  14. def launch(): Unit = {
  15.  
  16. val listNum = 10
  17. val alist = (1 to listNum).toList
  18.  
  19. for (i <- 1 to 3) {
  20. val listPolySum = polynomialSum(i)(_)
  21. val sum = listPolySum(alist)
  22. assert(sum == polynomialSum2(i, listNum))
  23. assert(sum == polynomialSumNotGood(alist)(i))
  24. println("sum: " + sum)
  25. }
  26. println("test passed.")
  27.  
  28. val filename = PathConstants.scalaSrcPath + "/basic/CurryDemo.scala"
  29. val fileContentHandler = handleFile(readFile(_))(_)
  30. val findInFileFunc = fileContentHandler(findInFile)(_)
  31. println(findInFileFunc(filename))
  32.  
  33. val countInFileFunc = fileContentHandler(countInFile)(_)
  34. println("Non Empty Lines: " + countInFileFunc(filename))
  35.  
  36. val result = handleFiles(fetchAllFiles)((file:String) => file.endsWith("scala"))( List((s:String) => readFileLines(s)))((liststr: List[Any]) => liststr.mkString)(PathConstants.srcPath)
  37. println(result)
  38. }
  39.  
  40. /* calc 1^m + 2^m + ... + n^m */
  41. def polynomialSum2(m:Int, n:Int):Long = {
  42. return 1.to(n).toList.map(pow(_,m)).sum.asInstanceOf[Long];
  43. }
  44.  
  45. /* calc 1^m + 2^m + ... + n^m */
  46. def polynomialSum(m: Int)(list: List[Int]): Long = {
  47. return list.map(pow(_,m)).sum.asInstanceOf[Long];
  48. }
  49.  
  50. /* calc 1^m + 2^m + ... + n^m */
  51. def polynomialSumNotGood(list: List[Int])(m:Int):Long = {
  52. return list.map(pow(_,m)).sum.asInstanceOf[Long];
  53. }
  54.  
  55. }

FileAbility.scala

  1. package traits
  2.  
  3. import java.io.File
  4.  
  5. import scala.collection.mutable.ArrayBuffer
  6. import scala.io.Source
  7. import scalastudy.traits.LineHandler
  8.  
  9. /**
  10. * Created by lovesqcc on 16-3-27.
  11. */
  12. trait FileAbility extends LineHandler {
  13.  
  14. def readFile(filename:String): String = {
  15. val fileSource = Source.fromFile(filename)
  16. try {
  17. return fileSource.mkString
  18. } finally {
  19. fileSource.close()
  20. }
  21. }
  22.  
  23. def readFileLines(filename:String):List[String] = {
  24. val fileSource = Source.fromFile(filename)
  25. try {
  26. return fileSource.getLines().toList
  27. } finally {
  28. fileSource.close()
  29. }
  30. }
  31.  
  32. /* a simple tool for processing a file */
  33. def handleFile(filePathHandler:(String) => String)
  34. (fileContentHandler: (String) => Any)
  35. (filepath: String): Any = {
  36. return fileContentHandler(filePathHandler(filepath))
  37. }
  38.  
  39. /* a simple frame for processing files */
  40. def handleFiles(filePathHandler:(String) => List[String])
  41. (fileFilterHandler: (String) => Boolean)
  42. (fileHandlerList: List[(String)=>List[Any]])
  43. (totalHandler: List[Any] => Any)
  44. (filepath:String): Any = {
  45. return totalHandler(
  46. fileHandlerList.map(
  47. (handle:(String)=>List[Any]) =>
  48. filePathHandler(filepath).filter(fileFilterHandler(_))
  49. .map(handle(_)).flatten
  50. )
  51. )
  52. }
  53.  
  54. def findInFile(text:String):Any = {
  55. val patt = "f\\w+".r
  56. return patt.findAllIn(text).toList
  57. }
  58.  
  59. def countInFile(text:String):Any = {
  60. return text.split("\n").toList.filter(s => ! s.matches("^\\s*$")).length
  61. }
  62.  
  63. def fetchAllFiles(path:String): List[String] = {
  64. val fetchedFilesBuf = ArrayBuffer[String]()
  65. fetchFiles(path, fetchedFilesBuf)
  66. return fetchedFilesBuf.toList
  67. }
  68.  
  69. def fetchFiles(path:String, fetchedFiles:ArrayBuffer[String]):Unit = {
  70. val dirAndfiles = new File(path).listFiles
  71. if (dirAndfiles!=null && dirAndfiles.length > 0) {
  72. val files = dirAndfiles.filter(_.isFile)
  73. if (files.length > 0) {
  74. fetchedFiles ++= files.map(_.getCanonicalPath)
  75. }
  76.  
  77. val dirs = dirAndfiles.filter(_.isDirectory)
  78. if (dirs.length > 0) {
  79. dirs.map(_.getCanonicalPath).foreach { dirpath =>
  80. fetchFiles(dirpath, fetchedFiles) }
  81. }
  82. }
  83. }
  84.  
  85. def handleFile(filename:String):List[Any] = {
  86. return readFileLines(filename).map(handle(_)).toList
  87. }
  88.  
  89. def handleFileWithNoReturn(filename:String, lineHandler: LineHandler):Unit = {
  90. readFileLines(filename).foreach { line =>
  91. lineHandler.handle(line)
  92. }
  93. }
  94.  
  95. }

DefaultFileUtil.scala

  1. package scalastudy.utils
  2.  
  3. import traits.FileAbility
  4.  
  5. import scalastudy.traits.LinePrintHandler
  6.  
  7. /**
  8. * Created by lovesqcc on 16-4-16.
  9. */
  10. object DefaultFileUtil extends FileAbility with LinePrintHandler {
  11.  
  12. }

LineHandler.scala

  1. package scalastudy.traits
  2.  
  3. /**
  4. * Created by lovesqcc on 16-3-27.
  5. */
  6. trait LineHandler {
  7. def handle(line: String):Any = {}
  8. }

LinePrintHandler.scala

  1. package scalastudy.traits
  2.  
  3. /**
  4. * Created by lovesqcc on 16-4-2.
  5. */
  6. trait LinePrintHandler extends LineHandler {
  7. override def handle(line: String): Any = {
  8. println(line)
  9. }
  10. }

PathConstants.scala

  1. package scalastudy.utils
  2.  
  3. /**
  4. * Created by lovesqcc on 16-4-16.
  5. */
  6. object PathConstants {
  7.  
  8. val projPath = System.getProperty("user.dir")
  9. var srcPath = projPath + "/src/main/java"
  10. val scalaSrcPath = projPath + "/src/main/java/scalastudy"
  11.  
  12. }

函数柯里化(Currying)示例的更多相关文章

  1. Swift函数柯里化(Currying)简谈

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...

  2. 前端开发者进阶之函数柯里化Currying

    穆乙:http://www.cnblogs.com/pigtail/p/3447660.html 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接 ...

  3. 偏函数应用(Partial Application)和函数柯里化(Currying)

    偏函数应用指的是固化函数的一个或一些参数,从而产生一个新的函数.比如我们有一个记录日志的函数: 1: def log(level, message): 2: print level + ": ...

  4. 应用js函数柯里化currying 与ajax 局部刷新dom

    直接上代码吧 最近读javascript核心概念及实践的代码 感觉很有用 备忘. <div id="request"></div> <script t ...

  5. Javascript函数柯里化(curry)

    函数柯里化currying,是函数式编程非常重要的一个标志.它的实现需要满足以下条件,首先就是函数可以作为参数进行传递,然后就是函数可以作为返回值return出去.我们依靠这个特性编写很多优雅酷炫的代 ...

  6. 深入理解javascript函数进阶系列第二篇——函数柯里化

    前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...

  7. JS中的柯里化(currying)

    何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参 ...

  8. 函数柯里化(Currying)小实践

    什么是函数柯里化 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由 Ch ...

  9. 高阶函数,柯里化,sort排序

    高阶函数概念 first class object:     函数在python中时一等公民.     函数也是对象,可调用的对象.     函数可以作为普通变量,参数,返回值等等. 高阶函数:    ...

随机推荐

  1. 利用MetaWeblog API实现XMLRPC写博客功能

    Windows Live Writer是一款小巧的写博客的工具,非常方便,甚至网上看到过有的评论称Live Writer是一款最不像微软产品的微软产品. Writer支持MSN Spaces以及Wor ...

  2. 重命名PDF打印文件名

    Odoo系统默认打印出来的PDF文件都是以当前文档模型对象对应的模板文件名命名的,对用户来说,这样的命名很不友好. 我们希望能够将打印出来的文件名以单号命名,下面是实现这种目的的方法. 在report ...

  3. HDU 2577

    How to Type Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  4. thinkphp框架3.2的cookie删除问题记录

    在使用框架删除cookie时,发现cookie(null)不起作用.后来查看官网相关信息,看到了讨论http://www.thinkphp.cn/bug/2602.html

  5. [转]Oracle数据库ASH和AWR的简单介绍

    在Oracle数据库中,有时我们可能会遇到这样的术语:ASH和AWR,那么它们是怎样产生的呢?它们的作用又是什么呢?本文我们就来介绍这一部分内容.       1.10g之前 用户的连接将产生会话,当 ...

  6. Flashback闪回技术小实验

    1闪回查询 2闪回删除 3闪回版本查询 4闪回事务查询 5闪回某张表   6闪回数据库

  7. AppStore审核

    应用被拒分为两种:Binary Rejected 和 Metadata Rejected.前者需要重新上传应用并且重新排队,后者只需要修改信息,不需要重新上传应用. 1.应用内包含检查更新功能 iOS ...

  8. JavaScript入门篇 第三天(认识DOM)

    认识DOM 文档对象模型DOM(Document Object Model)定义访问和处理HTML文档的标准方法.DOM 将HTML文档呈现为带有元素.属性和文本的树结构(节点树). 先来看看下面代码 ...

  9. Struts 2.x No result defined for action 异常

      这是我跑struts2的第一个例子,跑的也够郁闷的,这个问题烦了我几个钟... 2011-5-10 10:10:17 com.opensymphony.xwork2.util.logging.co ...

  10. C#构造Http 破解学校教务系统学生账号密码

    背景介绍 我们学校的教务系统的是以学生学号作为登陆账号,初始密码是自己的生日. 一点点想法 每次期末查成绩的时候,我都会有一个想法,要是我能跑到系统后台,把自己的成绩修改一下,那该时间多么舒坦的事情啊 ...