# 轻量函数式 JavaScript：附录 A、Transducing

+关注继续查看

Transducing 意味着带有变形（transforming）的递减（reduction）。

## 首先，为什么

function isLongEnough(str) {
return str.length >= 5;
}

function isShortEnough(str) {
return str.length <= 10;
}

var words = [ "You", "have", "written", "something", "very", "interesting" ];

words
.filter( isLongEnough )
.filter( isShortEnough );
// ["written","something"]

zip(
list1.filter( isLongEnough ).filter( isShortEnough ),
list2.filter( isLongEnough ).filter( isShortEnough ),
list3.filter( isLongEnough ).filter( isShortEnough )
)

function isCorrectLength(str) {
return isLongEnough( str ) && isShortEnough( str );
}

words
.map(
pipe( removeInvalidChars, upper, elide )
);

words
.map( strUppercase )
.filter( isLongEnough )
.filter( isShortEnough )
.reduce( strConcat, "" );
// "WRITTENSOMETHING"

## 接下来，如何做

### 将映射/过滤表达为递减

function strUppercase(str) { return str.toUpperCase(); }
function strConcat(str1,str2) { return str1 + str2; }

function strUppercaseReducer(list,str) {
list.push( strUppercase( str ) );
return list;
}

function isLongEnoughReducer(list,str) {
if (isLongEnough( str )) list.push( str );
return list;
}

function isShortEnoughReducer(list,str) {
if (isShortEnough( str )) list.push( str );
return list;
}

words
.reduce( strUppercaseReducer, [] )
.reduce( isLongEnoughReducer, [] )
.reduce( isShortEnough, [] )
.reduce( strConcat, "" );
// "WRITTENSOMETHING"

function strUppercaseReducer(list,str) {
return list.concat( [strUppercase( str )] );
}

function isLongEnoughReducer(list,str) {
if (isLongEnough( str )) return list.concat( [str] );
return list;
}

function isShortEnoughReducer(list,str) {
if (isShortEnough( str )) return list.concat( [str] );
return list;
}

### 将递减函数参数化

function filterReducer(predicateFn) {
return function reducer(list,val){
if (predicateFn( val )) return list.concat( [val] );
return list;
};
}

var isLongEnoughReducer = filterReducer( isLongEnough );
var isShortEnoughReducer = filterReducer( isShortEnough );

function mapReducer(mapperFn) {
return function reducer(list,val){
return list.concat( [mapperFn( val )] );
};
}

var strToUppercaseReducer = mapReducer( strUppercase );

words
.reduce( strUppercaseReducer, [] )
.reduce( isLongEnoughReducer, [] )
.reduce( isShortEnough, [] )
.reduce( strConcat, "" );

### 抽取共通的组合逻辑

return list.concat( .. );

// 或者
return list;

function WHATSITCALLED(list,val) {
return list.concat( [val] );
}

function listCombination(list,val) {
return list.concat( [val] );
}

function mapReducer(mapperFn) {
return function reducer(list,val){
return listCombination( list, mapperFn( val ) );
};
}

function filterReducer(predicateFn) {
return function reducer(list,val){
if (predicateFn( val )) return listCombination( list, val );
return list;
};
}

### 将组合参数化

function mapReducer(mapperFn,combinationFn) {
return function reducer(list,val){
return combinationFn( list, mapperFn( val ) );
};
}

function filterReducer(predicateFn,combinationFn) {
return function reducer(list,val){
if (predicateFn( val )) return combinationFn( list, val );
return list;
};
}

var strToUppercaseReducer = mapReducer( strUppercase, listCombination );
var isLongEnoughReducer = filterReducer( isLongEnough, listCombination );
var isShortEnoughReducer = filterReducer( isShortEnough, listCombination );

var curriedMapReducer = curry( function mapReducer(mapperFn,combinationFn){
return function reducer(list,val){
return combinationFn( list, mapperFn( val ) );
};
} );

var curriedFilterReducer = curry( function filterReducer(predicateFn,combinationFn){
return function reducer(list,val){
if (predicateFn( val )) return combinationFn( list, val );
return list;
};
} );

var strToUppercaseReducer =
curriedMapReducer( strUppercase )( listCombination );
var isLongEnoughReducer =
curriedFilterReducer( isLongEnough )( listCombination );
var isShortEnoughReducer =
curriedFilterReducer( isShortEnough )( listCombination );

### 组合柯里化后的函数

var x = curriedMapReducer( strUppercase );
var y = curriedFilterReducer( isLongEnough );
var z = curriedFilterReducer( isShortEnough );

var upperReducer = x( listCombination );
var longEnoughReducer = y( listCombination );
var shortEnoughReducer = z( listCombination );

function reducer(list,val) {
if (isLongEnough( val )) return z( list, val );
return list;
}

var shortEnoughReducer = z( listCombination );
var longAndShortEnoughReducer = y( shortEnoughReducer );

// shortEnoughReducer，来自 z(..):
function reducer(list,val) {
if (isShortEnough( val )) return listCombination( list, val );
return list;
}

// longAndShortEnoughReducer，来自 y(..):
function reducer(list,val) {
if (isLongEnough( val )) return shortEnoughReducer( list, val );
return list;
}

longAndShortEnoughReducer( [], "nope" );
// []

longAndShortEnoughReducer( [], "hello" );
// ["hello"]

longAndShortEnoughReducer( [], "hello world" );
// []

longAndShortEnoughReducer(..) 工具滤除了既不够长也不够短的值，而且它是在同一个步骤中做了这两个过滤的。它是一个组合的递减函数！

var longAndShortEnoughReducer = y( z( listCombination) );
var upperLongAndShortEnoughReducer = x( longAndShortEnoughReducer );

// upperLongAndShortEnoughReducer:
function reducer(list,val) {
return longAndShortEnoughReducer( list, strUppercase( val ) );
}

upperLongAndShortEnoughReducer( [], "nope" );
// []

upperLongAndShortEnoughReducer( [], "hello" );
// ["HELLO"]

upperLongAndShortEnoughReducer( [], "hello world" );
// []

var x = curriedMapReducer( strUppercase );
var y = curriedFilterReducer( isLongEnough );
var z = curriedFilterReducer( isShortEnough );

var upperLongAndShortEnoughReducer = x( y( z( listCombination ) ) );

words.reduce( upperLongAndShortEnoughReducer, [] );
// ["WRITTEN","SOMETHING"]

x(y(z( .. ))) 是一个组合。让我们跳过中间的变量名 x / y / z，直接表达这个组合：

var composition = compose(
curriedMapReducer( strUppercase ),
curriedFilterReducer( isLongEnough ),
curriedFilterReducer( isShortEnough )
);

var upperLongAndShortEnoughReducer = composition( listCombination );

words.reduce( upperLongAndShortEnoughReducer, [] );
// ["WRITTEN","SOMETHING"]

1. listCombination(..) 作为组合函数流入 isShortEnough(..)，为它制造了过滤-递减函数。
2. 然后这个结果递减函数作为组合函数流入 isLongEnough(..)，为它制造了过滤-递减函数。
3. 最后，这个结果递减函数作为组合函数流入 strUppercase(..)，为它制造了映射-递减函数。

// TODO: fact-check if the transducer produces the reducer or is the reducer

var transducer = compose(
curriedMapReducer( strUppercase ),
curriedFilterReducer( isLongEnough ),
curriedFilterReducer( isShortEnough )
);

words
.reduce( transducer( listCombination ), [] );
// ["WRITTEN","SOMETHING"]

#### 列表组合：纯粹 vs 不纯粹

function listCombination(list,val) {
return list.concat( [val] );
}

function listCombination(list,val) {
list.push( val );
return list;
}

listCombination(..) 根本不是我们要与之交互的函数。我们没有在程序的任何部分直接使用它，而是让 transducer 处理使用它。

listCombination(..) 更像是一个 transducing 的内部实现细节 —— 事实上，它经常由一个 transducing 库提供给你！ —— 而非一个你平常在程序中与之交互的顶层方法。

### 替换组合函数

words
.reduce( transducer( listCombination ), [] )
.reduce( strConcat, "" );
// WRITTENSOMETHING

function strConcat(str1,str2) { return str1 + str2; }

function listCombination(list,val) { list.push( val ); return list; }

words.reduce( transducer( strConcat ), "" );
// WRITTENSOMETHING

## 最后，什么

var transduceMap = curry( function mapReducer(mapperFn,combinationFn){
return function reducer(list,v){
return combinationFn( list, mapperFn( v ) );
};
} );

var transduceFilter = curry( function filterReducer(predicateFn,combinationFn){
return function reducer(list,v){
if (predicateFn( v )) return combinationFn( list, v );
return list;
};
} );

var transducer = compose(
transduceMap( strUppercase ),
transduceFilter( isLongEnough ),
transduceFilter( isShortEnough )
);

transducer(..) 任然需要被传入一个组合函数（比如 listCombination(..)strConcat(..)）来声称一个 transduce-递减函数，然后这个函数才能在 reduce(..) 中使用（与一个初始值一起）。

function transduce(transducer,combinationFn,initialValue,list) {
var reducer = transducer( combinationFn );
return list.reduce( reducer, initialValue );
}

var transducer = compose(
transduceMap( strUppercase ),
transduceFilter( isLongEnough ),
transduceFilter( isShortEnough )
);

transduce( transducer, listCombination, [], words );
// ["WRITTEN","SOMETHING"]

transduce( transducer, strConcat, "", words );
// WRITTENSOMETHING

### Transducers.js

var transformer = transducers.comp(
transducers.map( strUppercase ),
transducers.filter( isLongEnough ),
transducers.filter( isShortEnough )
);

transducers.transduce( transformer, listCombination, [], words );
// ["WRITTEN","SOMETHING"]

transducers.transduce( transformer, strConcat, "", words );
// WRITTENSOMETHING

transducers.map(..)transducers.filter(..) 是特殊的帮助函数，它们将普通的判定或映射函数适配为生成一个特殊（在底层包装了一个 transducer 函数的）变形对象的函数；这个库将这些变形对象用于 transducing。这个变形函数抽象的额外能力超出了我们要探索的范围，更多的信息请参阅库的文档。

words.reduce(
transducers.toFn( transformer, strConcat ),
""
);
// WRITTENSOMETHING

into(..) 是库提供的另一个帮助函数，它根据被指定的空/初始值类型自动地选择一个默认组合函数：

transducers.into( [], transformer, words );
// ["WRITTEN","SOMETHING"]

transducers.into( "", transformer, words );
// WRITTENSOMETHING

## 总结

Transduce 意味着使用递减来变形。更具体点儿说，一个 transducer 是一个可以进行组合的递减函数。

Transducing 主要改善了新能，这在用于一个懒惰序列（异步 observable）时尤其明显。

|
JavaScript 前端开发 索引
JavaScript 函数式编程技巧 - 反柯里化

110 0
|

JavaScript 函数式编程技巧 - 柯里化

87 0
|
JavaScript 前端开发 Java

71 0
|
JavaScript 前端开发 API
Vanilla JS——世界上最轻量的JavaScript框架（没有之一）
Vanilla JS 是一个快速、轻量级、跨平台的JavaScript框架。我们可以用它构建强大的JavaScript应用程序。
3056 0

989 0
|
JavaScript 前端开发
JavaScript函数式编程之pointfree与声明式编程

3178 0
|
JavaScript 前端开发

1071 0
|

1481 0
|
JavaScript 前端开发 自然语言处理
|