函数柯里化(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中时一等公民. 函数也是对象,可调用的对象. 函数可以作为普通变量,参数,返回值等等. 高阶函数: ...
随机推荐
- HDU 2955(0-1背包问题)
题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87125#problem/M 题目: Description The aspir ...
- 用简单直白的方式讲解A星寻路算法原理
很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...
- 淘宝前端工程师:国内WEB前端开发十日谈
一直想写这篇"十日谈",聊聊我对Web前端开发的体会,顺便解答下周围不少人的困惑和迷惘.我不打算聊太多技术,我想,通过技术的历练,得到的反思应当更重要. 我一直认为自己是" ...
- orcale 动态执行语句
create or replace function fn_test2(tablename in varchar2) return number is rtn number; begin --通用的获 ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- HDU1134/HDU1133 递推 大数 java
Game of Connections Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- 在VS2012/2013上编辑和调试Quick-cocos2d-x的Lua代码
类似教程链接:http://www.cocoachina.com/bbs/read.php?tid=205043 步骤: 一.Quick-cocos2d-x 最新版本或要打算使用的版本 (http:/ ...
- $.getJSON JSONP的新坑
神坑1:返回的内容必须是正规的json数据.如 { "firstName": "Bill", "lastName": "Gates ...
- SSH整合简单实例
1.配置struts.xml文件: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE str ...
- Bootstrap css背景图片的设置
一. 网页中添加图片的方式有两种 一种是:通过<img>标签直接插入到html中 另一种是:通过css背景属性添加 居中方法:水平居中的text-align:center 和 margin ...