Example of defining a type: Arrays with arbitrary indexing

For scientific applications, it is often useful to have arrays whose indices may start anywhere. Let's try to define a 1D array with this property, as an example of implementing a new type.

I want

a = IndexedArray([3,4,5], -7)  # starting from -7

a[-7]

Exercise: What structure should it have?



In [1]:


In [1]:
type IndexedArray
    data::Vector{Float64}
    low_index::Integer
end

Exercise: Write a function that calculates the correct index.


We calculate the correct index once and for all:


In [2]:
reindex(a::IndexedArray, i) = i - a.low_index + 1


Out[2]:
reindex (generic function with 1 method)

In [3]:
getindex(a::IndexedArray, i) = a.data[reindex(a, i)]


Out[3]:
getindex (generic function with 182 methods)

In [ ]:


In [4]:
v = IndexedArray([3, 4, 5], -3)


Out[4]:
IndexedArray([3.0,4.0,5.0],-3)

In [5]:
v[-3]


Out[5]:
3.0

In [12]:
v[-2]


Out[12]:
4.0

In [6]:
v[-1]


Out[6]:
5.0

In [7]:
v[0]


BoundsError()
while loading In[7], in expression starting on line 1
 in getindex at array.jl:246
 in getindex at In[3]:1

In [8]:
v[-1] = 10


no method setindex!(IndexedArray, Int64, Int64)
while loading In[8], in expression starting on line 1

Many operations already work:


In [13]:
v[-3:-2]


Out[13]:
2-element Array{Float64,1}:
 3.0
 4.0

But others don't yet:


In [14]:
v[-3] = 10


no method setindex!(IndexedArray, Int64, Int64)
while loading In[14], in expression starting on line 1

In [9]:
setindex!(a::IndexedArray, x, i) = a.data[reindex(a, i)] = x


Out[9]:
setindex! (generic function with 115 methods)

In [10]:
v[-3] = 10


Out[10]:
10

In [12]:
v


Out[12]:
IndexedArray([10.0,4.0,5.0],-3)

In [20]:
v[-3] = 10


Out[20]:
10

In [21]:
v


Out[21]:
IndexedArray([10.0,4.0,5.0],-3)

In [13]:
for i in v
    println(i)
end


no method start(IndexedArray)
while loading In[13], in expression starting on line 1
 in anonymous at no file

Iteration

To define iteration for our new type, we need to define the start, next and done functions. To see how to do so, the easiest thing is to copy some pre-existing code!


In [13]:
methods(start)


Out[13]:
43 methods for generic function start:

Let's pick the one for an abstract array. This is what we need to implement:


In [18]:
# start(a::AbstractArray) = 1
# next(a::AbstractArray,i) = (a[i],i+1)
# done(a::AbstractArray,i) = (i > length(a))

First, we must, as the error message says, explicitly import the functions from Base in order to extend them:


In [21]:
import Base: start, next, done


Warning: import of Base.start into Main conflicts with an existing identifier; ignored.
Warning: import of Base.next into Main conflicts with an existing identifier; ignored.
Warning: import of Base.done into Main conflicts with an existing identifier; ignored.

In [22]:
start(a::IndexedArray) = a.low_index
next(a::IndexedArray,i) = (a[i],i+1)
done(a::IndexedArray,i) = (i > a.low_index + length(a) - 1)


Out[22]:
done (generic function with 1 method)

In [23]:
v


Out[23]:
IndexedArray([10.0,4.0,5.0],-3)

In [24]:
for i in v
    println(i)
end


no method start(IndexedArray)
while loading In[24], in expression starting on line 1
 in anonymous at no file

In [25]:
length(a::IndexedArray) = length(a.data)


Out[25]:
length (generic function with 1 method)

In [24]:
import Base.length

In [25]:
length(a::IndexedArray) = length(a.data)


Out[25]:
length (generic function with 58 methods)

In [32]:
for i in v
    println(i)
end


10.0
4.0
5.0

In [26]:
v


Out[26]:
IndexedArray([10.0,4.0,5.0],-3)

In [27]:
v + v


no method +(IndexedArray, IndexedArray)
while loading In[27], in expression starting on line 1

In [ ]: