Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce tuple iteration: for (x, y) in (A, B) {} #877

Open
DmitryVasilevsky opened this issue Nov 29, 2023 · 3 comments
Open

Introduce tuple iteration: for (x, y) in (A, B) {} #877

DmitryVasilevsky opened this issue Nov 29, 2023 · 3 comments
Labels

Comments

@DmitryVasilevsky
Copy link
Contributor

DmitryVasilevsky commented Nov 29, 2023

Zipped family of functions and Enumerated function create new arrays, which isn't very obvious in a typical usage code and incur performance penalties. If we introduce this pattern, the need for Enumerated and Zipped family will be greatly reduced to the point we might want to remove them.

Here's an example from the old school of writing loops. (Which I belong to :)

        for i in 0..Length(xs)-1 {
            X(xs[i]);
        }

You have access to i and you can index an array any way you want. However it has drawbacks.

  • You have to know what index range is - i.e. from 0 to |xs|-1. In some languages indexes start with 1.
  • You need to be careful with index ranges and index expressions every time your write such loop. In practice most loops are over all the elements of the array.
  • You have to write indexing everywhere the 'current' element is used. If there're many of them it gets annoying. You need to think which element of which array is 'current' in every such place. In practice you often start you loop with a local variable holding the 'current' element: let x = xs[i].

A modern way of writing this is

        ApplyToEach(X, xs)

which I don't like much because it's hard to figure out the complexity by looking at the code. Another way is this.

        for q in xs {
            X(q);
        }

It solves the problem of the first approach but hides index from you. You can no longer use index. If you need index, you may write something like this.

        for i in IndexRange(xs) {
            X(xs[i]);
        }

It gives you index and hides array bounds. But it doesn't give you the 'current' element. So to get all the benefits you use 'Enumerated'.

        for (i,q) in Enumerated(xs) {
            R1Frac(1, i, q);
        }

Now, it's not efficient because it creates an array. Maybe we can solve this by allowing the following construction:

        for (i,q) in (IndexRange(xs), xs) {
            R1Frac(1, i, q);
        }

In this case, you have access to the index, 'current' element and index range is hidden.

Complication: What to do if the number of elements in these ranges are different? Use the shortest one? Fail execution?

@DmitryVasilevsky DmitryVasilevsky added enhancement New feature or request needs triage labels Nov 29, 2023
@sezna
Copy link
Contributor

sezna commented Jan 8, 2024

Would it also be acceptable to optimize Enumerated so it doesn't perform this allocation? That avoids the case where the two tuple elements are not the same size.

@swernli
Copy link
Collaborator

swernli commented Feb 1, 2024

Would it also be acceptable to optimize Enumerated so it doesn't perform this allocation? That avoids the case where the two tuple elements are not the same size.

Unfortunately, no. There is no concept of an iterator in Q#, so there is nothing we could return that would satisfy iteration other than a new array.

@sezna
Copy link
Contributor

sezna commented Feb 13, 2024

Hm, could we introduce the concept of an iterator, and hook it up to for loops?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants