Semicolon is sometimes significant inside square brackets

Take the following example:

julia> [1]
1-element Vector{Int64}:
 1

julia> [1;]
1-element Vector{Int64}:
 1

In this case, there is no effect of putting a semicolon after the vector element. So what changes in the following case?

julia> [1:2]
1-element Vector{UnitRange{Int64}}:
 1:2

julia> [1:2;]
2-element Vector{Int64}:
 1
 2

Because I read semicolons as a way to effectivly end the line without an actual line break, making [1:2;] the same as

julia> [1:2
       ]
1-element Vector{UnitRange{Int64}}:
 1:2

, which is the same output as no semocolon, and different from the case with the semicolon. So how do semicolons actually influence code? Is it an effective newline in all cases but this one, or is there some more fundamental and consistent behaviour going on?

A sidequestion: is [1:2;] ever reccomended? It does the same as collect(1:2) in fewer characters, but it seems less clear and less widespread, so I am wondering about the place of this syntax in the Julia programming language.

1 Like

Instead of thinking of it as collect, it’s better to think of it as vcat.

You can vertically concatenate vectors with semicolons:

julia> [[1, 2]; [3, 4, 5]]
5-element Vector{Int64}:
 1
 2
 3
 4
 5

and you can also use it in matrix builder notation, using ;; for horizontal concatenation:

julia> H = [1; 2;; 3; 4]
2Ă—2 Matrix{Int64}:
 1  3
 2  4

julia> [  0; [5,6];; [7 8]; H ]
3Ă—3 Matrix{Int64}:
 0  7  8
 5  1  3
 6  2  4

where the semicolons are being used for vertical and horizontal concatenation. You can also space it out with newlines:

julia> [   0    7 8
         [5,6]   H   ]
3Ă—3 Matrix{Int64}:
 0  7  8
 5  1  3
 6  2  4

which is, again, consistent with the vector version:

julia> [1:2
        3:5]
5-element Vector{Int64}:
 1
 2
 3
 4
 5

However, it looks like the difference when there is only a single element is a special case. They’re parsed differently:

julia> :([1:2
       ]).head
:vect

julia> :([1:2;]).head
:vcat

I’m not entirely sure that this parsing rule is the best decision, but it’s what we have for now.

2 Likes

I would write collect(1:2) for clarity and use [1:2;] only in instruction material to make a point about the syntax rules, except in special cases for example when it resonates with neighboring code which makes the whole thing easier to read. Example:

julia> a = 1:2
1:2

julia> [a;]
2-element Vector{Int64}:
 1
 2

julia> [a;;]
2Ă—1 Matrix{Int64}:
 1
 2

julia> [a;;;]
2Ă—1Ă—1 Array{Int64, 3}:
[:, :, 1] =
 1
 2

If I need these three operations for some reason, I would probably write [a;] for “symmetry”.

1 Like

I just stumbled upon the following use of ;. This one I would strictly reserve to code golf and obfuscation contests :slight_smile:

a = [[1,2],[3]]
[a...;]
1 Like

Unless I’m mistaken, [x;] is not just like vcat, but is the same thing, ie, the former is syntax sugar for the latter.

1 Like