+ for Unit Range. Is my REPL broken?

Is this really not there:

methods(+,(UnitRange{Int},Int))
# 0 methods for generic function "+" from Base

or is my local REPL broken?
This is what I had to add:

Base.:(+)(r::AbstractRange{T}, x::Real) where {T<:Real} = range(first(r)+x, step=step(r), length=length(r))

Also, why this does not work: Vect[[1,3,5,10:15]]? I had to create custom types to overload this behavior. Is there a performance reason to it? Why do I need to use vectors to retrive indexes instead of Tuples?

This was never defined I think for the same reason +(AbstractVector,Int) isn’t but this works

julia> (1:4) .+1
2:5

What do you mean by Vect

Nope, not broken; the lack of that method is highly intentional! But adding your own methods like that to + is indeed a very easy way to break your REPL :slight_smile:

If you want to add a value to every value in an array, use broadcasting with .+.

Unlike some other languages, Julia draws a sharp distinction between an integer and an array of an integer. You can’t use them in the same places, nor can they seamlessly convert back-and-forth without manual intervention.

This is an intentional limitation that has a very significant payoff: in my experience the explicitness required — with either a broadcast . or something similar — ends up making code easier to read and less prone to unintentional errors.

6 Likes

Ok, even thought for me it is still hard to grasp all of the intricacies.

I will try to lay out my problem better:

I have a “group” of indexes, probably a vector or a tuple, and they can be either Int, unitrange/steprange, or vector/tuple of int, e.g.:

1, 3, 5, 10:12 ....

I would like to extract values from a vector, something like

vect[[1,3,5,10:12]]

What is your suggested course of action?

Then what I would like to do, is to add an offset to that “group” of indexes.
How should I approach the problem in Julia?

I know that (1:4) .+ 1 works, but nor [1,3,5:9] .+1 and (1,3,5:9).+1 work

It is never a good idea to put things with different types into the same vector. It usually leads to performance issues.

In your case, I propose to make all them ranges, i.e. replace say 5 with 5:5. Then the type is homogeneous. You can’t do a “double broadcast” easily but a list comprehension doesn’t seem too bad:

indices = [1:3, 5:5, 7:9]
shifted = [I .+ 2 for I in indices]

something like this ?

julia> mapreduce(collect,vcat,[1,2,3:4,(4,5,6)])
7-element Vector{Int64}:
 1
 2
 3
 4
 4
 5
 6
1 Like

In this particular situation, I suggest vect[[1;3;5;10:12]]. There are more efficient possibilities and also probably some packages for doing this sort of thing, depending on your needs, but I would start with this.

You can offset these with vect[[1;3;5;10:12] .+ offset].

1 Like

I agree on the fact that in this case tuple is better than vector. But since vect[1,3,5] and vect[(1,3,5)] does not work, but only vect[[1,3,5]], I wanted to stay consistent with that.

The solution you propose is interesting, but cumbersome. I wanted to provide an easy interactable framework, and that will complicate it even further

I didn’t know that with ; would work, but not with ,.
The difference that comes to mind is that one is a column-vector and the other is a row vector, is there something else under it? (just trying to understand)

also the correct one is vect[([1;3;5;10:12].+ offeset)] but don’t ask me why

[x;y;z] is equivalent to vcat(x,y,z) which is vertical concatenation. It creates a result with as many rows as there exist total in all the inputs. This works in this case because it takes all of your collections-of-Ints and makes them into one larger collection-of-Ints, which is a valid index type.

[x,y,z] is a different operation, which creates a 3-element array where one entry is each of the inputs. Julia doesn’t have definitions for how to index in this way (for good reason, as it quickly becomes very ambiguous) which is why it didn’t work for indexing nor offsetting.

If we compare these different techniques, we see

julia> [1;3;5;10:12]
6-element Vector{Int64}:
  1
  3
  5
 10
 11
 12

julia> [1,3,5,10:12]
4-element Vector{Any}:
 1
 3
 5
  10:12
1 Like

Ohh, basically it works because it collects them. I wanted to keep them as unitrange for memory implications.

I think I’ll stick with what I got for the vector (basically I created a new type of vector which interact nicely with this type of things), and for the offset, if overloading the + for UnitRange is dangerous as @mbauman was saying, I will just implement an offset! function.

Thanks everyone for the help!

For what it’s worth, the following works:

julia> (.+).([1,3,5:9], 1)
3-element Vector{Any}:
 2
 4
  6:10
1 Like