I have the following which tries to apply a relative unit range to a string whose length is unknown:
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?
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.
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.
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).
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:
pass in the index ranges you want directly,
or you can write a macro (I advise against, but it is technically the âbestâ solution)
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
function testnums(data, back_i)
data[end-back_i:end]
end
foo = "123456789"
testnums(foo, 3) # "6789"
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
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.
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,
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?
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.
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
# 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.
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.