如何执行数组迭代时,为了避免中间结果? [英] How to avoid intermediate results when performing array iterations?

查看:161
本文介绍了如何执行数组迭代时,为了避免中间结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用数组时,经常需要重新中间presentations - 特别是在功能的编程接口,其中的数据一般被当作一成不变的:

When working with arrays, intermediate representations are needed regularly - particularly in connection with functional programming, in which data is often treated as immutable:

const square = x => x * x;
const odd = x => (x & 1) === 1;
let xs = [1,2,3,4,5,6,7,8,9];

// unnecessary intermediate array:
xs.map(square).filter(odd); // [1,4,9,16,25,36,49,64,81] => [1,9,25,49,81]

// even worse:
xs.map(square).filter(odd).slice(0, 2); // [1,9]

我怎样才能避免这种行为的JavaScript / ECMAScript的2015年,获得更高效的迭代算法?

How can I avoid this behavior in Javascript/Ecmascript 2015 to obtain more efficient iterative algorithms?

推荐答案

传感器的一种可能的方式,以避免迭代算法中的中间结果。为了了解他们更好,你必须认识到,这本身传感器是相当没有意义的:

Transducers are one possible way to avoid intermediate results within iterative algorithms. In order to understand them better you have to realize, that transducers by themselves are rather pointless:

// map transducer
let map = tf => rf => acc => x => rf(acc)(tf(x));

我们为什么要传递给地图还原功能,对于每次调用时所需的功能是永远不变的, CONCAT 即?

Why should we pass a reducing function to map for each invocation when that required function is always the same, concat namely?

回答这个问题是位于官方传感器定义:

The answer to this question is located in the official transducer definition:

探头是的可组合的算法转换。

传感器开发他们的前pressive功率仅与函数组合结合:

Transducer develop their expressive power only in conjunction with function composition:

const comp = f => g => x => f(g(x));
let xf = comp(filter(gt3))(map(inc));

foldL(xf(append))([])(xs);

补偿传递传感器(任意数量的过滤器地图)和单一功能的降低(追加)作为其最后一个参数。从补偿构建,无需中间阵列转换序列。每个阵列元素穿过整个序列之前的下一个元素为线

comp is passed an arbitrary number of transducers (filter and map) and a single reduction function (append) as its final argument. From that comp builds a transformation sequence that requires no intermediate arrays. Each array element passes through the entire sequence before the next element is in line.

在这一点上,<​​code>地图定义传感器是可以理解的:可组合需要匹配函数签名

At this point, the definition of the map transducer is understandable: Composability requires matching function signatures.

请注意,该换能器叠层的评价的顺序推移从左至右并因此反对功能组合物的正常顺序

Note that the order of evaluation of the transducer stack goes from left to right and is thus opposed to the normal order of function composition.

换能器的一个重要特性是它们的提前退出反复处理能力。在选择的实现,这种行为是通过实现这两个传感器和与foldl 在延续传递风格来实现的。另一种方法是懒惰的评价。下面是CPS实现:

An important property of transducers is their ability to exit iterative processes early. In the chosen implementation, this behavior is achieved by implementing both transducers and foldL in continuation passing style. An alternative would be lazy evaluation. Here is the CPS implementation:

const foldL = rf => acc => xs => {
  return xs.length
   ? rf(acc)(xs[0])(acc_ => foldL(rf)(acc_)(xs.slice(1)))
   : acc;
};

// transducers
const map = tf => rf => acc => x => cont => rf(acc)(tf(x))(cont);
const filter = pred => rf => acc => x => cont => pred(x) ? rf(acc)(x)(cont) : cont(acc);
const takeN = n => rf => acc => x => cont =>
 acc.length < n - 1 ? rf(acc)(x)(cont) : rf(acc)(x)(id);

// reducer
const append = xs => ys => xs.concat(ys);

// transformers
const inc = x => ++x;
const gt3 = x => x > 3;

const comp = f => g => x => f(g(x));
const liftC2 = f => x => y => cont => cont(f(x)(y));
const id = x => x;

let xs = [1,3,5,7,9,11];

let xf = comp(filter(gt3))(map(inc));
foldL(xf(liftC2(append)))([])(xs); // [6,8,10,12]

xf = comp(comp(filter(gt3))(map(inc)))(takeN(2));
foldL(xf(liftC2(append)))([])(xs); // [6,8]

请注意,这个实现是比较概念的证明,也没有成熟的解决方案。换能器的显而易​​见的好处是:

Please note that this implementation is more of a proof of concept and no full-blown solution. The obvious benefits of transducers are:


  • 无中间再presentations

  • 纯粹的功能和简洁的解决方案

  • 泛型(与任何骨料/收藏,不只是阵列工作)

  • 非凡code可重用性(减速/变压器是他们一贯的签名常用功能)

理论上,CPS是一样快势在必行环路,至少在2015年的ECMAScript,因为所有的尾呼叫具有相同的返回点,并且可以从而共享相同的堆栈帧(TCO)。

Theoretically, CPS is as fast as imperative loops, at least in Ecmascript 2015, since all tail calls have the same return point and can thereby share the same stack frame (TCO).

它被认为是有争议的这种做法是否是一个JavaScript解决方案不够地道。我preFER这种实用的风格。然而,最常见的传感器库在对象样式实现,应该找开发商OO更熟悉。

It is considered controversial whether this approach is idiomatic enough for a Javascript solution. I prefer this functional style. However, the most common transducer libraries are implemented in object style and should look more familiar to OO developers.

这篇关于如何执行数组迭代时,为了避免中间结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆