链接映射减少过滤器时如何减少迭代?

ayu*_*hgp 3 javascript monads functional-programming

我一直在阅读有关mapreduce的文章,filter因为它们在 React 和 FP 中的使用量很大。如果我们写这样的东西:

let myArr = [1,2,3,4,5,6,7,8,9]
let sumOfDoubleOfOddNumbers = myArr.filter(num => num % 2)
                                   .map(num => num * 2)
                                   .reduce((acc, currVal) => acc + currVal, 0);
Run Code Online (Sandbox Code Playgroud)

运行 3 个不同的循环。

我也读过有关 Java 8 流的内容,并且知道它们使用所谓的 monad,即首先存储计算。它们仅在一次迭代中执行一次。例如,

Stream.of("d2", "a2", "b1", "b3", "c")
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("A");
    })
    .forEach(s -> System.out.println("forEach: " + s));

// map:     d2
// filter:  D2
// map:     a2
// filter:  A2
// forEach: A2
// map:     b1
// filter:  B1
// map:     b3
// filter:  B3
// map:     c
// filter:  C
Run Code Online (Sandbox Code Playgroud)

PS:Java代码取自:http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/

还有许多其他语言也使用相同的方法。在 JS 中是否有同样的方法?

geo*_*org 5

这是您的 Java 代码的精确克隆。与 Bergi 的解决方案不同,无需修改全局原型。

class Stream {
    constructor(iter) {
        this.iter = iter;
    }

    * [Symbol.iterator]() {
        yield* this.iter;
    }

    static of(...args) {
        return new this(function* () {
            yield* args
        }());
    }

    _chain(next) {
        return new this.constructor(next.call(this));
    }

    map(fn) {
        return this._chain(function* () {
            for (let a of this)
                yield fn(a);
        });
    }

    filter(fn) {
        return this._chain(function* () {
            for (let a of this)
                if (fn(a))
                    yield (a);
        });
    }

    forEach(fn) {
        for (let a of this)
            fn(a)
    }
}


Stream.of("d2", "a2", "b1", "b3", "c")
    .map(s => {
        console.log("map: " + s);
        return s.toUpperCase();
    })
    .filter(s => {
        console.log("filter: " + s);
        return s.startsWith("A");
    })
    .forEach(s => console.log('forEach', s));
Run Code Online (Sandbox Code Playgroud)

实际上,链接功能可以与特定迭代器解耦,以提供通用框架:

// polyfill, remove me later on
Array.prototype.values = Array.prototype.values || function* () { yield* this };

class Iter {
    constructor(iter)     { this.iter = iter }
    * [Symbol.iterator]() { yield* this.iter }
    static of(...args)    { return this.from(args) }
    static from(args)     { return new this(args.values()) }
    _(gen)                { return new this.constructor(gen.call(this)) }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以将任意生成器放入其中,包括预定义的和临时的,例如:

let map = fn => function* () {
    for (let a of this)
        yield fn(a);
};

let filter = fn => function* () {
    for (let a of this)
        if (fn(a))
            yield (a);
};

it = Iter.of("d2", "a2", "b1", "b3", "c", "a000")
    ._(map(s => s.toUpperCase()))
    ._(filter(s => s.startsWith("A")))
    ._(function*() {
        for (let x of [...this].sort())
            yield x;
    });

console.log([...it])
Run Code Online (Sandbox Code Playgroud)