一時的なデータの格納について：独自型Temporalfieldsの作成

# 何がしたいのか
任意の型を「一時的なデータ」として取りまわすことを目的としています。特に、さまざまなところでデータを使い回すときに、「すでに確保している」「確保していない」がわかるようにしたいです。確保しているデータを使おうとするとエラーが出るようにしたいです。

## この記事が役に立つこと
- 具体的な独自型の定義の例
- 不必要なメモリアロケーションをしないデータの取り回し

## 背景
格子量子色力学のシミュレーションを行うためのオープンソースソフトウェア群[JuliaQCD](https://github.com/JuliaQCD)を開発しています。この中の一つに、4次元の格子上に定義されたゲージ場を扱うパッケージ[Gaugefields.jl](https://github.com/akio-tomiya/Gaugefields.jl)があります。一つのゲージ場は4次元の格子上に定義された複素3x3ユニタリー行列で表現されており、非常に大きな配列です。ですので、新しい配列を毎回定義しているとすごく時間がかかります。ですので、あらかじめ「一時的なデータ」を最初の方で確保しておき、それを使い回すことで計算を高速化できます。

現在のGaugefields.jlでは、一時データはゲージ場を要素とする```Vector```として定義されています。必要に応じて複数の一時ゲージ場をここから取り出して使います。しかし、この一時ゲージ場に入っていて後で使うデータと、上書きしてしまってもいいデータがどれかがわかりにくく、コードを書く際にデバッグが困難になっていました。これを解決することを目的としています。

# コード
コードは一つのmoduleとしました。
```julia:Temporalfields.jl
module Temporalfields_module

mutable struct Temporalfields{TG}
    _data::Vector{TG}
    _flagusing::Vector{Bool}
    _indices::Vector{Int64}
    Nmax::Int64

    function Temporalfields(a::TG; num=1, Nmax=1000) where {TG}
        _data = Vector{TG}(undef, num)
        _flagusing = zeros(Bool, num)
        _indices = zeros(Int64, num)
        for i = 1:num
            _data[i] = similar(a)
        end

        return new{TG}(_data, _flagusing, _indices, Nmax)
    end
end

Base.eltype(::Type{Temporalfields{TG}}) where {TG} = TG

Base.length(t::Temporalfields{TG}) where {TG} = length(t._data)

Base.size(t::Temporalfields{TG}) where {TG} = size(t._data)

function Base.firstindex(t::Temporalfields{TG}) where {TG}
    return 1
end

function Base.lastindex(t::Temporalfields{TG}) where {TG}
    return length(t._data)
end

function Base.getindex(t::Temporalfields{TG}, i::Int) where {TG}
    if i > length(t._data)
        @warn "The length of the temporalfields is shorter than the index $i. New temporal fields are created."
        ndiff = i - length(t._data)
        @assert i <= t.Nmax "The number of the tempralfields $i is larger than the maximum number $(Nmax). Change Nmax."
        for n = 1:ndiff
            push!(t._data, similar(t._data[1]))
            push!(t._flagusing, 0)
            push!(t._indices, 0)
        end
    end
    if t._indices[i] == 0
        index = findfirst(x -> x == 0, t._flagusing)
        t._flagusing[index] = true
        t._indices[i] = index
    else
        error("This index $i is being using.  You should pay attention")
    end

    return t._data[t._indices[i]]
end

function Base.getindex(t::Temporalfields{TG}, I::Vararg{Int,N}) where {TG,N}
    data = TG[]
    for i in I
        push!(data, t[i])
    end
    return data
end

function Base.getindex(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
    data = TG[]
    for i in I
        push!(data, t[i])
    end
    return data
end

function Base.display(t::Temporalfields{TG}) where {TG}
    n = length(t._data)
    println("The total number of fields: $n")
    numused = sum(t._flagusing)
    println("The total number of fields used: $numused")
    for i = 1:n
        if t._indices[i] != 0
            #println("The address $i is used as the index $(t._indices[i])")
            println("The address $(t._indices[i]) is used as the index $i")
        end
    end
    println("The flags: $(t._flagusing)")
    println("The indices: $(t._indices)")
end
function unused!(t::Temporalfields{TG}, i) where {TG}
    if t._indices[i] != 0
        index = t._indices[i]
        t._flagusing[index] = false
        t._indices[i] = 0
    end
end


function unused!(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
    for i in I
        unused!(t, i)
    end
end

function unused!(t::Temporalfields{TG}) where {TG}
    for i = 1:length(t)
        unused!(t, i)
    end
end


export Temporalfields, unused!

end
```

## 使用例
この```Temporalfields```型の使用例は以下のとおりです。

```julia
include("Temporalfields.jl")
using .Temporalfields_module
function main()
    a = rand(10)
    tempvec = Temporalfields(a)
    display(tempvec)
    b = tempvec[1]
    println(b)
    display(tempvec)
    c = tempvec[2]
    println(c)
    display(tempvec)

    tempvec2 = Temporalfields(a; num=4)
    d1 = tempvec2[1]
    println(d1)
    display(tempvec2)
    println("---------------------")
    d2 = tempvec2[2]
    println(d2)
    display(tempvec2)
    unused!(tempvec2, 2)
    println("---------------------")
    d3 = tempvec2[3]
    println(d3)
    display(tempvec2)
    println("---------------------")
    d6 = tempvec2[6]
    println(d6)
    display(tempvec2)
    println("---------------------")
    vecd = tempvec2[3:10]
end
main()
```
これを実行すると、

```
The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 3
The flags: Bool[1, 1, 0, 0]
The indices: [1, 0, 2, 0]
---------------------
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 6
The total number of fields used: 3
The address 1 is used as the index 1
The address 2 is used as the index 3
The address 3 is used as the index 6
The flags: Bool[1, 1, 1, 0, 0, 0]
The indices: [1, 0, 2, 0, 0, 3]
---------------------
ERROR: LoadError: This index 3 is being using.  You should pay attention
```
となります。通常の配列のように
```julia
tempvec2 = Temporalfields(a; num=4)
d1 = tempvec2[1]
```
とすると、一時データの1番を使います。ただし、1番がすでに使われているときは、エラーを出して終了します。使われていないときは、一時データの中で使っていない要素を取り出してきて、それを1番とします。また、
```julia
unused!(tempvec2,1)
```
とすると、一時データの1番を解放します。つまり、自分で一時データを確保して、使い終わった後に別の用途で使用したいときには```unused!```で解放してあげることになります。この```unused!```を使う方法によって、不用意に意図しない上書きを防ぎます。これは、定義するときにだけ使えるので
```julia
d1 = tempvec2[1]
d2 = tempvec2[2]
d3 = tempvec2[3]
```
や
```julia
d = tempvec2[1:4]
```
のように定義してあげた後は、通常のデータとして使用することができます。

# 改良
毎回インデックスを指定するのは、少し面倒です。そこで、
```julia
function get_temp(t::Temporalfields{TG}) where {TG}
    n = length(t._data)
    i = findfirst(x -> x == 0, t._indices)
    if i == nothing
        @warn "All temporal fields are used. New one is created."
        i = n + 1
    end
    return t[i], i
end

function get_temp(t::Temporalfields{TG}, num) where {TG}
    n = length(t._data)
    i_s = Int64[]
    t_s = TG[]
    for k = 1:num
        tk, i = get_temp(t)
        push!(t_s, tk)
        push!(i_s, i)
    end
    return t_s, i_s
end
export Temporalfields, unused!, get_temp
```
という関数を実装しました。これにより、
```julia
function main2()
    a = rand(10)
    tempvec = Temporalfields(a)
    display(tempvec)
    b, it_b = get_temp(tempvec)
    println(b)
    display(tempvec)
    c, it_c = get_temp(tempvec)
    println(c)
    display(tempvec)

    tempvec2 = Temporalfields(a; num=4)
    d1, it_d1 = get_temp(tempvec2)
    println(d1)
    display(tempvec2)
    println("---------------------")
    d2, it_d2 = get_temp(tempvec2)
    println(d2)
    display(tempvec2)
    unused!(tempvec2, it_d2)
    println("---------------------")
    #d3 = tempvec2[3]
    d3, it_d3 = get_temp(tempvec2)
    println(d3)
    display(tempvec2)
    println("---------------------")
    vecd, its_vecd = get_temp(tempvec2, 7)
    display(tempvec2)

    unused!(tempvec2, its_vecd[3])
    unused!(tempvec2, its_vecd[7])
    display(tempvec2)
    unused!(tempvec2, its_vecd)
    display(tempvec2)

end
main2()
```
の実行結果は、

```
The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.2338109335e-314, 2.2338130204e-314, 2.233810965e-314, 2.233813036e-314, 2.2338109967e-314, 2.2411677607e-314, 2.2338110283e-314, 2.23381106e-314, 2.2338110916e-314, 2.2411677686e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.349793168e-314, 2.34979765e-314, 2.349793168e-314, 2.34979765e-314, 2.34979765e-314, 2.34979765e-314, 2.3497931996e-314, 2.3497931996e-314, 2.2338106647e-314, 2.34979765e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 5. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 7. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 8. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 9. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
The total number of fields: 9
The total number of fields used: 9
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 5 is used as the index 5
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The address 9 is used as the index 9
The flags: Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]
The indices: [1, 2, 3, 4, 5, 6, 7, 8, 9]
The total number of fields: 9
The total number of fields used: 7
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The flags: Bool[1, 1, 1, 1, 0, 1, 1, 1, 0]
The indices: [1, 2, 3, 4, 0, 6, 7, 8, 0]
The total number of fields: 9
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0, 0, 0, 0, 0, 0]
The indices: [1, 2, 0, 0, 0, 0, 0, 0, 0]
```
となります。