我对换能器的理解和js代码是否正确

时间:2019-06-08 13:50:35

标签: javascript clojure functional-programming

我已阅读有关换能器的网络文章

Js

  • 换能器:JavaScript中的高效数据处理管道@ Eric Elliott -Medium
  • 了解JavaScript的传感器@ Roman Liutikov-Medium

难以理解...

  • 什么是换能器?
  • 更简单的JavaScript转换器
  • 如何使用换能器使数据转换效率更高

Clojure

  • Rich Hickey-cognitect即将推出TRANSDUCERS
  • Transducers-Clojure.org

我阅读了大约2页的Clojure官方教程,并了解了基本语法。我参考了内置函数参考以了解换能器示例代码。

我对以上两篇文章的理解大概是75%...

我的问题

我想知道以下理解/ js代码是正确还是不正确。请帮助我。<(_ _)>

关于传感器

  1. compose()返回的值是一个换能器。
  2. 通过将传递器传递给transduce()函数作为参数来执行,并且(2)通过将数组直接传递给transducer()来执行传递器。
  3. 在(2)的过程中,不会生成中间值,并且将执行以下链接这样的高效过程。

我的代码

"use strict";

const map = fn => arr => arr.map(fn),
filter = fn => arr => arr.filter(fn),
addReducer = arr => arr.reduce((acc, num) => acc + num, 0),
add1 = n => n + 1,
even = n => n % 2 === 0,

compose = (...fns) => initVal => fns.reduce((acc, fn) => fn(acc), initVal),
transduce = (xform, reducer, arr ) => reducer( xform(arr) );



const arr = [1,2,3],
transducer = compose(  /* called transducer or xform */
   map( add1 ), // 2,3,4
   filter( even ), // 2,4
);

console.log( transducer(arr) ) // 2,4
console.log( transduce(transducer, addReducer, arr) ) // 6

2 个答案:

答案 0 :(得分:1)

换能器利用了以下事实:函数组成从arity中抽象出来,即可以返回函数而不是“正常值”:

const comp = f => g => x => f(g(x));

const add = x => y => x + y;

const sqr = x => x * x;

const add9 = comp(add) (sqr) (3); // returns a lambda

console.log(
  add9(6)); // 15

现在换能器本身很无聊:

reduce => acc => x => /* body is specific to the transducer at hand */

这只是一个需要一个减速器的闭包(即结合了两个参数的二进制函数),然后可以将其直接馈送到您喜欢的减速函数中。

让我们看一下地图转换器:

const mapper = f => (reduce => acc => x =>
  reduce(acc) (f(x)));

多余的括号仅说明了换能器的闭合。在这种情况下,它将关闭我们的转换函数f。接下来,我们将应用它:

// map transducer

const mapper = f => reduce => acc => x =>
  reduce(acc) (f(x));

// my favorite fold (reducing function)

const arrFold = alg => zero => xs => {
  let acc = zero;

  for (let i = 0; i < xs.length; i++)
    acc = alg(acc) (xs[i], i);

  return acc;
};

// reducer

const add = x => y => x + y;

// transformer

const sqr = x => x * x;

// MAIN

const main = arrFold(mapper(sqr) (add)) (0);

console.log(
  main([1,2,3])); // 14

嗯,不是那么令人印象深刻吧?换能器的有功功率是由其与功能组成的组合得出的:

// map transducer

const mapper = f => reduce => acc => x =>
  reduce(acc) (f(x));

// filter transducer

const filterer = p => reduce => acc => x =>
  p(x) ? reduce(acc) (x) : acc;
  
// my favorite fold (reducing function)

const arrFold = alg => zero => xs => {
  let acc = zero;

  for (let i = 0; i < xs.length; i++)
    acc = alg(acc) (xs[i], i);

  return acc;
};

// helpers

const add = x => y => x + y; // reducer
const sqr = x => x * x; // transformer
const isOdd = x => (x & 1) === 1; // predicate
const comp = f => g => x => f(g(x));

// MAIN

const main = arrFold(comp(filterer(isOdd)) (mapper(sqr)) (add)) (0);

console.log(
  main([1,2,3])); // 10

尽管我们涉及两个传感器,但仅Array进行一次遍历。此属性称为循环融合。由于换能器组合返回了另一个功能,因此评估顺序是相反的,即从左到右,而功能组合通常是从右到左。

可重用性是另一个优势。您只需定义一次传感器,就可以一次全部使用所有可折叠数据类型。

值得注意的是,transduce只是一个便捷功能,对于理解该概念并不重要。

关于换能器,这差不多可以说了。

答案 1 :(得分:1)

您编写的代码与换能器无关。您对filterm̀ap的定义表明它使用普通的JS filtermap

const map = fn => arr => arr.map (fn),
const filter = fn => arr => arr.filter (fn),

const combo = compose(map(add1), filter(even));
combo(arr); ==> [2, 4]

发生的情况是,初始数组通过map传递到add1,这将产生数组[2, 3, 4],然后它通过{{1}传递到filter },并创建了一个新数组even

换能器中相同:

[2, 4]

所以区别是,当您将const arr = [1, 2, 3]; const add1 = n => n + 1; const even = n => n% 2 === 0; const compose = (...fns) => { const [firstFunc, ...restFuncs] = fns.reverse(); return (...args) => restFuncs.reduce((acc, fn) => fn(acc), firstFunc(...args)); }; const mapping = fn => join => (acc, e) => join(acc, fn(e)); const filtering = isIncluded => join => (acc, e) => isIncluded(e) ? join(acc, e) : acc; const transducer = compose(mapping(add1), filtering(even)); const arrayJoin = (acc, e) => ([...acc, e]); const result = arr.reduce(transducer(arrayJoin), []); console.log(result);传递到换能器时,就是这样:

join

mapping(add1)(filtering(even)(arrayAdd)) 是添加到某些集合的唯一步骤。当filtering调用mapping时,直接调用join。这就是为什么签名filtering在工作部分和(acc, e)函数上相同的原因。当代码运行时,添加和过滤同时完成,仅生成一个数组,没有中间值。