javascript - 在所有可迭代对象上懒惰地调用过滤器、查找(数组方法)

标签 javascript arrays functional-programming iterator

<分区>

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

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

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

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

会先把 fib 序列转储出来,这会导致浏览器因为内存消耗而崩溃(无限序列)。我能做的是手动调用 for 循环并在其中使用 conditionFunction。

有没有什么方法可以在(非数组)可迭代对象上延迟调用过滤器、查找、映射等?

最佳答案

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

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])

关于javascript - 在所有可迭代对象上懒惰地调用过滤器、查找(数组方法),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44326481/

相关文章:

javascript - 在页面选项卡中订阅edge.create

javascript - 如何使用 jQuery 获取重复表单中的字段值?

ios - Objective-C UIButton 网格生成

Java 函数式编程 : How to convert a if-else ladder inside for loop to functional style?

functional-programming - 有没有没有垃圾收集的函数式语言

javascript - 如何使此联系​​表起作用?

javascript - 带有 Animate.css 的文本淡入淡出效果 - 无法解释的行为

java - 为什么 ArrayList add() 和 add(int index, E) 复杂度是摊销常数时间?为什么 add() 不是 O(1),add(int index, E) 不是 O(n)?

ruby - 用 ruby​​ 将字符串数组变成一个句子

haskell - 函数式编程中的哪些技术难以学习但之后有用?