functional.js 介绍及源码分析


网站: JavaEye 作者: Lich_Ray 发表时间: 2007-07-26 22:17 此文章来自于 http://www.iteye.com
声明:本文系JavaEye网站原创文章,未经JavaEye网站或者作者本人书面许可,任何其他网站严禁擅自发表本文,否则必将追究法律责任!
原文链接: http://www.iteye.com/topic/105854

引用
本文对刚刚在网络上现身的 JavaScript 函数式编程库 functional.js 进行详尽的解读(地址已更正,感谢 hax)。

functional.js 是模仿 Haskell 语言标准库 Prelude 制作的函数式编程库,主要实现了:

  • 扩展的克里化函数

  • 运算符函数化

  • 紧缩的匿名函数语法

  • 无须指定参数的匿名函数语法

  • 函数向导语法

  • 基本的通用列表操作

  • 部分扩展基于对象化


其中,扩展语法由字符串表示。未能实现的特性有:

  • 尾递归优化

  • 模式匹配(包括参数匹配、列表匹配、情况分析)

  • 惰性运算(包括无穷列表)

  • 列表领悟

  • 扩展绑定、同时绑定

  • 其它列表操作(以及对于列表操作的基于对象化)


下面我们一边分析源代码,一边讲解库的用法。

一、库安装和概览
functional.js 库的所有用户级操作分为3个部分:


  1. 全局操作,绑定在全局对象
  2. Functional 上,主要是高阶函数操作和列表操作,所谓库安装即把这些内容可选的、安全地复制到全局环境
  3. 函数扩展,实现特殊高阶函数特性的工具(特供内部)

  4. 语法扩展,绑定在
  5. String.prototype 上,负责将字符串表示的 lambda 语法翻译为相应的高阶函数(特供内部)

下面是安装函数 Functional.install 的源代码(中文注释为笔者所加,下同):
代码

一般只要执行 Functional.install() 一句即可。

二、高阶函数操作
1. Functional.compose ([Function]) // 匿名的参数类型指的是 arguments 的类型,下同
  接受一列参数个数被认为相等的(允许 Currying 算子)函数为参数,返回一个函数,它接受一定的参数,能够对它们累积倒序 apply 那列函数。
  示例:compose('1+', '2*')(2) => 5
  
2. Functional.sequence ([Function])
  累积 apply 的顺序为参数顺序,为 compose 的反序。
  示例:sequence('1+', '2*')(2) => 6

以上两个操作亦可见于 Function.prototype,用法:'1+'.lambda().sequence('2*')(2) ==> 6

3. Function.prototype.flip ()
  返回一个函数,是原函数对象 this 参数接受顺序颠倒后的版本,不应属于函数扩展类。
  示例:flip('a/b')(1, 2) => 2
  
4. Function.prototype.saturate ([]) {
  返回一个函数,是原函数对象 this 忽略自己接受的参数,仅接受指定参数的版本。
  形式:f.saturate(args...)(args2...) == f(args...)
  
5. Function.prototype.aritize (n::Number) // 有名的参数类型由 :: 指定,下同
  返回一个函数,是原函数对象 this 忽略自己接受的参数列表中下标为 n 的参数的版本。
  
6. Function.S (f, g::Function)
  以单个大写字母命名的是函数的原子操作,应被收入 Functional 对象,这个很奇怪。
  形式:S(f, g)(args...) == f(g(args...), args...)
  
三、通用列表操作
1. 绑定在 Functional 对象上的部分完全照抄 Haskell Prelude 以及 Clean 的命名,它们是:


  1. map(f,
  2. [x1, x2...]) = [f(x, 0), f(x2, 1), ...]
  3. foldl,
  4. reduce(f, init, [x0, x1, x2]) == f(f(f(init, x0), x1), x2)
  5. filer,
  6. select('%2', [1,2,3,4]) -> [1, 3]
  7. foldr(f,
  8. init, [x0, x1, x2]) == fn(x0, f(x1, f(x2, init)))
  9. some(f,
  10. [x1, x2, x3, ...]) == f(x1) || f(x2) || f(x3)...
  11. every(f,
  12. [x1, x2, x3, ...]) == f(x1) && f(x2) && f(x3)...

以上操作的介绍网上到处都是,不再赘述;但有一点不同,即它们除了接受正常参数之外,还在最后接受一个可选参数 object::Object,它被用于指定操作执行的对象/环境。
另外,这些操作全部基于命令式风格实现,对于没有尾递归优化的 JavaScript 来说,效率有保障。

四、群体谓词操作
1. Functional.and ([Function])
  接受一列函数为参数,返回一个函数,它接受一个参数,对该参数 apply 那列函数,如结果全为 true,返回 true;否则返回 false。
  形式:and(f1, f2...)(args...) == f1(args...) && f2(args...)...
  示例:and('>1', '>2')(2) => false
  
2. Functional.or ([Function])
  接受一列函数为参数,返回一个函数,它接受一个参数,对该参数 apply 那列函数,如结果全为 false,返回 false;否则返回 true。
  形式:or(f1, f2...)(args...) == f1(args...) || f2(args...)...
  示例:or('>1', '>2')(2) => true
  
3. Functional.not = function(fn::Function)
  返回一个函数,是参数返回的布尔值的函数(谓词,下同) fn 取否的版本。
  形式:f.not()(args...) == !f(args...)
  
4. Functional.equal ([Function])
  接受一列函数为参数,返回一个函数,它接受一个参数,对该参数 apply 那列函数,如结果全部 == ,返回 true;否则返回 false。
  形式:equal(f1, f2...)(args...) == f1(args...) == f2(args...)...
  示例:equal()() => true // 特殊情况

五、函数扩展
  这一章仅仅是介绍内部实现。
1. Function.prototype.bind (object::Object,[])
  返回一个函数,作为 this 函数对象的副本,使其将在 object 环境下执行,并额外携带参数。
  形式:f.bind(obj, args...)(args2...) == f.apply(obj, [args..., args2...])
  
2. Function.prototype.curry ([])
这是实现克里化特性的关键函数,思想来自网络

代码

  返回那个传说中的可在参数不足时分步调用的函数——Currying 算子。
  形式:f.curry(args1...)(args2...) == f(args1..., args2...)

其它的 curry 类函数有:


  • rcurry,对从右边开始缺少参数的函数作克里化

  • ncurry,不接受全部参数就不
  • apply 参数的版本
  • rncurry,前者的反序版本

  • uncurry,作者一再强调,这不是
  • curry 的反转版本。它会拆分出第一个已得参数,形式为:f.uncurry(a, b...) == f(a)(b...)

3. Function.prototype.partial ([])
  在此函数定义之前,有定义 _ = Function._ = {} 。结合它们可以允许你像在 Haskell 中那样在参数列表中用 _ 忽略参数。但现在空谈是没用的,要结合第七章的语法扩展。
  
4. Function.prototype.guard (guard:>Function, otherwise)
  类似的,是一个允许在函数定义中使用向导功能的工具,尚缺少语法扩展支持。
  形式:f.guard(g, h)(args...) == f(args...), when g(args...) is true
    f.guard(g ,h)(args...) == h(args...), when g(args...) is false

六、工具函数
1. Functional.invoke (methodName::String, [])
  示例:invoke('toString')(123) => "123"
  
2. Functional.pluck (name::String)
  示例:pluck('length')("abc") => 3
  
3. Functional.until (pred:>Function, fn:>Function) // 用 :> 表示将参数强制转换类型

代码

  类似 Haskell 的 until,是一种函数式的循环,用命令式风格实现。
  
4. Functional.zip ([])
  特别注意,此 zip 并非 Haskell 中的 zip,它接受可变参数列表而不是列表的列表。
  形式:zip(a, b...) == [[a0, b0], [a1, b1], ...]

以上的章节中绑定在 Functional 上函数都可作为 Function 的对象方法直接使用,我们看这一行:

代码

前文对它们作出了定义,这里忽略。用法:name(arg, args...) == arg.name(args...)。

七、语法扩展
1. String.prototype.lambda ()
  把字符串表示的字符串翻译为函数扩展可接受的函数,进一步转为 JavaScript 函数。

代码

八、过滤器生成器
  仅供特别好学的同志们参考。
1. Function.prototype.prefilterObject (filter::Function)
  形式:fn.prefilterObject(filter).apply(object, args...) == fn.apply(filter(object), args...)
  
2. Function.prototype.prefilterAt (index::Number, filter::Function)
  形式:fn.prefilterAt(i, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(a_{i}), ..., a_{n})
  
3. Function.prototype.prefilterSlice (filter::Function, start, end::Number)
  形式:fn.prefilterSlice(i0, i1, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(args_{i0}, ..., args_{i1}), ..., a_{n})

九、其它用户级函数
1. Functional.id = Functional.I = function(x) {return x};

2. Functional.constfn = Functional.K = function(x) {return function() {return x}};

3. .toFunction ()
  在 String.prototype,Function.prototype,Function(需要参数 fn::Function) 上都有绑定,把对象转换为一个合适的 Functional 函数。但你不需要把代码写这样,map('*2'.toFunction(),alist),因为全局用户级函数都会对应为函数的参数自动执行 toFunction(),只要 map('*2',alist) 就行了。另外,String.prototype 上还有 JavaScript-Like 的 call、apply 方法。

十、结语
  functional.js 很强,很有用,很牛X;但同时也很年轻(7.20 发布),很多可以实现的功能还不完善,不说列表领悟什么的吧,至少应该把 Haskell Prelude 库在通用列表操作方面的函数的移植工作完成。我们期待 Oliver Steele 的表现。




《 functional.js 介绍及源码分析 》 的评论也很精彩,欢迎您也添加评论。查看详细 >>

推荐相关文章:
   javascript中的FP
   Programming in Emacs Lisp笔记(十八) 终结




JavaEye推荐
上海乐福狗信息技术有限公司:诚聘技术经理和开发工程师
免费下载IBM社区版软件--它基于开放的标准,支持广泛的开发类型,让您的开发高效自主!
京沪穗蓉四地免费注册,SOA技术高手汇聚交锋.
上海:优秀公司德比:高薪诚聘 资深Java工程师
广州:优易公司:诚聘Java工程师,开发经理
上海:尤恩斯国际集团:诚聘开发工程师
北京:优秀公司NHNChina招聘:WEB开发,系统管理,JAVA开发, DBA


你可能感兴趣的:(JavaScript,编程,prototype,F#,haskell)