代码之家  ›  专栏  ›  技术社区  ›  georg

从阵列创建的生成器列表中生成

  •  14
  • georg  · 技术社区  · 7 年前

    我有一个递归生成器

    var obj = [1,2,3,[4,5,[6,7,8],9],10]
    
    function *flat(x) {
        if (Array.isArray(x))
            for (let y of x)
                yield *flat(y)
        else
            yield 'foo' + x;
    
    }
    
    console.log([...flat(obj)])

    它很好用,但我不喜欢 for 部分有没有一种方法可以从功能上编写它?我试过了

    if (Array.isArray(x))
       yield *x.map(flat)
    

    这不起作用。

    有没有一种方法可以在没有 对于

    5 回复  |  直到 7 年前
        1
  •  3
  •   Nina Scholz    7 年前

    rest parameters ... 并检查rest数组的长度,以便再次调用生成器

    function* flat(a, ...r) {
        if (Array.isArray(a)) {
            yield* flat(...a);
        } else {
            yield 'foo' + a;
        }
        if (r.length) {
            yield* flat(...r);
        }
    }
    
    var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
    console.log([...flat(obj)])
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    spread

    function* spread(g, a, ...r) {
        yield* g(a);
        if (r.length) {
            yield* spread(g, ...r);
        }
    }
    
    function* flat(a) {
        if (Array.isArray(a)) {
            yield* spread(flat, ...a);
        } else {
            yield 'foo' + a;
        }
    }
    
    var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
    console.log([...flat(obj)])
    .作为控制台包装{最大高度:100%!重要;顶部:0;}
        2
  •  2
  •   trincot Jakube    7 年前

    这个 map 这是一个好主意,但您需要将生成的生成器对象数组减少为一个生成器对象:

    function *flat(x) {
        if (Array.isArray(x)) 
            yield *x.map(flat).reduce((a, b) => function*() { yield *a; yield *b }());
        else
            yield 'foo' + x;
    }
    
    var obj = [1,2,3,[4,5,[6,7,8],9],10];
    console.log([...flat(obj)]);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
        3
  •  2
  •   Bergi    7 年前

    for 循环?

    我们要寻找的是迭代器的函数组合子:

    function* of(x) { // also known as `pure` or `return`
        yield x;
    }
    function map(f) { return function* (xs) { // also known as `fmap`
        for (const x of xs)
            yield f(x);
    }
    function* join(xss) { // also known as `concat` (not `append`!) or `flatten` (but non-recursive!)
        for (const xs of xss)
            for (const x of xs)
                yield x;
    }
    function chain(f) { return function* (xs) { // also known as `concatMap` or `bind`
        for (const x of xs)
            const ys = f(x);
            for (const y of ys)
                yield y;
    }
    // or const chain = f => compose(concat, map(f)) :-)
    

    现在我们可以将迭代器视为 单子 ,不再关心实施。

    yield* xs 上面(基本上)只有糖

    for (const x of xs)
        yield x;
    

    在您的实现中看起来奇怪的是外部循环和内部非循环之间的差异。在一个最优的世界里,会有一个 yield** 语法 那做了什么 join

    function* flat(x) {
        if (Array.isArray(x))
            yield* chain(flat)(x);
        else
            yield* of('foo' + x); // foreshadowing
    }
    

    或者只是

    function flat(x) {
        return Array.isArray(x) ? chain(flat)(x) : of('foo' + x);
    }
    
        4
  •  1
  •   Yury Tarabanko    7 年前

    您可以将阵列简化为生成器。但在我看来,这比for loop更糟糕(虽然是功能性的:)

    var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10]
    
    function* flat(x) {
      if (Array.isArray(x))
        yield * x.reduceRight(
          (f, y) => function*() {
            yield * flat(y);
            yield * f()
          },
          function*() {}
        )()
      else
        yield 'foo' + x;
    
    }
    
    console.log([...flat(obj)])
        5
  •  -1
  •   tilin    7 年前

    var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
    
    function* flat(x) {
        if (Array.isArray(x)) {
            yield x.map(v => {
                return [...flat(v)].join();
            });
        } else yield "foo" + x;
    }
    
    console.log([...flat(obj)]);