avatarEmma Boudreau

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5113

Abstract

ass="language-julia"> ca[index] </span>"pageone"</pre></div><p id="9dff">This continues, and the indexes will repeat infinitely.</p><div id="da16"><pre><span class="hljs-meta prompt_">julia></span><span class="language-julia"> index = <span class="hljs-number">45</span> </span>45</pre></div><div id="9f14"><pre><span class="hljs-meta prompt_">julia></span><span class="language-julia"> ca[index] </span>"pagethree"</pre></div><p id="5e48">No matter how many times the index is rolled over, this will continue. The same can also be said for negative indexes. Think of it somewhat like a number line, where 1 is the center.</p><div id="2cd0"><pre><span class="hljs-meta prompt_">julia></span><span class="language-julia"> ca[<span class="hljs-number">0</span>] </span>"pagethree"</pre></div><div id="e755"><pre><span class="hljs-meta prompt_">julia></span><span class="language-julia"> ca[-<span class="hljs-number">1</span>] </span>"pagetwo"</pre></div><p id="be19">In my use case, this makes it extremely easy to create pages that rollover! In many other languages, something like this might be reasonably hard to program. The problem is that often creating a new array inside of a language is quite difficult. Consider Python as an example, with Python, we would need to create a bunch of weird default <code>whatever()</code> functions, and then also need to create a bunch of new bindings to replace many of Python’s built-ins that are used with lists. These names of course could not conflict with Python’s built-in function names, so we would have to create new names — new names that users would need to learn and figure out how to work with. However, in Julia this is not the case. Let’s first take a look at the CarouselArray constructor:</p><div id="0593"><pre><span class="hljs-keyword">mutable struct</span> CarouselArray{T <: <span class="hljs-built_in">Any</span>} dims::<span class="hljs-built_in">Vector</span>{T} <span class="hljs-keyword">function</span> CarouselArray(x::<span class="hljs-built_in">AbstractVector</span>)::CarouselArray new{typeof(x).parameters[<span class="hljs-number">1</span>]}(x)::CarouselArray <span class="hljs-keyword">end</span> CarouselArray{T}() <span class="hljs-keyword">where</span> {T <: <span class="hljs-built_in">Any</span>} = new{T}(<span class="hljs-built_in">Vector</span>{T}([])) CarouselArray{T}(x::<span class="hljs-built_in">AbstractVector</span>) <span class="hljs-keyword">where</span> {T <: <span class="hljs-built_in">Any</span>} = new{T}(<span class="hljs-built_in">Vector</span>{T}([x])) <span class="hljs-keyword">end</span></pre></div><p id="9f16">This is a pretty average constructor for an Array in Julia, at least an Array that is not a Base Julia Array. Rather than really being an Array, a CarouselArray is much more-so a wrapper for an Array. The reason this is the case is because sub-typing an AbstractArray is not something that can be done in Julia. Unfortunate, but there are so few bindings we need to make, most of which we would probably need to make anyway, that it really is not as big of a deal as it seems to be at face value. Allow me to explain.</p><div id="4b21"><pre>iterate(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}) = iterate(<span class="hljs-name">ca</span>.dims) iterate(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}, i:<span class="hljs-symbol">:Int64</span>) = iterate(<span class="hljs-name">ca</span>.dims, i) push!(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}, a:<span class="hljs-symbol">:Any</span> ...) = push!(<span class="hljs-name">ca</span>.dims, a ...) append!(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}, a:<span class="hljs-symbol">:Any</span>) = append!(<span class="hljs-name">ca</span>.dims, a) deleteat!(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}, i:<span class="hljs-symbol">:Int64</span>) = deleteat!(<span class="hljs-name">ca</span>.dims, i) keys(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}) = keys(<span class="hljs-name">ca</span>.dims) length(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}) = length(<span class="hljs-name">ca</span>.dims)</pre></div><p id="c85f">These are all of the bindings I created for my Array. In all honesty, I am not entirely sure which of these bindings is necessary, for example, I am pretty sure that the <code>deleteat!</code> binding is covered so long as we bind <code>keys</code> , <code>length</code> , and <code>iterate</code> . Either way, each binding is a mere one line, and each additional binding comes from Base. All together, to impl

Options

ement this Array into Base as a new Array type, we only need five or six lines of code. This Array now works with essentially every Base method for Arrays, including broadcasting and finding functions such as <code>findall</code> .</p><p id="97a7">Of course, none of this is special to the CarouselArray, but indexing is a different story. The index method uses some very light recursion, mod, and absolute distance to create carousel indexing. This is done with conditionals. First, we check if the index is below one. In this case, we will repeat the getindex on the length of the array subtracted by the absolute distance of the index. If we were to not subtract this, we would be indexing the Array by ascending values. We of course want the values to roll back over to the end of the Array, so we subtract from the end of the Array however far we are from zero.</p><div id="f307"><pre>function getindex(<span class="hljs-name">ca</span>:<span class="hljs-symbol">:CarouselArray</span>{<<span class="hljs-symbol">:Any</span>}, i:<span class="hljs-symbol">:Int64</span>) if i < <span class="hljs-number">1</span> getindex(<span class="hljs-name">ca</span>, length(<span class="hljs-name">ca</span>) - abs(<span class="hljs-name">i</span>))</pre></div><p id="777a">Next, we check if the index is above the length of the Array. If this is the case, we divide x by the length of the array and get the remainder. This will tell us how far our index is away from a division of the length of the Array. Considering that this getindex function is also called by the absolute distance portion above, this can become a recursive call with a possible depth of two at most. In my subjective view, this is one of the better applications of recursion, as in this instance the technique is used relatively lightly and incredibly effectively. If the absolute distance of our index below 0 is more than the length of the array away from 1, then this portion of the function will be called after the absolute distance is retrieved.</p><div id="3acb"><pre>elseif i > <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>) getindex(<span class="hljs-keyword">ca</span>, <span class="hljs-built_in">mod</span>(i, <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>)))</pre></div><p id="15b7">Finally, if the index is not greater or lower, we simply index our dims <code>Vector</code> :</p><div id="094b"><pre><span class="hljs-keyword">else</span> ca.dims[i] <span class="hljs-keyword">end</span></pre></div><p id="8e9c">For a final function that looks like this:</p><div id="0aa7"><pre>function getindex(<span class="hljs-keyword">ca</span>::CarouselArray{<:Any}, i::Int64) <span class="hljs-keyword">if</span> i < 1 getindex(<span class="hljs-keyword">ca</span>, <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>) - <span class="hljs-built_in">abs</span>(i)) elseif i > <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>) getindex(<span class="hljs-keyword">ca</span>, <span class="hljs-built_in">mod</span>(i, <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>))) <span class="hljs-keyword">else</span> <span class="hljs-keyword">ca</span>.dims[i] end end</pre></div><p id="0ad4">This is then repeated for <code>setindex!</code>:</p><div id="f376"><pre>function setindex!(<span class="hljs-keyword">ca</span>::CarouselArray{<:Any}, v::Any, i::Int64) <span class="hljs-keyword">if</span> i < 1 setindex!(<span class="hljs-keyword">ca</span>, v, <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>) - <span class="hljs-built_in">abs</span>(i)) elseif i > <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>) setindex!(<span class="hljs-keyword">ca</span>, v, <span class="hljs-built_in">mod</span>(i, <span class="hljs-built_in">length</span>(<span class="hljs-keyword">ca</span>))) <span class="hljs-keyword">else</span> <span class="hljs-keyword">ca</span>.dims[i] = v end end</pre></div><p id="7d6c">The code shown in this article is actually the entire module. It is simply incredible how easy it is to implement something like this in Julia. This is of course possible thanks to Julia’s incredible extensibility and as always,</p><p id="dd7f" type="7">multiple dispatch.</p><p id="f34f">Needless to say, I am incredibly excited to apply this to my project! This project all together took less than an hour, and really goes to show just how quickly things can be developed inside of the Julia language. I could not imagine the anguish of implementing something like this in many other languages, and I am thankful for the paradigm of Julia making this an absolute cake walk. This is certainly some awesome data functionality to get in less than an hour! Thank you for reading, and I hope that this was a cool demonstration of what is possible in Julia with very minimal code!</p></article></body>

CarouselArrays.jl — Repeating Arrays In Julia

The surprisingly simple, yet effective new package for repeating arrays.

Recently, while developing a website — the website being toolips app — I came across a very interesting use-case for what I am now calling Carousel Arrays. What I have is an infinite list of panels which need to be indexed repeatedly, meaning I want the indexes to roll over. For example, consider the following set of elements:

["pageone", "pagetwo", "pagethree"]

Each time that a page is changed, the index will simply be added or subtracted by one depending on the direction. The problem with that, however, is that whenever we hit the first index, page one, subtracting one will of course cause an error. In Julia, indexes start at one, so the 0th index will return an index error, and since this is a three element array, indexing the 4th index will also return an index error. What I wanted was an array that can be indexed with 0, -1, and so-forth without throwing such an error. The result of this specific need in one circumstance was an entirely new module, because I am a complete and utter maniac. The module that was born as a result is called CarouselArrays.jl.

Carousel Arrays provide this exact ‘ roll-over’ indexing method through some very simple Base dispatches. From a declarative standpoint, the module is incredibly simple to use, and Carousel Arrays are essentially identical to regular Julia Arrays with the only difference being how they are indexed.

using CarouselArrays

Once we import CarouselArrays.jl, we can construct a CarouselArray as we would any other Julian Array.

julia> ca = CarouselArray{String}()
CarouselArray{String}(String[])
julia> ca = CarouselArray(["pageone", "pagetwo", "pagethree"])
CarouselArray{String}(["pageone", "pagetwo", "pagethree"])

The difference comes whenever we index our Carousel Array. Let’s consider my use-case for Carousel Arrays, and make a pretend index.

julia> index = 1
1
julia> ca[index]
"pageone"

We will start on the default page, which is of course stored at the first index. Let’s pretend someone wants to go to the page on the right of this page, we increment the index by one.

julia> index += 1
2
julia> ca[index]
"pagetwo"

They continue to the third page.

julia> index += 1
3
julia> ca[index]
"pagethree"

This is where the difference begins to show. In a normal Array, the next index will return an index error. However, with a carousel, the next index will start us back at the first element of the Array.

julia> index += 1
4
julia> ca[index]
"pageone"

This continues, and the indexes will repeat infinitely.

julia> index = 45
45
julia> ca[index]
"pagethree"

No matter how many times the index is rolled over, this will continue. The same can also be said for negative indexes. Think of it somewhat like a number line, where 1 is the center.

julia> ca[0]
"pagethree"
julia> ca[-1]
"pagetwo"

In my use case, this makes it extremely easy to create pages that rollover! In many other languages, something like this might be reasonably hard to program. The problem is that often creating a new array inside of a language is quite difficult. Consider Python as an example, with Python, we would need to create a bunch of weird default __whatever__() functions, and then also need to create a bunch of new bindings to replace many of Python’s built-ins that are used with lists. These names of course could not conflict with Python’s built-in function names, so we would have to create new names — new names that users would need to learn and figure out how to work with. However, in Julia this is not the case. Let’s first take a look at the CarouselArray constructor:

mutable struct CarouselArray{T <: Any}
    dims::Vector{T}
    function CarouselArray(x::AbstractVector)::CarouselArray
        new{typeof(x).parameters[1]}(x)::CarouselArray
    end
    CarouselArray{T}() where {T <: Any} = new{T}(Vector{T}([]))
    CarouselArray{T}(x::AbstractVector) where {T <: Any} = new{T}(Vector{T}([x]))
end

This is a pretty average constructor for an Array in Julia, at least an Array that is not a Base Julia Array. Rather than really being an Array, a CarouselArray is much more-so a wrapper for an Array. The reason this is the case is because sub-typing an AbstractArray is not something that can be done in Julia. Unfortunate, but there are so few bindings we need to make, most of which we would probably need to make anyway, that it really is not as big of a deal as it seems to be at face value. Allow me to explain.

iterate(ca::CarouselArray{<:Any}) = iterate(ca.dims)
iterate(ca::CarouselArray{<:Any}, i::Int64) = iterate(ca.dims, i)
push!(ca::CarouselArray{<:Any}, a::Any ...) = push!(ca.dims, a ...)
append!(ca::CarouselArray{<:Any}, a::Any) = append!(ca.dims, a)
deleteat!(ca::CarouselArray{<:Any}, i::Int64) = deleteat!(ca.dims, i)
keys(ca::CarouselArray{<:Any}) = keys(ca.dims)
length(ca::CarouselArray{<:Any}) = length(ca.dims)

These are all of the bindings I created for my Array. In all honesty, I am not entirely sure which of these bindings is necessary, for example, I am pretty sure that the deleteat! binding is covered so long as we bind keys , length , and iterate . Either way, each binding is a mere one line, and each additional binding comes from Base. All together, to implement this Array into Base as a new Array type, we only need five or six lines of code. This Array now works with essentially every Base method for Arrays, including broadcasting and finding functions such as findall .

Of course, none of this is special to the CarouselArray, but indexing is a different story. The index method uses some very light recursion, mod, and absolute distance to create carousel indexing. This is done with conditionals. First, we check if the index is below one. In this case, we will repeat the getindex on the length of the array subtracted by the absolute distance of the index. If we were to not subtract this, we would be indexing the Array by ascending values. We of course want the values to roll back over to the end of the Array, so we subtract from the end of the Array however far we are from zero.

function getindex(ca::CarouselArray{<:Any}, i::Int64)
    if i < 1
        getindex(ca, length(ca) - abs(i))

Next, we check if the index is above the length of the Array. If this is the case, we divide x by the length of the array and get the remainder. This will tell us how far our index is away from a division of the length of the Array. Considering that this getindex function is also called by the absolute distance portion above, this can become a recursive call with a possible depth of two at most. In my subjective view, this is one of the better applications of recursion, as in this instance the technique is used relatively lightly and incredibly effectively. If the absolute distance of our index below 0 is more than the length of the array away from 1, then this portion of the function will be called after the absolute distance is retrieved.

elseif i > length(ca)
        getindex(ca, mod(i, length(ca)))

Finally, if the index is not greater or lower, we simply index our dims Vector :

else
        ca.dims[i]
    end

For a final function that looks like this:

function getindex(ca::CarouselArray{<:Any}, i::Int64)
    if i < 1
        getindex(ca, length(ca) - abs(i))
    elseif i > length(ca)
        getindex(ca, mod(i, length(ca)))
    else
        ca.dims[i]
    end
end

This is then repeated for setindex!:

function setindex!(ca::CarouselArray{<:Any}, v::Any, i::Int64)
    if i < 1
        setindex!(ca, v, length(ca) - abs(i))
    elseif i > length(ca)
        setindex!(ca, v, mod(i, length(ca)))
    else
        ca.dims[i] = v
    end
end

The code shown in this article is actually the entire module. It is simply incredible how easy it is to implement something like this in Julia. This is of course possible thanks to Julia’s incredible extensibility and as always,

multiple dispatch.

Needless to say, I am incredibly excited to apply this to my project! This project all together took less than an hour, and really goes to show just how quickly things can be developed inside of the Julia language. I could not imagine the anguish of implementing something like this in many other languages, and I am thankful for the paradigm of Julia making this an absolute cake walk. This is certainly some awesome data functionality to get in less than an hour! Thank you for reading, and I hope that this was a cool demonstration of what is possible in Julia with very minimal code!

Julia
Programming
Software Development
Web Development
Data
Recommended from ReadMedium