【问题标题】:calling filter, find (array methods) lazily on all iterables调用过滤器,在所有可迭代对象上懒惰地查找(数组方法)
【发布时间】:2017-11-03 17:17:41
【问题描述】:

问题很简单:有什么方法可以调用数组方法,如filterfindmap 等,不仅在数组上,而且在任何可迭代对象上?

过滤、查找、映射等不仅对数组有意义,而且通常对序列有意义。而iterable是一个可以迭代的序列,所以过滤序列、查找(序列中的第一个元素)、映射序列的元素......无论序列是什么都是有意义的。

想象一下这样的情况:一个无限的生成器(例如斐波那契数列,生成器一次返回一个项目)。我想找到满足给定条件的第一个元素。像这样使用传播:

[...fib()].find(conditionFunction)

会先让fib序列被转储,导致浏览器因为内存消耗而崩溃(无限序列)。我可以做的是手动调用for循环并在里面使用conditionFunction。

有没有办法在(非数组)可迭代对象上懒惰地调用过滤器、查找、映射等?

【问题讨论】:

    标签: javascript arrays functional-programming iterator


    【解决方案1】:

    不幸的是,像 find 这样的迭代器方法是使用序列协议 (.length + Get) 实现的,而不是使用迭代器协议。您可以尝试使用代理来欺骗他们,使可迭代对象模拟序列,例如

    let asArray = iterable => new Proxy(iterable, {
    
        get(target, prop, receiver) {
            if(prop === 'length')
                return Number.MAX_SAFE_INTEGER;
            return target.next().value;
        }
    });
    
    
    function *fib() {
        let [a, b] = [1, 1];
    
        while (1) {
            yield b;
            [a, b] = [b, a + b];
        }
    }
    
    found = [].find.call(
        asArray(fib()),
        x => x > 500);
    
    console.log(found);

    需要更多的工作,但你明白了。

    另一种(也是 IMO 更简洁的方法)是重新实现迭代器方法以支持可迭代对象(并成为生成器本身)。幸运的是,这很简单:

    function *lazyMap(iter, fn) {
        for (let x of iter)
            yield fn(x);
    }
    
    
    for (let x of lazyMap(fib(), x => x + ' hey'))...
    

    下面是如何使用可链接的方法创建一个惰性迭代器对象:

    let iter = function (it) {
        return new _iter(it);
    };
    
    let _iter = function(it) {
        this.it = it;
    };
    
    _iter.prototype[Symbol.iterator] = function *() {
        for (let x of this.it) {
            yield x;
        }
    };
    
    _iter.prototype.map = function (fn) {
        let _it = this.it;
        return iter((function *() {
            for (let x of _it) {
                yield fn(x)
            }
        })())
    };
    
    _iter.prototype.take = function (n) {
        let _it = this.it;
        return iter((function *() {
            for (let x of _it) {
                yield x;
                if (!--n)
                    break;
            }
        })())
    };
    
    // @TODO: filter, find, takeWhile, dropWhile etc
    
    // example:
    
    
    // endless fibonacci generator
    function *fib() {
        let [a, b] = [1, 1];
    
        while (1) {
            yield b;
            [a, b] = [b, a + b];
        }
    }
    
    // get first 10 fibs, multiplied by 11
    a =  iter(fib())
         .map(x => x * 11)
         .take(10)
    
    console.log([...a])

    【讨论】:

    • 不要经常得到这么准确的答案,谢谢!!! PS您认为重新实现过滤器/查找/映射/等是否有意义?到可迭代对象上,以创建真正的管道处理,无论您迭代什么结构(以及如何)?还是只有我……?
    • @ducin:是的,绝对有道理,请参阅更新以了解可能的方法。
    【解决方案2】:

    使用iter-ops库(我是作者):

    import {pipe, start, take} from 'iter-ops';
    
    const result = pipe(
        fib(),
        start((value, index) => {
            // return a truthy value here when found what you need
        }),
        take(1)
    );
    
    console.log('found:', result.first);
    

    【讨论】:

      猜你喜欢
      • 2019-06-20
      • 2016-03-01
      • 2018-04-17
      • 1970-01-01
      • 2023-03-18
      • 2022-11-13
      • 2014-10-27
      • 2019-04-19
      • 1970-01-01
      相关资源
      最近更新 更多