When Should One Use `(...)` or `[...]` for Array Comprehensions

When Should One Use (...) or [...] for Array Comprehensions?
What is the difference between the 2?

From Slack:

julia> @btime [x^2 for x in 1:10000];
  6.519 μs (2 allocations: 78.20 KiB)julia> @btime (x^2 for x in 1:10000);
  0.015 ns (0 allocations: 0 bytes)julia> @btime sum([x^2 for x in 1:10000]);
  8.855 μs (2 allocations: 78.20 KiB)julia> @btime sum((x^2 for x in 1:10000));
  1.409 ns (0 allocations: 0 bytes)julia> @btime sum(x^2 for x in 1:10000);
  1.410 ns (0 allocations: 0 bytes)

The code above was by @kimlaberinto.

Some answers were:

(x^2 for x in 1:10000) is just producing a generator.

The difference is that () is lazy while [] is eager. If you perform a reduction (like sum(x^2 for x in 1:10000) ), you won’t allocate anything.

Someone also mentioned one is heap allocated while the other is stack allocated.

Please add more information on the subject.

P. S.
I’m putting it here for knowledge preservation of a discussion I read (Not participated) on Slack.

4 Likes

Some methods explicitly expect AbstractArray as a parameter, not just any iterable in this case they will not work with a generator, a most basic example in this class is getindex.

Also if you materialize the array you can mutate it later, which might be relevant in some contexts.

1 Like

One thing to mention is that only [...] is an array comprehension, because it creates an array. Generators, (...), don’t, and aren’t called array comprehensions.

4 Likes

This post provides advanced analysis of the topic.

In the Julia manual, comprehensions written without the enclosing square brackets using generator expressions lie in the same Multi-dimensional Arrays section as comprehensions written with enclosing square brackets.