Range with end

This works as expected:

b = rand(10)
b[2:end]
view(b, 2:10)

but not this:

julia> view(b, 2:end)
ERROR: syntax: missing last argument in "1:" range expression 

Do I really have to use:

view(b, 2:length(b))

Is there is a more clever way to exclude the first element of a vector?

2 Likes

No, that’s the advantage of the macro form:

@view b[2:end]
11 Likes

The end keyword for the last index of an array only works inside [...] indexing expressions.

4 Likes

Has there been an effort to make “end” more like a keyword that can get passed around? Or is this considered a bad idea?

ie:

a = 2:end
b = [1,2,3,4,5]
c = b[a]
1 Like

One slight nuance is that a[2:end] is actually lowered to getindex(a, 2:lastindex(a)), not length, which is an important distinction for OffsetArrays or strings.

4 Likes

The problem is that we use end to denote the end of blocks, so treating it as a symbol everywhere would lead to a lot of parsing ambiguities. That’s also why you can’t use begin ... end blocks inside indexing expressions, for example.

Another problem with this approach is that you basically end up writing a small computer algebra system and you would need a way to trace through arbitrary functions, since you don’t know the last index of the array in advance. I implemented something like that in a more limited way as tbegin and tend in CoolTensors.jl, so it’s not impossible to make this work for the most common cases, but it’s very difficult and brittle to implement this in all generality, so it’s suitable for Base.

2 Likes

end is a keyword that is also used to end blocks so it would be hard to also parse it as an end-of-array indices marker in general contexts. However, it would certainly be possible to define an END object that behaves very similarly:

import Base: Colon, getindex
struct End; end # singleton
const END = End()
struct EndUnitRange
    start::Int
end
(::Colon)(start::Integer, ::End) = EndUnitRange(start)
getindex(A::Array, r::EndUnitRange) = A[r.start:end]
# ... and other AbstractRange methods, probably…

You can then do:

julia> a = 2:END;

julia> b = [1,2,3,4,5];

julia> c = b[a]
4-element Array{Int64,1}:
 2
 3
 4
 5

However, a problem with this is that such a 2:END range object has little meaning except in getindex and a few similar contexts, and for all of these specialized methods would have to be defined — you can’t use it in most contexts where an ordinary range would work. (What is length(a)? What does for x in a mean?) So I’m not sure how useful it would be in practice. (But someone could certainly make a package for it if they felt otherwise.)

In contrast, the current 2:end syntax is only available in object[...] contexts where the meaning of end is unambiguous. And since end is “lowered” to lastindex(object), individual types don’t need to do anything special to support it.

7 Likes

I think that just assigning lastindex etc to a variable and using it for arithmetic is always better in this case.