多次元配列
大抵の技術計算用の言語と同様に、Juliaでは、第一級の配列の実装が利用可能です。 ほとんどの技術計算用の言語は、他のコンテナをないがしろにして配列の実装に注力していますが、 Juliaでは配列を特別扱いしません。 配列のライブラリは、ほぼJuliaだけで完全に実装されており、他のJuliaで書かれたコードと同様に、コンパイラがパフォーマンスをだせるようにしています。 また、AbstractArray
を継承して、独自の配列の型を定義することも可能です。 配列に対する独自の型の実装の詳細については、[マニュアルのAbstractArrayインタフェースに関する部分](@ ref man-interface-array)を参照してください。
配列は、多次元グリッドに格納されたオブジェクトのコレクションです。 大抵の一般的な場合には、配列にはAny
型のオブジェクトが含まれていてもかまいません。 しかし殆どの計算用途には、配列は、Float64
やInt32
のように型を特定すべきです。
他の多くの技術計算用の言語とは異なり、Juliaでは通常、パフォーマンスをあげたいからといって、プログラムをベクトル化したスタイルで書く必要はありません。 Juliaのコンパイラは型推論を使用し、配列のスカラインデックスによる参照に最適化されたコードを生成するので、便利で読みやすいスタイルで書いても、パフォーマンスを犠牲にすることなく、少ないメモリで使用することができます。
Juliaでは、すべての関数の引数は参照渡しです。技術計算用の言語のなかには配列を値渡しするものもあり、便利な場合も多いです。 Juliaでは、関数が入力用の配列に加えた変更を、親関数から参照できます。 Juliaの配列ライブラリはすべて、ライブラリ関数が入力を変更しないことを、保証しています。 ユーザーのコードも同様の動作をさせる必要がある場合は、変更してもよい入力のコピーを作成するよう注意してください。
基本的な関数
関数 | 説明 |
---|---|
eltype(A) | A に含まれる要素の型。 |
length(A) | A の要素の数 |
ndims(A) | A の次元の数 |
size(A) | A の次元を保持するタプル |
size(A,n) | 次元n に沿った A のサイズ |
indices(A) | A の妥当なインデックスを表すタプル |
indices(A,n) | 次元n の A の妥当なインデックスを表す範囲 |
eachindex(A) | A の各位置を走査する効率的なイテレータ |
stride(A,k) | 次元k に沿ったストライド(隣接する要素間の線形インデックスの距離) |
strides(A) | 各次元のストライドのタプル |
生成と初期化
配列の生成と初期化用に多くの関数が用意されています。 下記の関数のリストにある、引数にdims...
を使う関数呼び出しは、次元のサイズを表す1個のタプル、または可変引数で渡される次元サイズの列かのいずれかを取ることができます。 これらの関数のほとんどは、配列の要素型であるT
を入力の最初に受けとります。 型T
が省略された場合、デフォルト のFloat64
になります。
関数 | 説明 |
---|---|
Array{T}(dims...) | 初期化されていない密な Array |
zeros(T, dims...) | すべてが0の 配列 |
zeros(A) | 要素の型やシェイプがA である、すべてが同じ型の0である配列 |
ones(T, dims...) | すべてが1の 配列 |
ones(A) | 要素の型やシェイプがA である、すべてが同じ型の1である配列 |
trues(dims...) | すべての値がtrue であるBitArray |
trues(A) | すべての値がtrue でシェイプがA と同じBitArray |
falses(dims...) | すべての値がfalse であるBitArray |
falses(A) | すべての値がfalse でシェイプがA と同じBitArray |
reshape(A, dims...) | A と同じデータだが次元の違う配列 |
copy(A) | A をコピーする |
deepcopy(A) | A をコピーする(再帰的にその要素もコピーする) |
similar(A, T, dims...) | A (密・疎など)と型が同じだが初期化されていない配列で、要素の型や次元が指定されたもの。2番目と3番目の引数は共に省略可能で、省略した時のデフォルト値はA の要素の型と次元になる。 |
reinterpret(T, A) | A とバイナリデータが同じだが、要素の型がT である配列 |
rand(T, dims...) | 半開区間$[0, 1)$で独立同分布、一様分布の乱数の配列 |
randn(T, dims...) | 独立同分布、標準正規分布の乱数の配列 |
eye(T, n) | n ×n の単位行列 |
eye(T, m, n) | m ×n の単位行列 |
linspace(start, stop, n) | start からstop までn 個の要素が等間隔にある範囲 |
fill!(A, x) | 配列A を値x で埋める |
fill(x, dims...) | 値がx で埋まった配列 |
iid, 独立同分布
構文[A, B, C, ...]
では、引数からなる1次元配列(ベクトル)を生成します。 すべての引数が共通の昇格した型を持つ場合、引数はconvert()
を使用してその型に変換されます。
julia> zeros(Int8, 2, 2)
2×2 Array{Int8,2}:
0 0
0 0
julia> zeros(Int8, (2, 2))
2×2 Array{Int8,2}:
0 0
0 0
julia> zeros((2, 2))
2×2 Array{Float64,2}:
0.0 0.0
0.0 0.0
Here, (2, 2)
is a Tuple
.
連結
配列は、以下の関数を使用して生成・連結することもできます。
関数 | 説明 |
---|---|
cat(k, A...) | 入力した n次元の配列を次元kに沿って連結する |
vcat(A...) | cat(1, A...) の簡略表記 |
hcat(A...) | cat(2, A...) の簡略表記 |
これらの関数に渡されるスカラー値は、1要素の配列として扱われます。
julia> vcat([1, 2], 3)
3-element Array{Int64,1}:
1
2
3
julia> hcat([1 2], 3)
1×3 Array{Int64,2}:
1 2 3
連結関数はよく使用されるので、特殊構文も存在します。
式 | 関数呼び出し |
---|---|
[A; B; C; ...] | vcat() |
[A B C ...] | hcat() |
[A B; C D; ...] | hvcat() |
hvcat()
は次元1(セミコロンを使う)と次元2(空白を使う)の両方向に連結します。
julia> [[1; 2]; [3, 4]]
4-element Array{Int64,1}:
1
2
3
4
julia> [[1 2] [3 4]]
1×4 Array{Int64,2}:
1 2 3 4
julia> [[1 2]; [3 4]]
2×2 Array{Int64,2}:
1 2
3 4
型つき配列の初期化
T[A, B, C, ...]
という構文を使うと、要素の型を指定して配列を生成できます。 この時、要素の型がT
の1次元配列が生成され、要素がA
, B
, C
などと初期化されます。 例えばAny[x, y, z]
とすると、任意の値を含むことができる型の不均質な配列が生成されます。
連結構文にも同様に型の接頭辞を付けて、演算結果に対して要素の型を指定することができます。
julia> [[1 2] [3 4]]
1×4 Array{Int64,2}:
1 2 3 4
julia> Int8[[1 2] [3 4]]
1×4 Array{Int8,2}:
1 2 3 4
内包表記
Comprehensions provide a general and powerful way to construct arrays. Comprehension syntax is similar to set construction notation in mathematics:
A = [ F(x,y,...) for x=rx, y=ry, ... ]
The meaning of this form is that F(x,y,...)
is evaluated with the variables x
, y
, etc. taking on each value in their given list of values. Values can be specified as any iterable object, but will commonly be ranges like 1:n
or 2:(n-1)
, or explicit arrays of values like [1.2, 3.4, 5.7]
. The result is an N-d dense array with dimensions that are the concatenation of the dimensions of the variable ranges rx
, ry
, etc. and each F(x,y,...)
evaluation returns a scalar.
The following example computes a weighted average of the current element and its left and right neighbor along a 1-d grid. :
julia> x = rand(8)
8-element Array{Float64,1}:
0.843025
0.869052
0.365105
0.699456
0.977653
0.994953
0.41084
0.809411
julia> [ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
6-element Array{Float64,1}:
0.736559
0.57468
0.685417
0.912429
0.8446
0.656511
The resulting array type depends on the types of the computed elements. In order to control the type explicitly, a type can be prepended to the comprehension. For example, we could have requested the result in single precision by writing:
Float32[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
ジェネレーター式
Comprehensions can also be written without the enclosing square brackets, producing an object known as a generator. This object can be iterated to produce values on demand, instead of allocating an array and storing them in advance (see Iteration). For example, the following expression sums a series without allocating memory:
julia> sum(1/n^2 for n=1:1000)
1.6439345666815615
When writing a generator expression with multiple dimensions inside an argument list, parentheses are needed to separate the generator from subsequent arguments:
julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;])
ERROR: syntax: invalid iteration specification
All comma-separated expressions after for
are interpreted as ranges. Adding parentheses lets us add a third argument to map
:
julia> map(tuple, (1/(i+j) for i=1:2, j=1:2), [1 3; 2 4])
2×2 Array{Tuple{Float64,Int64},2}:
(0.5, 1) (0.333333, 3)
(0.333333, 2) (0.25, 4)
Generators are implemented via inner functions. As in other cases of inner functions in the language, variables from the enclosing scope can be "captured" in the inner function. For example, sum(p[i] - q[i] for i=1:n)
captures the three variables p
, q
and n
from the enclosing scope. Captured variables can present performance challenges described in performance tips.
Ranges in generators and comprehensions can depend on previous ranges by writing multiple for
keywords:
julia> [(i,j) for i=1:3 for j=1:i]
6-element Array{Tuple{Int64,Int64},1}:
(1, 1)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
(3, 3)
In such cases, the result is always 1-d.
Generated values can be filtered using the if
keyword:
julia> [(i,j) for i=1:3 for j=1:i if i+j == 4]
2-element Array{Tuple{Int64,Int64},1}:
(2, 2)
(3, 1)
インデックスづけ
The general syntax for indexing into an n-dimensional array A is:
X = A[I_1, I_2, ..., I_n]
where each I_k
may be a scalar integer, an array of integers, or any other supported index. This includes Colon
(:
) to select all indices within the entire dimension, ranges of the form a:c
or a:b:c
to select contiguous or strided subsections, and arrays of booleans to select elements at their true
indices.
If all the indices are scalars, then the result X
is a single element from the array A
. Otherwise, X
is an array with the same number of dimensions as the sum of the dimensionalities of all the indices.
If all indices are vectors, for example, then the shape of X
would be (length(I_1), length(I_2), ..., length(I_n))
, with location (i_1, i_2, ..., i_n)
of X
containing the value A[I_1[i_1], I_2[i_2], ..., I_n[i_n]]
.
Example:
julia> A = reshape(collect(1:16), (2, 2, 2, 2))
2×2×2×2 Array{Int64,4}:
[:, :, 1, 1] =
1 3
2 4
[:, :, 2, 1] =
5 7
6 8
[:, :, 1, 2] =
9 11
10 12
[:, :, 2, 2] =
13 15
14 16
julia> A[1, 2, 1, 1] # all scalar indices
3
julia> A[[1, 2], [1], [1, 2], [1]] # all vector indices
2×1×2×1 Array{Int64,4}:
[:, :, 1, 1] =
1
2
[:, :, 2, 1] =
5
6
julia> A[[1, 2], [1], [1, 2], 1] # a mix of index types
2×1×2 Array{Int64,3}:
[:, :, 1] =
1
2
[:, :, 2] =
5
6
Note how the size of the resulting array is different in the last two cases.
If I_1
is changed to a two-dimensional matrix, then X
becomes an n+1
-dimensional array of shape (size(I_1, 1), size(I_1, 2), length(I_2), ..., length(I_n))
. The matrix adds a dimension.
Example:
julia> A = reshape(collect(1:16), (2, 2, 2, 2));
julia> A[[1 2; 1 2]]
2×2 Array{Int64,2}:
1 2
1 2
julia> A[[1 2; 1 2], 1, 2, 1]
2×2 Array{Int64,2}:
5 6
5 6
The location (i_1, i_2, i_3, ..., i_{n+1})
contains the value at A[I_1[i_1, i_2], I_2[i_3], ..., I_n[i_{n+1}]]
. All dimensions indexed with scalars are dropped. For example, the result of A[2, I, 3]
is an array with size size(I)
. Its i
th element is populated by A[2, I[i], 3]
.
As a special part of this syntax, the end
keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the innermost array being indexed. Indexing syntax without the end
keyword is equivalent to a call to getindex
:
X = getindex(A, I_1, I_2, ..., I_n)
Example:
julia> x = reshape(1:16, 4, 4)
4×4 reshape(::UnitRange{Int64}, 4, 4) with eltype Int64:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
julia> x[2:3, 2:end-1]
2×2 Array{Int64,2}:
6 10
7 11
julia> x[1, [2 3; 4 1]]
2×2 Array{Int64,2}:
5 9
13 1
Empty ranges of the form n:n-1
are sometimes used to indicate the inter-index location between n-1
and n
. For example, the searchsorted
function uses this convention to indicate the insertion point of a value not found in a sorted array:
julia> a = [1,2,5,6,7];
julia> searchsorted(a, 3)
3:2
代入
The general syntax for assigning values in an n-dimensional array A is:
A[I_1, I_2, ..., I_n] = X
where each I_k
may be a scalar integer, an array of integers, or any other supported index. This includes Colon
(:
) to select all indices within the entire dimension, ranges of the form a:c
or a:b:c
to select contiguous or strided subsections, and arrays of booleans to select elements at their true
indices.
If X
is an array, it must have the same number of elements as the product of the lengths of the indices: prod(length(I_1), length(I_2), ..., length(I_n))
. The value in location I_1[i_1], I_2[i_2], ..., I_n[i_n]
of A
is overwritten with the value X[i_1, i_2, ..., i_n]
. If X
is not an array, its value is written to all referenced locations of A
.
Just as in Indexing, the end
keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the array being assigned into. Indexed assignment syntax without the end
keyword is equivalent to a call to setindex!
:
setindex!(A, X, I_1, I_2, ..., I_n)
Example:
julia> x = collect(reshape(1:9, 3, 3))
3×3 Array{Int64,2}:
1 4 7
2 5 8
3 6 9
julia> x[3, 3] = -9;
julia> x[1:2, 1:2] = [-1 -4; -2 -5];
julia> x
3×3 Array{Int64,2}:
-1 -4 7
-2 -5 8
3 6 -9
対応しているインデックスの型
In the expression A[I_1, I_2, ..., I_n]
, each I_k
may be a scalar index, an array of scalar indices, or an object that represents an array of scalar indices and can be converted to such by to_indices
:
- A scalar index. By default this includes:
- Non-boolean integers
CartesianIndex{N}
s, which behave like anN
-tuple of integers spanning multiple dimensions (see below for more details)
- An array of scalar indices. This includes:
- Vectors and multidimensional arrays of integers
- Empty arrays like
[]
, which select no elements - Ranges like
a:c
ora:b:c
, which select contiguous or strided subsections froma
toc
(inclusive) - Any custom array of scalar indices that is a subtype of
AbstractArray
- Arrays of
CartesianIndex{N}
(see below for more details)
- An object that represents an array of scalar indices and can be converted to such by
to_indices
. By default this includes:Colon()
(:
), which represents all indices within an entire dimension or across the entire array- Arrays of booleans, which select elements at their
true
indices (see below for more details)
Some examples:
julia> A = reshape(collect(1:2:18), (3, 3))
3×3 Array{Int64,2}:
1 7 13
3 9 15
5 11 17
julia> A[4]
7
julia> A[[2, 5, 8]]
3-element Array{Int64,1}:
3
9
15
julia> A[[1 4; 3 8]]
2×2 Array{Int64,2}:
1 7
5 15
julia> A[[]]
0-element Array{Int64,1}
julia> A[1:2:5]
3-element Array{Int64,1}:
1
5
9
julia> A[2, :]
3-element Array{Int64,1}:
3
9
15
julia> A[:, 3]
3-element Array{Int64,1}:
13
15
17
直積のインデックス
The special CartesianIndex{N}
object represents a scalar index that behaves like an N
-tuple of integers spanning multiple dimensions. For example:
julia> A = reshape(1:32, 4, 4, 2);
julia> A[3, 2, 1]
7
julia> A[CartesianIndex(3, 2, 1)] == A[3, 2, 1] == 7
true
Considered alone, this may seem relatively trivial; CartesianIndex
simply gathers multiple integers together into one object that represents a single multidimensional index. When combined with other indexing forms and iterators that yield CartesianIndex
es, however, this can lead directly to very elegant and efficient code. See Iteration below, and for some more advanced examples, see this blog post on multidimensional algorithms and iteration.
Arrays of CartesianIndex{N}
are also supported. They represent a collection of scalar indices that each span N
dimensions, enabling a form of indexing that is sometimes referred to as pointwise indexing. For example, it enables accessing the diagonal elements from the first "page" of A
from above:
julia> page = A[:,:,1]
4×4 Array{Int64,2}:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
julia> page[[CartesianIndex(1,1),
CartesianIndex(2,2),
CartesianIndex(3,3),
CartesianIndex(4,4)]]
4-element Array{Int64,1}:
1
6
11
16
This can be expressed much more simply with dot broadcasting and by combining it with a normal integer index (instead of extracting the first page
from A
as a separate step). It can even be combined with a :
to extract both diagonals from the two pages at the same time:
julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), 1]
4-element Array{Int64,1}:
1
6
11
16
julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), :]
4×2 Array{Int64,2}:
1 17
6 22
11 27
16 32
CartesianIndex
and arrays of CartesianIndex
are not compatible with the end
keyword to represent the last index of a dimension. Do not use end
in indexing expressions that may contain either CartesianIndex
or arrays thereof.
論理インデックスづけ
Often referred to as logical indexing or indexing with a logical mask, indexing by a boolean array selects elements at the indices where its values are true
. Indexing by a boolean vector B
is effectively the same as indexing by the vector of integers that is returned by findall(B)
. Similarly, indexing by a N
-dimensional boolean array is effectively the same as indexing by the vector of CartesianIndex{N}
s where its values are true
. A logical index must be a vector of the same length as the dimension it indexes into, or it must be the only index provided and match the size and dimensionality of the array it indexes into. It is generally more efficient to use boolean arrays as indices directly instead of first calling findall
.
julia> x = reshape(1:16, 4, 4)
4×4 reshape(::UnitRange{Int64}, 4, 4) with eltype Int64:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
julia> x[[false, true, true, false], :]
2×4 Array{Int64,2}:
2 6 10 14
3 7 11 15
julia> mask = map(ispow2, x)
4×4 Array{Bool,2}:
true false false false
true false false false
false false false false
true true false true
julia> x[mask]
5-element Array{Int64,1}:
1
2
4
8
16
イテレーション
The recommended ways to iterate over a whole array are
for a in A
# Do something with the element a
end
for i in eachindex(A)
# Do something with i and/or A[i]
end
The first construct is used when you need the value, but not index, of each element. In the second construct, i
will be an Int
if A
is an array type with fast linear indexing; otherwise, it will be a CartesianIndex
:
julia> A = rand(4,3);
julia> B = view(A, 1:3, 2:3);
julia> for i in eachindex(B)
@show i
end
i = CartesianIndex(1, 1)
i = CartesianIndex(2, 1)
i = CartesianIndex(3, 1)
i = CartesianIndex(1, 2)
i = CartesianIndex(2, 2)
i = CartesianIndex(3, 2)
In contrast with for i = 1:length(A)
, iterating with eachindex
provides an efficient way to iterate over any array type.
配列のトレイト
If you write a custom AbstractArray
type, you can specify that it has fast linear indexing using
Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
This setting will cause eachindex
iteration over a MyArray
to use integers. If you don't specify this trait, the default value IndexCartesian()
is used.
配列とベクトル化した演算子・関数
The following operators are supported for arrays:
- Unary arithmetic –
-
,+
- Binary arithmetic –
-
,+
,*
,/
,\
,^
- Comparison –
==
,!=
,≈
(isapprox
),≉
Most of the binary arithmetic operators listed above also operate elementwise when one argument is scalar: -
, +
, and *
when either argument is scalar, and /
and \
when the denominator is scalar. For example, [1, 2] + 3 == [4, 5]
and [6, 4] / 2 == [3, 2]
.
Additionally, to enable convenient vectorization of mathematical and other operations, Julia provides the dot syntax f.(args...)
, e.g. sin.(x)
or min.(x,y)
, for elementwise operations over arrays or mixtures of arrays and scalars (a Broadcasting operation); these have the additional advantage of "fusing" into a single loop when combined with other dot calls, e.g. sin.(cos.(x))
.
Also, every binary operator supports a dot version that can be applied to arrays (and combinations of arrays and scalars) in such fused broadcasting operations, e.g. z .== sin.(x .* y)
.
Note that comparisons such as ==
operate on whole arrays, giving a single boolean answer. Use dot operators like .==
for elementwise comparisons. (For comparison operations like <
, only the elementwise .<
version is applicable to arrays.)
Also notice the difference between max.(a,b)
, which broadcast
s max
elementwise over a
and b
, and maximum(a)
, which finds the largest value within a
. The same relationship holds for min.(a,b)
and minimum(a)
.
ブロードキャスト
It is sometimes useful to perform element-by-element binary operations on arrays of different sizes, such as adding a vector to each column of a matrix. An inefficient way to do this would be to replicate the vector to the size of the matrix:
julia> a = rand(2,1); A = rand(2,3);
julia> repeat(a,1,3)+A
2×3 Array{Float64,2}:
1.20813 1.82068 1.25387
1.56851 1.86401 1.67846
This is wasteful when dimensions get large, so Julia offers broadcast
, which expands singleton dimensions in array arguments to match the corresponding dimension in the other array without using extra memory, and applies the given function elementwise:
julia> broadcast(+, a, A)
2×3 Array{Float64,2}:
1.20813 1.82068 1.25387
1.56851 1.86401 1.67846
julia> b = rand(1,2)
1×2 Array{Float64,2}:
0.867535 0.00457906
julia> broadcast(+, a, b)
2×2 Array{Float64,2}:
1.71056 0.847604
1.73659 0.873631
Dotted operators such as .+
and .*
are equivalent to broadcast
calls (except that they fuse, as described below). There is also a broadcast!
function to specify an explicit destination (which can also be accessed in a fusing fashion by .=
assignment). Moreover, f.(args...)
is equivalent to broadcast(f, args...)
, providing a convenient syntax to broadcast any function (dot syntax). Nested "dot calls" f.(...)
(including calls to .+
etcetera) automatically fuse into a single broadcast
call.
Additionally, broadcast
is not limited to arrays (see the function documentation), it also handles tuples and treats any argument that is not an array, tuple or Ref
(except for Ptr
) as a "scalar".
julia> convert.(Float32, [1, 2])
2-element Array{Float32,1}:
1.0
2.0
julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7])
2×2 Array{UInt8,2}:
0x02 0x04
0x06 0x07
julia> string.(1:3, ". ", ["First", "Second", "Third"])
3-element Array{String,1}:
"1. First"
"2. Second"
"3. Third"
実装
The base array type in Julia is the abstract type AbstractArray{T,N}
. It is parametrized by the number of dimensions N
and the element type T
. AbstractVector
and AbstractMatrix
are aliases for the 1-d and 2-d cases. Operations on AbstractArray
objects are defined using higher level operators and functions, in a way that is independent of the underlying storage. These operations generally work correctly as a fallback for any specific array implementation.
The AbstractArray
type includes anything vaguely array-like, and implementations of it might be quite different from conventional arrays. For example, elements might be computed on request rather than stored. However, any concrete AbstractArray{T,N}
type should generally implement at least size(A)
(returning an Int
tuple), getindex(A,i)
and getindex(A,i1,...,iN)
; mutable arrays should also implement setindex!
. It is recommended that these operations have nearly constant time complexity, or technically Õ(1) complexity, as otherwise some array functions may be unexpectedly slow. Concrete types should also typically provide a similar(A,T=eltype(A),dims=size(A))
method, which is used to allocate a similar array for copy
and other out-of-place operations. No matter how an AbstractArray{T,N}
is represented internally, T
is the type of object returned by integer indexing (A[1, ..., 1]
, when A
is not empty) and N
should be the length of the tuple returned by size
. For more details on defining custom AbstractArray
implementations, see the array interface guide in the interfaces chapter.
DenseArray
is an abstract subtype of AbstractArray
intended to include all arrays where elements are stored contiguously in column-major order (see additional notes in Performance Tips). The Array
type is a specific instance of DenseArray
Vector
and Matrix
are aliases for the 1-d and 2-d cases. Very few operations are implemented specifically for Array
beyond those that are required for all AbstractArrays
s; much of the array library is implemented in a generic manner that allows all custom arrays to behave similarly.
SubArray
is a specialization of AbstractArray
that performs indexing by sharing memory with the original array rather than by copying it. A SubArray
is created with the view
function, which is called the same way as getindex
(with an array and a series of index arguments). The result of view
looks the same as the result of getindex
, except the data is left in place. view
stores the input index vectors in a SubArray
object, which can later be used to index the original array indirectly. By putting the @views
macro in front of an expression or block of code, any array[...]
slice in that expression will be converted to create a SubArray
view instead.
BitArray
s are space-efficient "packed" boolean arrays, which store one bit per boolean value. They can be used similarly to Array{Bool}
arrays (which store one byte per boolean value), and can be converted to/from the latter via Array(bitarray)
and BitArray(array)
, respectively.
A "strided" array is stored in memory with elements laid out in regular offsets such that an instance with a supported isbits
element type can be passed to external C and Fortran functions that expect this memory layout. Strided arrays must define a strides(A)
method that returns a tuple of "strides" for each dimension; a provided stride(A,k)
method accesses the k
th element within this tuple. Increasing the index of dimension k
by 1
should increase the index i
of getindex(A,i)
by stride(A,k)
. If a pointer conversion method Base.unsafe_convert(Ptr{T}, A)
is provided, the memory layout must correspond in the same way to these strides. DenseArray
is a very specific example of a strided array where the elements are arranged contiguously, thus it provides its subtypes with the approporiate definition of strides
. More concrete examples can be found within the interface guide for strided arrays. StridedVector
and StridedMatrix
are convenient aliases for many of the builtin array types that are considered strided arrays, allowing them to dispatch to select specialized implementations that call highly tuned and optimized BLAS and LAPACK functions using just the pointer and strides.
The following example computes the QR decomposition of a small section of a larger array, without creating any temporaries, and by calling the appropriate LAPACK function with the right leading dimension size and stride parameters.
julia> a = rand(10, 10)
10×10 Array{Float64,2}:
0.517515 0.0348206 0.749042 0.0979679 … 0.75984 0.950481 0.579513
0.901092 0.873479 0.134533 0.0697848 0.0586695 0.193254 0.726898
0.976808 0.0901881 0.208332 0.920358 0.288535 0.705941 0.337137
0.657127 0.0317896 0.772837 0.534457 0.0966037 0.700694 0.675999
0.471777 0.144969 0.0718405 0.0827916 0.527233 0.173132 0.694304
0.160872 0.455168 0.489254 0.827851 … 0.62226 0.0995456 0.946522
0.291857 0.769492 0.68043 0.629461 0.727558 0.910796 0.834837
0.775774 0.700731 0.700177 0.0126213 0.00822304 0.327502 0.955181
0.9715 0.64354 0.848441 0.241474 0.591611 0.792573 0.194357
0.646596 0.575456 0.0995212 0.038517 0.709233 0.477657 0.0507231
julia> b = view(a, 2:2:8,2:2:4)
4×2 view(::Array{Float64,2}, 2:2:8, 2:2:4) with eltype Float64:
0.873479 0.0697848
0.0317896 0.534457
0.455168 0.827851
0.700731 0.0126213
julia> (q, r) = qr(b);
julia> q
4×4 LinearAlgebra.QRCompactWYQ{Float64,Array{Float64,2}}:
-0.722358 0.227524 -0.247784 -0.604181
-0.0262896 -0.575919 -0.804227 0.144377
-0.376419 -0.75072 0.540177 -0.0541979
-0.579497 0.230151 -0.00552346 0.781782
julia> r
2×2 Array{Float64,2}:
-1.20921 -0.383393
0.0 -0.910506