函数柯里化(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中时一等公民. 函数也是对象,可调用的对象. 函数可以作为普通变量,参数,返回值等等. 高阶函数: ...
随机推荐
- 9x9乘法表输出[Java]
笔试,9x9乘法表输出的问题,看似简单,回来把当时写的结果输入一遍后发现,并没有想象中的“完美”.把改写的pos在此,警示...不要忘记任何细节. public class NXN { public ...
- String的hashcode(java)
hashCode就是我们所说的散列码,使用hashCode算法可以帮助我们进行高效率的查找,例如HashMap,说hashCode之前,先来看看Object类. Java程序中所有类的直接或间接父类, ...
- java之数组(笔记)
1.与c++不同的声明 int[] i, j; int i[], j; 这两种声明在c++中是同等的,但是在Java中,前者是声明两个,后者是只有i是数组. 2.数组可以看作是特殊的类 3.数组的cl ...
- C#抽象类、抽象方法、虚方法
定义抽象类和抽象方法: abstract 抽象类特点: 1.不能初始化的类被叫做抽象类,它们只提供部分实现,但是另一个类可以继承它并且能创建它们的实例 2.一个抽象类可以包含抽象和非抽象方法,当一个类 ...
- Java程序设计 实验三
北京电子科技学院(BESTI) 实 验 报 告 课程:Java程序设计 班级:1353 姓名:李海空 学号:20135329 成绩: 指 ...
- POJ 1986 Distance Queries(Tarjan离线法求LCA)
Distance Queries Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 12846 Accepted: 4552 ...
- 精通JavaScript的this关键字
小提示 阅读本文,您需要了解JS的基本常识.您将花费40分钟完成本文的阅读. JS中的this关键字让很多新老JS开发人员都感到困惑.这篇文章将对this关键字进行完整地阐述.读完本文以后,您的困惑将 ...
- Ueditor 1.4.3.1 使用 ThinkPHP 3.2.3 的上传类进行图片上传
在 ThinkPHP 3.2.3 中集成百度编辑器最新版 Ueditor 1.4.3.1,同时将编辑器自带的上传类替换成 ThinkPHP 3.2.3 中的上传类. ① 下载编辑器(下载地址:http ...
- mysql开启慢查询
linux下: 一.在mysql中查询是否开启了慢查询mysql>SHOW VARIABLES LIKE '%slow%'; Variable_name Valuelog_slow_q ...
- 如何在Rails中执行Get/Post/Put请求
require 'open-uri' require 'json' require 'net/http' class CoupleController < ApplicationControll ...