I could go with an iterator equivalent, but I'd like to defer that to
the seemingly-planned "iterlib" thing that's been considered since
before ES2015 was released. Something that works with arrays is good
enough for now.

BTW, your `ziperator` isn't really the same as my `Array.interpolate`
(which is better named `Array.interleave`). It needs to be this:

```js
function *ziperator(...iters) {
    for (let i = 0; i < iters.length; i++) {
        iters[i] = iters[i][Symbol.iterator]()
    }
    while (true) {
        for (let i = 0; i < iters.length; i++) {
            const {done, value} = iters[i].next()
            if (done) return undefined
            yield value
        }
    }
}
```

The optimized version is pretty straightforward (using private fields
+ methods here):

```js
function ziperator(...iters) { return new InterleavedIterator(iters) }

class InterleavedIterator {
    #iters, #index
    constructor(iters) { this.#iters = iters; this.#index = 0 }
    [Symbol.iterator]() { return this }
    next(value) { return this.#invoke("next", value) }
    throw(value) { return this.#invoke("throw", value) }
    return(value) { return this.#invoke("return", value) }
    #invoke(method, value) {
        if (this.#iters == null) return {done: true, value: undefined}
        const index = this.#index
        this.#index = (index + 1) % this.#iters.length
        const {done, value} = this.#iters[index][method](value)
        if (done) this.#iters = undefined
        return {done, value}
    }
}
```

-----

Isiah Meadows
[email protected]
www.isiahmeadows.com
On Fri, Dec 14, 2018 at 2:55 PM Mike Samuel <[email protected]> wrote:
>
>
>
> On Fri, Dec 14, 2018 at 2:26 PM Isiah Meadows <[email protected]> wrote:
>>
>> The main difference with that loop is that it's generalized to any number of 
>> arrays, not just two with the second array having length one less than the 
>> first. Otherwise, it'd look exactly the same. BTW, I like this route 
>> (`Array.interleave`) better since it doesn't have to result in just a single 
>> string result - it could just be an array of strings plugged into some API 
>> instead, or it could be procedurally streamed out in chunks.
>
>
> Fair enough.
> If you're not looking for something template tag specific then a simple zip 
> over iterators should do it?
>
> function *ziperator(iterators) {
>     let progressed;
>     do {
>         progressed = false;
>         for (let iterator of iterators) {
>             for (let element of iterator) {
>                 yield element;
>                 progressed = true;
>                 break;
>             }
>         }
>     } while (progressed);
> }
>
> console.log(Array.from(ziperator([ ['a', 'b', 'c'][Symbol.iterator](), [1, 
> 2][Symbol.iterator]() ])).join(''));
> // -> a1b2c
>
> (but optimized :)
>
>
>
>>
>> On Fri, Dec 14, 2018 at 14:04 Mike Samuel <[email protected]> wrote:
>>>
>>>
>>>
>>> On Fri, Dec 14, 2018 at 12:51 PM Isiah Meadows <[email protected]> 
>>> wrote:
>>>>
>>>> I'll point out Kai could be on to something, although I disagree `zip` 
>>>> would be the right abstraction. Maybe `Array.interleave(...arrays)`? You 
>>>> could do `Array.interleave(template, args).map(String).join("")` for 
>>>> similar effect, and it'd be more generally useful.
>>>>
>>>> The key here is that iteration would stop after the index hits any array's 
>>>> length, so it'd be polyfilled kinda like this:
>>>>
>>>> ```js
>>>> Array.interpolate = (...args) => {
>>>>     let ret = []
>>>>     let lengths = []
>>>>     let count = 0
>>>>     for (let i = 0; i < args.length; i++) {
>>>>         lengths[i] = args[i].count
>>>>     }
>>>>     for (let index = 0; ; index++) {
>>>>         for (let i = 0; i < args.length; i++) {
>>>>             if (index === lengths[i]) return ret
>>>>             ret[count++] = args[i][index]
>>>>         }
>>>>     }
>>>> }
>>>> ```
>>>>
>>>> (This could be optimized, though.)
>>>
>>>
>>> As a data point, something like this loop appears in most of the template 
>>> tags I've written but
>>> it's never had these precise semantics so I didn't bother putting it into 
>>> template-tag-common.
>>>
>>> That library makes it easy to split the operation of a template tag into 3 
>>> stages:
>>> 1. An optional configuration stage accessed by calling the template tag as 
>>> a regular function: mytag({ /* options */)`...`
>>> 2. Static analysis over the strings.   This is memoized.
>>> 3. Computing a result from (options, strings, results of step 2, 
>>> interoplated values)
>>>
>>> The final loop (step 3) in the template tags I maintain tends to looks like
>>>
>>> function computeResult(options, staticState /* from step 2 */, strings, 
>>> ...values) {
>>>   let n = values.length;  // Could do Math.max(strings.length - 1, 
>>> values.length);
>>>   let result = strings[0];  // Usually strings.raw
>>>   for (let i = 0; i < n;) {
>>>     const interpolatedValue = f(options, staticState[i], values[i]);
>>>     // Sometimes code here looks backwards at the result to see if it needs 
>>> to avoid token-merging hazards.
>>>     result += interpolatedValue;
>>>     result += strings[++i];
>>>   }
>>>   return wrapResult(result);  // Produce a value of a type that 
>>> encapsulates the tag's security guarantees.
>>> }
>>>
>>>
>>>
>>>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to