函数柯里化(Currying)示例
”函数柯里化”是指将多变量函数拆解为单变量的多个函数的依次调用, 可以从高元函数动态地生成批量的低元的函数。可以看成一个强大的函数工厂,结合函数式编程,可以叠加出很BT的能力。下面给出了两个示例,说明如何使用 Currying 用一行代码计算任意指数的多项式的和; 以及使用 Currying 实现一个简单的文件处理框架。
举例一: 计算任意指数的多项式的和 sum(n, m) = 1^m + 2^m + ... + n^m
定义如下多变量的函数就可以解决
def polynomialSum2(m:Int, n:Int):Long = {
return 1.to(n).toList.map(pow(_,m)).sum.asInstanceOf[Long];
}
为什么要使用柯里化呢? 因为柯里化不仅仅可以求出最终的值,还可以生成批量的函数,复用这些函数。比如
def polynomialSum(m: Int)(list: List[Int]): Long = {
return list.map(pow(_,m)).sum.asInstanceOf[Long];
}
i = 2
val listPolySum = polynomialSum(i)(_)
当我们对第一个变量赋值后,返回得到的是一个平方和函数 listPolySum = 1^2 + 2^2 + ... + n^2 , 可以研究这个函数的性质而不仅仅是求值。
注意到, Currying 的过程中,参数的顺序是有讲究的。一般, 函数参数建议放在前面,按照想要调用的顺序; 数据参数放在后面;最易变的参数放在最后面。如果这个函数写成以下形式,求值还是一样的, 但是生成的函数就不一样了, 假设第一个变量赋值为 list[1:10], 那么生成的函数就变成:
generateFunc = 1^m + 2^m + ... + 10^m , 没有研究价值了。
def polynomialSumNotGood(list: List[Int])(m:Int):Long = {
return list.map(pow(_,m)).sum.asInstanceOf[Long];
}
举例二: Currying 实现简易的文件处理框架
我们不是打算成为数学家的或计算机科学家的,那么 Currying 对实际的软件开发有什么用处呢? 细细想来还是有的。通过在 Currying 的参数中传入函数,可以做一些简易的微框架。假设要做一个简易的文件处理框架: 1. 路径处理器 filePathHandler: 通过解析文件路径名生成一个文件名列表; 2. 文件名过滤器 fileFilterHander: 指定条件从文件名列表中筛选出需要的文件; 3. 文件处理器 fileHandler: 处理每一个文件生成一个列表; 4. totalHandler: 汇总所有列表的值并进行处理。可以对 (2) 进行扩展,使之成为一个过滤器处理链;可以对 (3) 进行扩展, 使之成为一个文件处理器链。 代码如下:
/* a simple frame for processing files */
def handleFiles(filePathHandler:(String) => List[String])
(fileFilterHandler: (String) => Boolean)
(fileHandlerList: List[(String)=>List[Any]])
(totalHandler: List[Any] => Any)
(filepath:String): Any = {
return totalHandler(
fileHandlerList.map(
(handle:(String)=>List[Any]) =>
filePathHandler(filepath).filter(fileFilterHandler(_))
.map(handle(_)).flatten
)
)
}
怎么理解这段代码呢? 最主要的是文件处理器链的 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 呢! 哪有这么用的! 列表中不是一般都是字符串或数值的吗,就像
filePathHandler(filepath).map( (file:String) => dosomethingWith(file) ) ?
不过,一旦突破了这一点,也就是将函数当成一个普通变量一样"肆意玩弄", Currying + 函数式编程的威力就发挥出来了。为简单起见,诸位请看 handleFile 的调用, 先将第一个变量赋值为读文件内容的函数, 返回一个函数, 该函数接受一个用来处理文件内容的函数作为参数以获得灵活的能力, 可以使用任意的函数对文件内容进行处理,比如在文件中查找字符串,计算非空行的行数等; 这就是函数动态组合获得的能力。
完整程序如下:
CurryDemo.scala
package scalastudy.basic import scala.math.pow
import scalastudy.utils.PathConstants
import scalastudy.utils.DefaultFileUtil._ /**
* Created by lovesqcc on 16-4-16.
*/
object CurryDemo extends App { launch() def launch(): Unit = { val listNum = 10
val alist = (1 to listNum).toList for (i <- 1 to 3) {
val listPolySum = polynomialSum(i)(_)
val sum = listPolySum(alist)
assert(sum == polynomialSum2(i, listNum))
assert(sum == polynomialSumNotGood(alist)(i))
println("sum: " + sum)
}
println("test passed.") val filename = PathConstants.scalaSrcPath + "/basic/CurryDemo.scala"
val fileContentHandler = handleFile(readFile(_))(_)
val findInFileFunc = fileContentHandler(findInFile)(_)
println(findInFileFunc(filename)) val countInFileFunc = fileContentHandler(countInFile)(_)
println("Non Empty Lines: " + countInFileFunc(filename)) val result = handleFiles(fetchAllFiles)((file:String) => file.endsWith("scala"))( List((s:String) => readFileLines(s)))((liststr: List[Any]) => liststr.mkString)(PathConstants.srcPath)
println(result)
} /* calc 1^m + 2^m + ... + n^m */
def polynomialSum2(m:Int, n:Int):Long = {
return 1.to(n).toList.map(pow(_,m)).sum.asInstanceOf[Long];
} /* calc 1^m + 2^m + ... + n^m */
def polynomialSum(m: Int)(list: List[Int]): Long = {
return list.map(pow(_,m)).sum.asInstanceOf[Long];
} /* calc 1^m + 2^m + ... + n^m */
def polynomialSumNotGood(list: List[Int])(m:Int):Long = {
return list.map(pow(_,m)).sum.asInstanceOf[Long];
} }
FileAbility.scala
package traits import java.io.File import scala.collection.mutable.ArrayBuffer
import scala.io.Source
import scalastudy.traits.LineHandler /**
* Created by lovesqcc on 16-3-27.
*/
trait FileAbility extends LineHandler { def readFile(filename:String): String = {
val fileSource = Source.fromFile(filename)
try {
return fileSource.mkString
} finally {
fileSource.close()
}
} def readFileLines(filename:String):List[String] = {
val fileSource = Source.fromFile(filename)
try {
return fileSource.getLines().toList
} finally {
fileSource.close()
}
} /* a simple tool for processing a file */
def handleFile(filePathHandler:(String) => String)
(fileContentHandler: (String) => Any)
(filepath: String): Any = {
return fileContentHandler(filePathHandler(filepath))
} /* a simple frame for processing files */
def handleFiles(filePathHandler:(String) => List[String])
(fileFilterHandler: (String) => Boolean)
(fileHandlerList: List[(String)=>List[Any]])
(totalHandler: List[Any] => Any)
(filepath:String): Any = {
return totalHandler(
fileHandlerList.map(
(handle:(String)=>List[Any]) =>
filePathHandler(filepath).filter(fileFilterHandler(_))
.map(handle(_)).flatten
)
)
} def findInFile(text:String):Any = {
val patt = "f\\w+".r
return patt.findAllIn(text).toList
} def countInFile(text:String):Any = {
return text.split("\n").toList.filter(s => ! s.matches("^\\s*$")).length
} def fetchAllFiles(path:String): List[String] = {
val fetchedFilesBuf = ArrayBuffer[String]()
fetchFiles(path, fetchedFilesBuf)
return fetchedFilesBuf.toList
} def fetchFiles(path:String, fetchedFiles:ArrayBuffer[String]):Unit = {
val dirAndfiles = new File(path).listFiles
if (dirAndfiles!=null && dirAndfiles.length > 0) {
val files = dirAndfiles.filter(_.isFile)
if (files.length > 0) {
fetchedFiles ++= files.map(_.getCanonicalPath)
} val dirs = dirAndfiles.filter(_.isDirectory)
if (dirs.length > 0) {
dirs.map(_.getCanonicalPath).foreach { dirpath =>
fetchFiles(dirpath, fetchedFiles) }
}
}
} def handleFile(filename:String):List[Any] = {
return readFileLines(filename).map(handle(_)).toList
} def handleFileWithNoReturn(filename:String, lineHandler: LineHandler):Unit = {
readFileLines(filename).foreach { line =>
lineHandler.handle(line)
}
} }
DefaultFileUtil.scala
package scalastudy.utils import traits.FileAbility import scalastudy.traits.LinePrintHandler /**
* Created by lovesqcc on 16-4-16.
*/
object DefaultFileUtil extends FileAbility with LinePrintHandler { }
LineHandler.scala
package scalastudy.traits /**
* Created by lovesqcc on 16-3-27.
*/
trait LineHandler {
def handle(line: String):Any = {}
}
LinePrintHandler.scala
package scalastudy.traits /**
* Created by lovesqcc on 16-4-2.
*/
trait LinePrintHandler extends LineHandler {
override def handle(line: String): Any = {
println(line)
}
}
PathConstants.scala
package scalastudy.utils /**
* Created by lovesqcc on 16-4-16.
*/
object PathConstants { val projPath = System.getProperty("user.dir")
var srcPath = projPath + "/src/main/java"
val scalaSrcPath = projPath + "/src/main/java/scalastudy" }
函数柯里化(Currying)示例的更多相关文章
- Swift函数柯里化(Currying)简谈
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...
- 前端开发者进阶之函数柯里化Currying
穆乙:http://www.cnblogs.com/pigtail/p/3447660.html 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接 ...
- 偏函数应用(Partial Application)和函数柯里化(Currying)
偏函数应用指的是固化函数的一个或一些参数,从而产生一个新的函数.比如我们有一个记录日志的函数: 1: def log(level, message): 2: print level + ": ...
- 应用js函数柯里化currying 与ajax 局部刷新dom
直接上代码吧 最近读javascript核心概念及实践的代码 感觉很有用 备忘. <div id="request"></div> <script t ...
- Javascript函数柯里化(curry)
函数柯里化currying,是函数式编程非常重要的一个标志.它的实现需要满足以下条件,首先就是函数可以作为参数进行传递,然后就是函数可以作为返回值return出去.我们依靠这个特性编写很多优雅酷炫的代 ...
- 深入理解javascript函数进阶系列第二篇——函数柯里化
前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...
- JS中的柯里化(currying)
何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参 ...
- 函数柯里化(Currying)小实践
什么是函数柯里化 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由 Ch ...
- 高阶函数,柯里化,sort排序
高阶函数概念 first class object: 函数在python中时一等公民. 函数也是对象,可调用的对象. 函数可以作为普通变量,参数,返回值等等. 高阶函数: ...
随机推荐
- 相邻div实现一个跟着另一个自适应高度示例代码
方法一: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> < ...
- 李洪强iOS经典面试题130
绘图与动画 CAAnimation的层级结构 CAPropertyAnimation是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimatio ...
- 【android studio】android studio使用过程中,搜集的一些问题
1.[知乎]在Android Studio中如何将依赖的jar包放在SDK的android.jar前? 在编译原生Contacts应用时需用到非公开的API,需要引入framework等jar包,但在 ...
- Linux_文件查看
文件查看 直接查看内容:cat , tac , nl 翻页查看:more , less 指定获取内容:head , tail 查看非纯文字文档:od 文件时间更新与新建:touch cat: 从首行开 ...
- HDU1892二维树状数组
See you~ Time Limit: 5000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total Su ...
- 测试常用SQL注入语句大全
转载自Cracer,标题:<渗透常用SQL注入语句大全>,链接http://www.xxxx.com/?p=2226 1.判断有无注入点 整形参数判断 1.直接加' 2.and 1=1 3 ...
- form表单提交controller层接收到的值为乱码的问题
今天遇到个中文乱码问题,大体情况是这样的:前台有一个form表单,其中有几个input的控件,值是带中文的,form表单只设置了id='form1' method='post' action='xx ...
- 基于Jenkins的环境搭建
基于 Jenkins 快速搭建持续集成环境 持续集成是一种软件开发实践,对于提高软件开发效率并保障软件开发质量提供了理论基础.Jenkins 是一个开源软件项目,旨在提供一个开放易用的软件平台,使持续 ...
- 在 Django 模板中遍历复杂数据结构的关键是句点字符
在 Django 模板中遍历复杂数据结构的关键是句点字符 ( . ). 实例二 mysit/templates/myhtml2.html修改如下 <!DOCTYPE html> <h ...
- 想通过加HINT让其走全表扫描
一个SQL,通过SPM固定它的执行计划,可以通过DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE实现.也可以通地此功能在不修改原SQL的情况下对其加HINT来固定执行计划.D ...