# 附录 A：Transducing

Transducing 是我们这本书要讲到的更为高级的技术。它继承了第 8 章数组操作的许多思想。

Transducing 就是通过减少来转换。

## 首先，为什么

``````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"
``````

## 如何，下一步

### 把 Map/Filter 表示为 Reduce

``````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;
}
``````

### 参数化 Reducers

``````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] );
}
``````

`WHATSITCALLED(..)` 函数做了些什么呢，它接收两个参数（一个数组和另一个值），将值 concat 到数组的末尾返回一个新的数组。所以这个 `WHATSITCALLED(..)` 名字不合适，我们可以叫它 `listCombination(..)`

``````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, from z(..):
function reducer(list,val) {
if (isShortEnough( val )) return listCombination( list, val );
return list;
}

// longAndShortEnoughReducer, from y(..):
function reducer(list,val) {
if (isLongEnough( val )) return shortEnoughReducer( list, val );
return list;
}
``````

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

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

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

`longAndShortEnoughReducer(..)` 会过滤出不够长且不够短的值，它在同一步骤中执行这两个过滤。这是一个组合 reducer！

``````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(..)` 过滤器的 reducer。

2. 然后，所得到的 reducer 函数作为组合函数传入，继续构造 `isShortEnough(..)` 过滤器的 reducer。

3. 最后，所得到的 reducer 函数作为组合函数传入，构造 `strUppercase(..)` 映射的 reducer。

// TODO：检查 transducer 是产生 reducer 还是它本身就是 reducer

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

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

#### 列表组合：纯与不纯

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

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

`listCombination(..)` 不是我们完全有交互的函数。我们不直接在程序中的任何地方使用它，而只是在 transducing 的过程中使用它。

`listCombination(..)` 更多的是转换的内部实现细节。实际上，它通常由 transducing 库提供！而不是你的程序中进行交互的顶层方法。

### 可选的组合

``````words
.reduce( transducer( listCombination ), [] )
.reduce( strConcat, "" );
// 写点什么
``````

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

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

``````words.reduce( transducer( strConcat ), "" );
// 写点什么
``````

Boom！ 这就是 transducing。

## 最后

``````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(..)`）来产生一个传递给 `reduce(..)` （连同初始值）的 transduce-reducer 函数。

``````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 );
// 写点什么
``````

### 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(..)` 是特殊的辅助函数，可以将常规的断言函数或映射函数转换成适用于产生特殊变换对象的函数（里面包含了 reducer 函数）；这个库使用这些变换对象进行转换。此转换对象抽象的额外功能超出了我们将要探索的内容，请参阅该库的文档以获取更多信息。

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

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

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

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

## 总结

Transduce 就是通过减少来转换。更具体点，transduer 是可组合的 reducer。

transducing 主要提高性能，如果在延迟序列（异步 observables）中使用，则这一点尤为明显。