Juliaの中心的な目標の1つは、数値データに対する計算を、すばやく効率的に実行できるようにすることです。しかも、_大量の_数値データを扱います。
通常、データは配列(またはそれに類する型)に格納されるので、配列がJulia言語の中心にあると言ってもよいでしょう。
配列は任意のサイズにでき(ハードウェアのメモリの制約を受けるだけです)、任意の数の次元を持てます。
この入門では、ベクトルと呼ばれる1次元配列の基本に焦点を当てます。
Juliaでは、ベクトルはインデックス番号でアクセスできる、順序付けられた値の並びです。
ベクトルを作るもっとも簡単な方法は、角括弧([])の中に値を並べることです。
julia> num_vec = [1, 4, 9]
3-element Vector{Int64}:
1
4
9
julia> str_vec = ["arrays", "are", "important"]
3-element Vector{String}:
"arrays"
"are"
"important"
ベクトルの型は、各要素の型と一致します。
つまり、ベクトルはすべての要素が同じ型でなければならないのでしょうか?
ある意味では、そのとおりです。最高のパフォーマンスを求めるなら、そうすることをおすすめします。ただしJuliaは、必要なときにはAny型を使うことで、この制約を回避できます。
julia> mixed_vector = [1, "str", 'c']
3-element Vector{Any}:
1
"str"
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
空のベクトルを作成するとき、Juliaは要素の型をAnyにします。ただし、空の配列に目的の要素型を注釈として付けると、より具体的な型を指定できます。
julia> empty_vec = []
Any[]
julia> empty_int_vec = Int64[]
Int64[]
次のルールをよく読んで、覚えておいてください。
他のデータサイエンス言語(R、Matlab、Fortranなど)を使ったことがある人にはおなじみですが、Cに似た言語を学んできた人には驚きに映るかもしれません。
それ以外は、インデックスは予想どおりに機能します。角括弧に入れるだけです。
squares = [0, 1, 4, 9, 16]
squares[1] # 0
squares[3] # 4
squares[begin] # 0 ("begin" is synonym for 1)
squares[end] # 16 ("end" is synonym for length(squares))
end(とても便利です)とbegin(おそらくあまり使われません)でインデックスできる手軽さに注目してください。
Pythonプログラマーは、負のインデックスはどうなのかと気になるかもしれません。気にしないでください。負のインデックスはJuliaには存在せず、BoundsErrorになります。1より小さいインデックスや、length(squares)より大きいインデックスも同様です。
Juliaのベクトルは_可変_です。個々の要素の中身を変更できます。
julia> vals = [1, 3, 5, 7]
4-element Vector{Int64}:
1
3
5
7
julia> vals[2] = 4 # reassign the value of this element
4
# Only the value at position 2 changes:
julia> vals
4-element Vector{Int64}:
1
4
5
7
ベクトルを操作するための関数はたくさんあります。ただしJuliaのドキュメントでは、これらはベクトルではなく「コレクション」として一般化されています。
慣例として、入力を変更する関数の名前には!が付きます。コンパイラーがこれを強制することはありませんが、Juliaの世界ではとても_強い_慣例です。
便利な関数をいくつか紹介します。
push!()を使います。pop!()を使います。pushfirst!()とpopfirst!()を使います。insert!()とdeleteat!()があります。julia> vals = [1, 3]
2-element Vector{Int64}:
1
3
julia> push!(vals, 5, 6) # can add multiple values
4-element Vector{Int64}:
1
3
5
6
julia> pop!(vals) # mutates vals, return popped value
6
julia> vals
3-element Vector{Int64}:
1
3
5
マジシャンの卵であるElyseは、基本をいくつか練習する必要があります。 彼女には、自由に操りたい山札があります。
少しでも簡単にするため、彼女は1から10のカードだけを使います。こうすると、山札は数値のベクトルで表せます。 あるカードの位置は、ベクトル内のインデックスに対応します。
カードを1枚選ぶには、与えられた山札から、インデックスpositionにあるカードを返します。
julia> stack = [1, 2, 4, 1];
julia> position = 3;
julia> get_item(stack, position)
4
ちょっとした手品で、インデックスpositionにあるカードを、与えられた交換用のカードと入れ替えます。
入れ替えた後の山札を返します。
julia> stack = [1, 2, 4, 1];
julia> position = 3;
julia> replacement_card = 6;
julia> set_item!(stack, position, replacement_card)
[1, 2, 6, 1]
山札の一番上に新しいカードを挿入して、カードを出現させます。 挿入した後の山札を返します。
julia> stack = [5, 9, 7, 1];
julia> new_card = 8;
julia> insert_item_at_top!(stack, new_card)
[5, 9, 7, 1, 8]
山札から、指定されたpositionにあるカードを取り除いて、カードを消します。
取り除いた後の山札を返します。
julia> stack = [3, 2, 6, 4, 8];
julia> position = 3;
julia> remove_item!(stack, position)
[3, 2, 4, 8]
山札の一番上にあるカードを取り除いて、カードを消します。 取り除いた後の山札を返します。
julia> stack = [3, 2, 6, 4, 8];
julia> remove_item_from_top!(stack)
[3, 2, 6, 4]
山札の一番下に新しいカードを挿入して、カードを出現させます。 挿入した後の山札を返します。
julia> stack = [5, 9, 7, 1];
julia> new_card = 8;
julia> insert_item_at_bottom!(stack, new_card)
[8, 5, 9, 7, 1]
山札の一番下にあるカードを取り除いて、カードを消します。 取り除いた後の山札を返します。
julia> stack = [8, 5, 9, 7, 1];
julia> remove_item_at_bottom!(stack)
[5, 9, 7, 1]
山札の枚数がstack_sizeと同じかどうかを確認します。
julia> stack = [3, 2, 6, 4, 8];
julia> stack_size = 4;
julia> check_size_of_stack(stack, stack_size)
false