Day 08 - Transduce II

review

jlongster.com

上一篇介绍了 transduce 基本概念後,就可以知道 transduce 就是对资料结构进行 transform 并 reduce,

其核心概念只有三步

  1. 迭代(iterate) Array 中的每一个值
  2. 转换(transform) 其值
  3. 建立(build) 新的 Array。

当然 transduce 不只能应用在 Array,接下来我们将介绍 transduce 如何应用在其他资料结构上 (ex: Object, Map, Immutable.js, ...)

overview

本篇会介绍以 transduce 为基底而延伸出的函式 into, sequence

transduce :

迭代给定的资料结构 (collection), 并将值透过 transform 函式进行转换,最後执行 reducer function,reduce 到给定的 init value.

const transduce = (transducer, reducer, initValue, collection) => {/** TODO */}

into

迭代给定的资料结构 (from), 并将值透过 transform 函式进行转换,最後把值塞到给定的资料结构 to

const into = (to, transducer, from) => {/** TODO */}

sequence

迭代给定的资料结构 (collection), 并将值透过 transform 函式进行转换,建立一个与 collection 相同的资料结构,并进行塞入。

const sequence = (transducer, collection) => {/** TODO */}

可以看到不变的都是 迭代,转换最後再建立,只是 transduce 像是最底层的函式,需要提供完整的资讯,而 into 则是透过给定的参数 (to),自动判断该型别对应的塞入函式,最後 sequence 则是更简洁了,其会透过给定的 collection,自动建立一个相同的结构与塞入函式。

rewrite transduce

上一篇我们实作 transduce,但那只能用在 Array 上,有没有一个可以让各种资料结构都可以使用的通用 transduce,没错,咱们就动手建立一个吧

const transduce = (transducer, reducer, initValue, collection) => {
    let acc = initValue;
    
    for (const val of collection) {
        acc = transducer(reducer)(acc, val)
    }
    
    return acc;
}

在这边要先提到 ES6 新增的协定 iteration protocols,而只要是 String、Array、TypedArray、Map 以及 Set 这种可迭代内建物件,就可以套用在我们写的通用 transduce。

String

例如,现在在给定字串中找出字元是母音,并要将其转换成大写,就可以用 transduce

const toUpper = str => str.toUpperCase();
const isVowel = char => ['a', 'e', 'i', 'o', 'u'].includes(char.toLowerCase())

transduce(
  compose(map(toUpper), filter(isVowel)),
  (str, char) => str + char,
  '',
  'JingHuangSu',
) // IUAU

Map

当然 Map 也没问题,就像先前的范例,每个数值乘三并且只保留偶数

const tripleIt = (num) => num * 3;
const isEven = (num) => num % 2 === 0;

const numMap = new Map();
numMap.set('a', 1);
numMap.set('b', 2);
numMap.set('c', 3);
numMap.set('d', 4);

transduce(
  compose(map(tripleIt), filter(isEven)),
  (acc, val) => (acc.push(val), acc),
  [],
  numMap.values(),
); // [6, 12]

Number (X)

但由於 Number 是不可迭代(iterable)的,所以就没有办法。

*compose, filter, map 前篇自写之函式

into

接下来就让我们尝试看看实作出 into, 而 ramda 也有一样的函式可供使用,在这里就先 demo 概念

由於要让 Object 也可以进行迭代,故要先将 transduce 稍微改写一下,

const transduce = (transducer, reducer, initValue, _collection) => {
    let acc = initValue;
    
    const collection = R.is(Object, _collection) ? R.toPairs(_collection) : _collection;
    
    for (const val of collection) {
        acc = transducer(reducer)(acc, val)
    }
    
    return acc;
}

就像前面提到的 into 是基於 transduce 延伸的概念,所以基底不变,只需要再进行判断其 init value 的型别,接下来就来打造出属於自己的 into 的函式!

const arrReducer = (acc, val) => (acc.push(val), acc)
const objectReducer = (obj, value) => Object.assign(obj, value);

const into = (to, transducer, from) => {
    if (Array.isArray(to)) return transduce(transducer, arrReducer, to, collection);
    else if (R.is(Object, to)) return transduce(transducer, objectReducer, to, collection);
    throw new Error('into only supports arrays and objects as `to`');
};

into(
  {},
  compose(map((kv) => ({[kv[0]]: kv[1].map(R.add(1))}))), 
  {a: [1, 2, 3], b: [2, 3, 4]},
) // {a: [2, 3, 4], b: [3, 4, 5]}

sequence

sequence 则是更只需要输入 transform 函式跟其要进行运算的资料结构就好!!

const sequence = (transducer, collection) => {
  if (Array.isArray(collection)) return transduce(transducer, arrReducer, [], collection);
  else if (R.is(Object, collection)) return transduce(transducer, objectReducer, {}, collection);
  throw new Error('unsupported collection type!!!');
};

sequence(
  compose(map((kv) => ({[kv[0]]: kv[1].map(R.add(1))}))), 
  {a: [1, 2, 3], b: [2, 3, 4]},
) // {a: [2, 3, 4], b: [3, 4, 5]}

小结

到目前已经将 transduce 基本概念介绍完了,本来想要用两部曲的方式介绍完所有概念,但笔者时间估算错误,决定将剩下的放到第三部曲。

感谢大家阅读!

NEXT: Type Signature

reference

  1. transduce tutorial
  2. 图片参照

<<:  Day 08: Creational patterns - Builder

>>:  Day8-滚动视差(上)_前有文字

25 | 【进阶教学】什麽是 WordPress 区块小工具?

由於 WordPress 是不停改进的 CMS 系统,它们在 2021 年的 WordPress ...

DAY26 linebot结果展示-2

原本使用者没有管理员权限,当输入[我要成为管理员]後将资料库的Root设为1来代表使用者获得权限。...

Day22 Let's ODOO: 继承Model来增加栏位

今天我们来示范透过继承Model来新增Field和更改Views里面的属性,用这种方法可以省去自己建...

[职场]掌握薪水谈判的秘诀,取得自己应有的报酬 & 完赛感言

如果你是一个有野心且不安於现状的人,薪水谈判是你一定要去做的事情。 在阅读这篇文章前,你可以先思考...

[13th][Day6] func

about func 有了变数、有了保留字之後,我们需要 方法/function/method/函式...