# Using end variable in ranges passed to functions other than getindex?

**URL:** <https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591>\
**Category:** General Usage\
**Tags:** indexing\
**Created:** [December 7, 2017, 2:04am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591 "2017-12-07T02:04:44Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tlnagy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tlnagy/32/5815_2.png) [@tlnagy](https://discourse.julialang.org/u/tlnagy)\
**Post date:** [December 7, 2017, 2:04am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/1 "2017-12-07T02:04:44Z")

</div>

I expected the following to work:

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

julia> deleteat!(a, end-1:end)
ERROR: syntax: unexpected "end"

julia> a[end-1:end]
2-element Array{Int64,1}:
 4
 5

```

I expected it to delete the last two variables in the array, instead it errors out. Any thoughts on why this behavior happens? Is this a bug?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [December 7, 2017, 2:20am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/2 "2017-12-07T02:20:03Z")

</div>

The parser treats `end` inside square brackets different from `end` outside square brackets (where it means “end of block” as in `for i = 1:10 ... end`).

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [December 7, 2017, 2:23am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/3 "2017-12-07T02:23:32Z")

</div>

Not quite as succinct, but you could use `endof`:

```julia
deleteat!(a, endof(a)-1:endof(a))

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 7, 2017, 2:24am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/4 "2017-12-07T02:24:50Z")

</div>

In particular the parser (or rather the “lowering” phase that happens right after parsing) converts any `end` inside `a[...]` into a call to `endof(a)`.

You can see this explicitly by calling `@code_lowered`:

```julia
julia> f(a) = a[end-1:end]

f (generic function with 1 method)

julia> @code_lowered f([1,2,3,4])

CodeInfo(:(begin 

        nothing

        return (Main.getindex)(a, (Main.colon)((Base.endof)(a) - 1, (Base.endof)(a)))

    end))

```

---

<div class="post-metadata">

**Author:** ![tlnagy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tlnagy/32/5815_2.png) [@tlnagy](https://discourse.julialang.org/u/tlnagy)\
**Post date:** [December 7, 2017, 2:32am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/5 "2017-12-07T02:32:48Z")

</div>

Thanks everyone for the explanations. That makes sense. But is this the best behavior? Could the parser not just recognize ends that are parts of ranges and convert those to `Base.endof`?

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [December 7, 2017, 3:07am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/6 "2017-12-07T03:07:05Z")

</div>

> [@tlnagy](#):
>
> Could the parser not just recognize ends that are parts of ranges and convert those to Base.endof?

I can’t really answer that question, but I think it’s more complicated.

To expand on Steven’s reply, the lowering of `end` is context-sensitive. For example, within a multi-dimensional array index, `end` is lowered to `size(a, dim)` where `dim` is determined by the position of `end` within the index:

```julia
Julia-0.6.0> f(a) = a[1, end-1:end, 3]
f (generic function with 1 method)

Julia-0.6.0> @code_lowered f(Array{Int}(3,4,5))
CodeInfo(:(begin
        nothing
        return (Main.getindex)(a, 1, (Main.colon)((Base.size)(a, 2) - 1, (Base.size)(a, 2)), 3)
    end))

```

---

<div class="post-metadata">

**Author:** ![jobjob](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jobjob/32/160_2.png) [@jobjob](https://discourse.julialang.org/u/jobjob)\
**Post date:** [October 10, 2018, 8:27am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/7 "2018-10-10T08:27:45Z")

</div>

I took this too far 😄: [https://github.com/JobJob/EndyIndexes.jl](https://github.com/JobJob/EndyIndexes.jl)

```julia
pkg> add https://github.com/JobJob/EndyIndexes.jl.git

```

```julia
using EndyIndexes

const start_ = StartBasedIdx()
const end_ = EndBasedIdx()

arr = [1:10;]
for idx in (end_-2:end_, start_+1:end_-2)
    @show arr[idx]
end
a = [1,2,3,4,5]
deleteat!(a, end_-1:end_)

```

Output

```julia
arr[idx] = [8, 9, 10]
arr[idx] = [2, 3, 4, 5, 6, 7, 8]
3-element Array{Int64,1}:
 1
 2
 3

```

Thanks to the fantastic work that went into custom AbstractArray indexing this was surprisingly easy to implement.

* * *

Also just re the comment above, in Julia \>=0.7, in multi-dimensional index expressions `end` now lowers to `lastindex(a, dim)`, in single index expressions just to `lastindex(a)`

```julia
jjulia> f1(a) = a[2, end-1:3, 1]
f1 (generic function with 1 method)

julia> f2(a) = a[end]
f2 (generic function with 1 method)

julia> @code_lowered f1(rand(3,3,3))
CodeInfo(
1 1 ─ %1 = (Base.lastindex)(a, 2) │
  │ %2 = %1 - 1 │
  │ %3 = %2:3 │
  │ %4 = (Base.getindex)(a, 2, %3, 1) │
  └── return %4 │
)

julia> @code_lowered f2(rand(3,3,3))
CodeInfo(
1 1 ─ %1 = (Base.lastindex)(a) │
  │ %2 = (Base.getindex)(a, %1) │
  └── return %2 │
)

```

---

<div class="post-metadata">

**Author:** ![mschauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mschauer/32/13946_2.png) [@mschauer](https://discourse.julialang.org/u/mschauer)\
**Post date:** [October 10, 2018, 8:59am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/8 "2018-10-10T08:59:43Z")

</div>

Maybe this is a good point to make advertisement for the idea for 2.0 to use `$begin` and `$end` as they are nice and free, unsurprising, and the dollar works as metaphor (the parser is supposed to substitute them by something like `endof(a)` and `$` indicates talking to the parser. )

---

<div class="post-metadata">

**Author:** ![lmtzx9h4qqnt](https://avatars.discourse-cdn.com/v4/letter/l/74df32/32.png) [@lmtzx9h4qqnt](https://discourse.julialang.org/u/lmtzx9h4qqnt)\
**Post date:** [March 22, 2023, 8:43pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/9 "2023-03-22T20:43:48Z")

</div>

Seems like EndpointRanges.jl also does this?

> **[EndpointRanges.jl](https://juliapackages.com/p/endpointranges)**
>
> Julia package for doing arithmetic on endpoints in array indexing

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [March 22, 2023, 10:10pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/10 "2023-03-22T22:10:22Z")

</div>

As this thread has been resurrected, it seems that `endof()` was deprecated. We can use `lastindex()` instead.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 23, 2023, 4:06am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/11 "2023-03-23T04:06:47Z")

</div>

I don’t know why we don’t use `first` and `last` for this…

Could also be useful for Unicode string from-end indexing. Example: `my_string[first+2:last-2]` could efficiently form a substring omitting the first two and last two characters of a Unicode string.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [March 23, 2023, 11:54am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/12 "2023-03-23T11:54:25Z")

</div>

You do know about `begin` and `end`?

```julia
julia> str = "αβγ"
"αβγ"

julia> str[begin+2:end-2]
"β"

```

The usual caveats about indexing unicode apply.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 23, 2023, 12:15pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/13 "2023-03-23T12:15:57Z")

</div>

> [@tim.holy](#):
>
> You do know about `begin` and `end`?

Yes, but these only work for calculating indices within `[]`; many times you might wish to calculate indices outside, such as in the OP. Hence, the motivation for [EndyIndexes.jl](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/7), this [suggestion](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/8) for `$begin` and `$end`, and [EndpointRanges.jl](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/9). (Not to mention, the motivation for the `@view` macro, @jishnub’s [OrdinalIndexing.jl](https://github.com/jishnub/OrdinalIndexing.jl), and [this thread](https://discourse.julialang.org/t/ordinal-indexing-as-a-language-feature/91970).) The suggestion to do arithmetic with `first` and `last` is just to use existing singleton objects, instead of introducing new `start_` and `end_` or `ibegin` and `iend` objects.

Additionally, because `first+x` and `last-y` could return special from-start and from-end indexing objects, they could be used for efficiently indexing strings by character (instead of by byte). For example, I’d imagine `"αβγ"[first+1:last-1]` would return `"β"` (with `getindex` making appropriate calls to `nextind` and `prevind` under the hood).

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [March 23, 2023, 12:40pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/14 "2023-03-23T12:40:19Z")

</div>

> [@uniment](#):
>
> The suggestion to do arithmetic with `first` and `last` is just to use existing singleton objects, instead of introducing new `start_` and `end_` or `ibegin` and `iend` objects.

But there’s little advantage to re-using `first` and `last` this way, as I think that just about the only thing it saves is

```julia
struct IBegin end
const ibegin = IBegin() end

```

and everything else is just writing methods that dispatch on whatever object you’ve decided to use.

Note that outside of `[]`, coverage is likely to be hit & miss; we’re very unlikely to support `tan(ibegin+2)` anytime soon. The good news is that relatively few operations seem to go a long way.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 23, 2023, 9:59pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/15 "2023-03-23T21:59:49Z")

</div>

> [@tim.holy](#):
>
> there’s little advantage to re-using `first` and `last` this way

It’s subjective, but I’m not a huge fan of the ergonomics of `ibegin` and `iend`. The words “first” and “last” also already are used several times for this sort of meaning (e.g., `first`, `firstindex`, `findfirst`, etc), so using them here isn’t especially surprising.

That said, indexing from `first` is effectively zero-based indexing, so even if we had such a feature I’d still want either a) a convenient way to construct 1-indexed views e.g. `nth(A)[1:n]`, or b) 1-based indices e.g. `A[(1:n)nth]`.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [March 24, 2023, 5:49am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/16 "2023-03-24T05:49:15Z")

</div>

We considered using `first` and `last` that way (I’m sure there’s a PR or issue discussion one could find if one wanted), but it was decided to be too weird.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 24, 2023, 6:34am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/17 "2023-03-24T06:34:43Z")

</div>

That’s too bad; special parsing rules to pun `begin` and `end` for indexing seems equally weird, but is less expressive 😅 I suppose that ship has sailed though.

---

<div class="post-metadata">

**Author:** ![ParadaCarleton](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paradacarleton/32/20005_2.png) [@ParadaCarleton](https://discourse.julialang.org/u/ParadaCarleton)\
**Post date:** [March 25, 2023, 11:29pm UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/18 "2023-03-25T23:29:55Z")

</div>

> [@tim.holy](#):
>
> Note that outside of `[]`, coverage is likely to be hit & miss; we’re very unlikely to support `tan(ibegin+2)` anytime soon. The good news is that relatively few operations seem to go a long way.

I’m not expecting `tan(begin+2)`, but there’s nothing wrong with the original request to support `deleteat!(a, end)`. I think ordinal indices are the way to go on that, for most cases (e.g. `deleteat!(1st)`). But we might need something for indexing from the end.

(We could try using Python’s `-1st` for ordinal indices? I know that conflicts a bit with R, but I think `!1st == Not(1st)` is the better syntax for that. Alternatively, I do think `last` is a reasonable choice.)

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 26, 2023, 5:02am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/19 "2023-03-26T05:02:32Z")

</div>

And just today, [another thread](https://discourse.julialang.org/t/using-the-end-keyword-as-a-function-argument/96615) asking about this. It’s funny how interest in topics tends to bunch temporally 😅

> [@ParadaCarleton](#):
>
> But we might need something for indexing from the end.

As you saw last time, I had talked myself out of this when I realized it doesn’t save any boundschecks. But then I saw this thread:

> [@Base.Chop API rationale](https://discourse.julialang.org/t/base-chop-api-rationale/96473):
>
> [Base.chop](https://docs.julialang.org/en/v1/base/strings/#Base.chop) chop(s::AbstractString; head::Integer = 0, tail::Integer = 1) Remove the first head and the last tail characters from s. The call chop(s) removes the last character from s. If it is requested to remove more characters than length(s) then an empty string is returned. I am curious about the reasoning for two choices: uses keyword arguments instead of positional defaults to tail=1 Especially the default tail=1 I really am puzzled by: what is this function for?

Unicode makes the string API rather awkward, necessitating strange functions like `chop`; if we could access relative to first and last ordinal indices, methods like this wouldn’t be needed. Dictionaries and I imagine some other data structures could also make use of such indexing; I begin to wonder about sparse arrays.

Maybe it even makes sense to have _both_ `xs[begin:end]` indexing _and_ `xs[first:last]` indexing, where the former is understood to range over positional indices `eachindex(xs)`, whereas the latter ranges over ordinal indices `1:length(xs)`? (similar to the contrast between `xs[i]` vs `xs[n*th]`)

> [@ParadaCarleton](#):
>
> We could try using Python’s `-1st` for ordinal indices?

I entertained that thought momentarily, but I was turned off when I realized the inconsistency in having `1st:-1st` work but `(1:-1)st` not work. I’m not sure why unitrange `a:b` clamps to `a:a-1` for `b` much less than `a`; maybe that can be changed?

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 28, 2023, 8:02am UTC](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591/20 "2023-03-28T08:02:36Z")

</div>

> [@uniment](#):
>
> Maybe it even makes sense to have _both_ `xs[begin:end]` indexing _and_ `xs[first:last]` indexing, where the former is understood to range over positional indices `eachindex(xs)`, whereas the latter ranges over ordinal indices `1:length(xs)`? (similar to the contrast between `xs[i]` vs `xs[n*th]`)

On second thought, I don’t like using `first` and `last` for this: it’d be strange for `"αβγ"[last-2]` to return `'α'`, but `s="αβγ"; s[lastindex(s)-2]` to return `'β'`. Likewise, `findfirst` returns a positional index, not an ordinal index. The way the words get overloaded here is too messy.

I’m warming up to `-1nth` to represent the ordinal index of the last element just to have less stuff to memorize. Even if you have to write `a*nth:-b*nth` instead of `(a:-b)nth`.

Since we don’t need to claim any O(1) access semantics here, `getindex` for ordinal indices could be defined to fall back to leaning on the iteration interface. The intuitive understanding of ordinal indices could be: where `c` is an arbitrary iterable collection, the ordinal index operates such that `c[n*nth] === collect(c)[n]` and `c[-n*nth] === collect(c)[end+1-n]` for positive `n`.

[Next page](https://discourse.julialang.org/t/using-end-variable-in-ranges-passed-to-functions-other-than-getindex/7591.md?page=2)
