# How to pass a relative UnitRange to a function?

**URL:** https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431
**Category:** New to Julia
**Tags:** question, metaprogramming, function
**Created:** [September 28, 2020, 7:00pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431 "2020-09-28T19:00:34Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![mkarikom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkarikom/32/7096_2.png) [@mkarikom](https://discourse.julialang.org/u/mkarikom)
#### Post date: [September 28, 2020, 7:00pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/1 "2020-09-28T19:00:34Z")

</div>

I have the following which tries to apply a relative unit range to a string whose length is unknown:

```julia
function testnums(data::String,numrange::String)
    data[numrange]
end

foo = "asdfasdfasdf"
bar = "end-3:end"
testnums(foo,bar)

```

> **Here is the error**
>
> ``` MethodError: no method matching getindex(::String, ::String) Closest candidates are: getindex(::String, !Matched::UnitRange{Int64}) at strings/string.jl:245 getindex(::String, !Matched::Int64) at strings/string.jl:210 getindex(::String, !Matched::UnitRange{#s69} where #s69\<:Integer) at strings/string.jl:242 ... testnums(::String, ::String) at passargs.jl:2 top-level scope at passargs.jl:7 ```

As suggested by the error, you can’t pass a string for the range. So how can I pass a relative range eg `end-3:end`?

---

<div class="post-metadata">

### Author: ![Adriel](https://avatars.discourse-cdn.com/v4/letter/a/f07891/32.png) [@Adriel](https://discourse.julialang.org/u/Adriel)
#### Post date: [September 28, 2020, 7:49pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/2 "2020-09-28T19:49:58Z")

</div>

I’d just build the range inside the function.

```julia
function testnums(data::String, n)
    data[end-n:end]
end

```

Does that work for you?

---

<div class="post-metadata">

### Author: ![HenriDeh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrideh/32/8316_2.png) [@HenriDeh](https://discourse.julialang.org/u/HenriDeh)
#### Post date: [September 28, 2020, 8:07pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/3 "2020-09-28T20:07:57Z")

</div>

Well,

bar is not a range it’s a string. You can change it to a range like this

```julia
function testnums(data::String,numrange::String)
    data[eval(Meta.parse(numrange))]
end

foo = "asdfasdfasdf"
bar = "end-3:end"
testnums(foo,bar)

```

---

<div class="post-metadata">

### Author: ![jebej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jebej/32/1784_2.png) [@jebej](https://discourse.julialang.org/u/jebej)
#### Post date: [September 28, 2020, 8:22pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/4 "2020-09-28T20:22:09Z")

</div>

What you should do, if you truly want to pass a relative range, is to pass a normal range, _and_ the index it’s relative to, and add those up inside the function. For example, for `end`, it would be `rel=length(data)`, and then you do `rge=-3:0`, and then in the function you take `data[rel.+rge]`. Otherwise, you need to pass the absolute range.

---

<div class="post-metadata">

### Author: ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)
#### Post date: [September 28, 2020, 11:32pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/5 "2020-09-28T23:32:15Z")

</div>

> [@HenriDeh](#):
>
> ```julia
> function testnums(data::String,numrange::String)
> data[eval(Meta.parse(numrange))]
> end
> 
> ```

Please don’t do this. This is a pathological example of pretty much every “gotcha” in the julia language put together. It relies both on using strings as expressions (using `parse`) and on global variables and using `eval`. There are so many subtleties to just how scary this is that it would even be difficult adequately explaining it 😆. Luckily, it throws a `ParseError`.

> [@mkarikom](#):
>
> So how can I pass a relative range eg `end-3:end` ?

The short answer to this is that since `end` is a keyword (not a function or a symbol), you can’t pass it around. In particular notice what happens when the expression `a[1:end]` is “lowered” (translated by julia into what’s actually going to get compiled).

```julia
julia> Meta.@lower a[1:end]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Base.lastindex(a)
│ %2 = 1:%1
│ %3 = Base.getindex(a, %2)
└── return %3
))))

```

In particular, note that %1 is evaluating `lastindex(a)`, %2 constructs the range `1:%1` (i.e. `1:lastindex(a)`), and then that’s what’s used as the index in %3. The keyword `end` is just syntactic sugar for `lastindex(self)`, so it doesn’t play a direct role in any of this and julia gets rid of it long before compile time. To do this you can:

1. pass in the index ranges you want directly,
2. or you can write a macro (I advise against, but it is technically the “best” solution)
3. or you create an api that doesn’t require it. If your example is the behavior that’s important to you, you could instead do

```julia
function testnums(data, back_i)
    data[end-back_i:end]
end

foo = "123456789"
testnums(foo, 3) # "6789"

```

---

<div class="post-metadata">

### Author: ![HenriDeh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrideh/32/8316_2.png) [@HenriDeh](https://discourse.julialang.org/u/HenriDeh)
#### Post date: [September 29, 2020, 11:42am UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/6 "2020-09-29T11:42:42Z")

</div>

> [@tomerarnon](#):
>
> Please don’t do this. This is a pathological example of pretty much every “gotcha” in the julia language put together. It relies both on using strings as expressions (using `parse` ) and on global variables and using `eval` . There are so many subtleties to just how scary this is that it would even be difficult adequately explaining it 😆. Luckily, it throws a `ParseError` .

Ha yes, I just (tried) to answer his question. But it is true that this is not best practice. I just assumed that he has a good reason to pass strings to the function.

What would work though is

```julia
function testnums(data::String,numrange::String)
           exp = reduce(*, ["data[",numrange,"]"])
           eval(Meta.parse(exp))
           end

foo = "asdfasdfasdf"
bar = "end-3:end"
testnums(foo,bar)

```

But it is certainly not the way to go. If you don’t have a specific reason to use a String argument for the range then dont.

---

<div class="post-metadata">

### Author: ![rashid](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rashid/32/9398_2.png) [@rashid](https://discourse.julialang.org/u/rashid)
#### Post date: [September 29, 2020, 1:12pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/7 "2020-09-29T13:12:03Z")

</div>

I have faced the same issue of not being able to pass relative ranges to functions. Coming from the python world where we could write the same function as,

```python
def testnums(data, numrange):
    return data[numrange]

foo = "asdfasdfasdf"
bar = slice(-3,None)
testnums(foo,bar) # Output: 'sdf'

```

I understand why the code provided by the OP doesn’t work. But shouldn’t there be a counterpart to python’s slice in Julia which allows easily passing relative ranges to functions. Would something like `RelativeRange(end-3,end)` be hard to implement in Julia? Or is there something fundamentally flawed about passing relative ranges as arguments to functions?

---

<div class="post-metadata">

### Author: ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)
#### Post date: [September 29, 2020, 1:50pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/8 "2020-09-29T13:50:31Z")

</div>

One could do something like this (not sure how useful it is)

```julia
struct Slice
    a::Union{Int, Nothing}
    b::Union{Int, Nothing}
end

Slice(; a = nothing, b = nothing) = Slice(a, b)

function Base.getindex(v::AbstractVector, s::Slice)
    return if isnothing(s.a) && isnothing(s.b)
        copy(v)
    elseif isnothing(s.b)
        v[end + s.a : end]
    elseif isnothing(s.a)
        v[begin:begin + s.b]
    else
        v[s.a:s.b]
    end
end

```

and it works in the following way

```julia
julia> v = collect(1:10)

julia> v[Slice(a = -2)]
3-element Array{Int64,1}:
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)
#### Post date: [September 29, 2020, 2:04pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/9 "2020-09-29T14:04:56Z")

</div>

> [@rashid](#):
>
> Would something like `RelativeRange(end-3,end)` be hard to implement in Julia? Or is there something fundamentally flawed about passing relative ranges as arguments to functions?

It’s not possible to pass the keyword `end` around. I was going to say this can be done with a macro, but turns out it _really_ can’t 😥. You cannot represent a standalone `end` as an expression without jumping through hoops.

```julia
julia> :(a[end-3:end]) ### legal
:(a[end - 3:end])

julia> :(end-3:end) ### illegal
ERROR: syntax: unexpected "end"
Stacktrace:
 [1] top-level scope at none:1

```

This means you cannot do what was my first though, which is writing a macro to convert

```julia
@slicelike end-3:end

```

to a new type like @Skoffer’s `Slice`

```julia
SliceLike(x -> lastindex(x)-3, lastindex)  
# getindex with this converts to 
# x[lastindex(x)-3:lastindex(x)], 
# which is the same as with end.

```

or even simply to a “slicing” function like

```julia
# called like slicer(x)
slicer = x -> x[end-3:end] 

```

But anyway it turns out you truly can’t do this with a macro, because `end` is even more of a reserved keyword than I thought! I’m not sure if this is necessarily the case for some intrinsic reason, or whether `:(end-3)` could technically be an allowed expression, but oh well.  
In any case, a custom type like `Slice` is still very much on the table, as is using a `slicer` function without a macro.

---

<div class="post-metadata">

### Author: ![mkarikom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkarikom/32/7096_2.png) [@mkarikom](https://discourse.julialang.org/u/mkarikom)
#### Post date: [September 29, 2020, 10:10pm UTC](https://discourse.julialang.org/t/how-to-pass-a-relative-unitrange-to-a-function/47431/10 "2020-09-29T22:10:46Z")

</div>

> [@tomerarnon](#):
>
> ```julia
> function testnums(data, back_i)
> data[end-back_i:end]
> end
> 
> foo = "123456789"
> testnums(foo, 3) # "6789"
> 
> ```

Thanks for the thorough answer.  
Lol, this is almost exactly how I did it while waiting on a more ‘elegant’ solution from Discourse.  
I was hoping that a metaprogramming technique was possible so that I could write a function that ‘understands’ relative and explicit ranges, looks like the quick and dirty solution is the only one in this case.
