欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Functor组合, 参数集合, Curry

程序员文章站 2022-07-04 21:19:36
...
Functor组合, 参数集合, Curry

Ajoo曾经写过面向组合子编程系列。我也帮着助威。
面向组合子编程和并不是简单意义上的Composite Pattern。Composite Pattern只是一个简单的基本Pattern。
面向组合子编程只是用到了Composite Pattern,面向组合子编程本身的内容复杂许多,以至于复杂到这样的程度,数据和行为必须分开,形成Visitor Pattern。
而一般意义上的Compositor Pattern都是数据和行为在一起的对象的组合。
用于面向组合子编程中,一般是指只有行为没有数据的Functor对象进行组合。

基本的模型是这样。包括3个参与部分。算法,Functor算子,参数。
算法基本上是固定的,Functor算子可能*组合,参数也可能一个或者多个。

下面根据Functor和参数这两个方面介绍面向组合子编程的3种类型。

1. Functor组合 – Combinator, Pipe
Functor有多个,参数只有一个。
这一类的特点是,Functor移动,参数不动。

这是最常见的一类。
常见于用于工作流程控制的逻辑组合子。for each, or, and, not, if, else 等。
Hibernate的 Criteria Query 的 And , Or , Like, Equals的组合,也属于这一类。

ForEachCombinator( param ){ // And, Or 
for( functor in functors ){
… functor( param); …
// do some thing. 
// 比如, or 的时候,第一个符合条件,就返回
// and 的时候,第一个不符合条件,就返回
}
}

ForEachCombinator可以扩展为 OrCombinator,AndCombinator。

比较有趣的是,Pipe这种类型。
Pipe也是多个Functor,一个参数。但和上述的For each 之类的Combinator不同的是,下一个Functor的参数,是上一个Functor的返回结果。

Pipe( param ){ 
for( functor in functors ){
param = functor( param); 
// do some thing. 
}
}


我们给Pipe模式加上一些逻辑判断,和其他的Combinator组合,就可以用来实现if, then之类复杂一些的条件组合逻辑。
比如,我们给Pipe加上一个中断条件。
Pipe( param ){ 
for( functor in functors ){
param = functor( param); 
if(param is not valid) // null, false, etc
  break;
}
}


pipe = new Pipe( {new Equls(1), new Printer });
表示参数是1的时候,会执行printer。
pipe(1) 就可以打。
pipe(2) 就不打印。

这里只是一个简单示意。比较详细的例子和实现,可以看ajoo的blog和帖子。

2. 参数集合 – Visitor, Map, Filter, Reduce
这一类比较有趣。参数是多个,是一个集合,Functor可以是一个(也可能是多个)。
这一类的特点是,Functor不动,参数移动。

FP的Map, Filter算法都是这样,接受一个集合参数(list, stream),都经过同一个Functor的处理,当然这个Functor本身可能是组合过的。
Reduce算法也是这样,只是有点不同。Reduce多了一个Continuation, Context作用的环境变量参数,这个参数上一次处理的结果,传递给同一个Functor的下一次调用。
自己产生参数,自己消费,这个行为模式,和Pipe有点类似。但是不同。
Reduce算法反复调用一个Functor,并遍历参数集合。Pipe调用多个Functor,而且每个Functor只调用一次。

3. Functor组合,参数集合 – Curry, Factory Chain
最复杂的类型,就是这一类了。Functor移动,参数也移动。

关于Curry的概念和例子,本文不展开。网上有不少好文章。可以搜索到。
Curry 很类似于 Factory Chain。
举个例子。F(x, y, z) = 2 * x + y - z。
F(1, 2, 3)
用Curry的方式写就是,f(1)(2)(3)。这是FP语法。
换成类似于c, java, c#的语法来写,就是Factory Chain的写法。
factory.generate(1).generate(2).generate(3);

这里的关键是,上一个Functor处理参数的结果产生了下一个Functor。
下一个Functor继续处理下一个参数,产生下一个Functor。

BigDecimal.add(…).add(…).multiply(…).substract(…)
StringBuffer.append(…).append(…)
factory.newSession(..).newQuery(…).setParameter(…).setParameter(…)

等等形式,都具有Curry, Factory Chain的形式特点。虽然有时候,Functor返回的是自己。

Design Pattern的 Abstract Factory ( Factory of Factor ) 可以扩展成 Factory of Factor of Factory .., 就成了Factory Chain。也具有Curry, Factory Chain的形式特点。

Curry, Factory Chain在问题降维(减少参数个数)方面有很大用处。