Settings

Theme

Ask HN: JS's for-of loop conceptually flawed, or is it just me?

2 points by antrion 10 years ago · 9 comments · 1 min read


When working with generators, two identical for-of loops don't show the same behaviour. Specifically, the first one will behave as expected, whereas the second one will terminate immediately. What is this?

I know that the iterator `g` will signal the value `done` after the first loop, but this behaviour was unexpected and really counterintuitive to me

Note: contrary to this example, for-of loops of arrays will work as I expected. It just doesn't make sense

Code example:

> function* gen() { yield* [1,2,3] }

undefined

> let g = gen()

undefined

> for (let el of g) console.log(el)

1

2

3

undefined

> for (let el of g) console.log(el)

undefined

edit: layout

tantalor 10 years ago

Same in python,

    >>> def gen(): yield 1; yield 2; yield 3
    ... 
    >>> g = gen()
    >>> for el in g: print el
    ... 
    1
    2
    3
    >>> for el in g: print el
    ... 
    >>>
detaro 10 years ago

You reached the end of the iterator with the first loop, so there aren't any elements left in it to iterate over. That's consistent to how iterators work in other languages at least.

  • antrionOP 10 years ago

    I know that as per the iterator spec this is normal behaviour. It just seems to me that the behaviour that follows from this is really quite strange. When I want to be able to loop over the contents twice, how am I supposed to to that?

    • detaro 10 years ago

      Depends on what your generator does. If it just does simple calculations, just generate a second instance of it.

      Otherwise there are constructions that provide a virtual copy of an iterator and cache the values they still need for the second loop.

      Or just don't use an iterator if it doesn't fit the model you need, or turn it into an array before.

      (Part of) the idea behind generators is that you don't store all results, because they could be a long or possibly even infinite sequence. E.g. in Python the basic iterator is range(X), which returns the numbers from 0 to X-1. If X=100000, it is a bad idea to generate a giant list of all those numbers, just because you want to know how often you ran through a loop.

      • antrionOP 10 years ago

        How would you copy it then? I have tried the npm deep-copy library, but that didn't work (Symbol.Iterator isn't enumerable for example)

        edit: I just thought, I could make a function, bind that to a variable and regen the generator again and again

    • tantalor 10 years ago

      Store the contents of the generator in memory (an array).

      Or make a second instance of the generator.

      • antrionOP 10 years ago

        My specific use case was working with 'infinite lists', so the first options is ruled out.

        The second option could be a solution, but the specific case was reusing a variable `const nums = iu.iterate(x => x+1, 1)`, and quite extensively. (It's just a helper function to make a list of all natural numbers)

        It would be so much nicer if I could just reuse this variable throughout my code

  • antrionOP 10 years ago

    And is it also that hard to loop twice in said languages?

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection