单维和多维数组
Julia与大多数技术计算语言一样,提供了数组的出色实现。 通常,技术计算语言更重视数组而不是其他容器。 数组在Julia中并不特别。 用于处理数组的库几乎完全在Julia中实现,因此它的性能取决于编译器,以及用Julia编写的其他代码的性能。 出于这个原因,您还可以通过从继承自定义数组类型来定义它们 'AbstractArray'。 有关实现自定义数组类型的详细信息,请参阅 手册中有关AbstractArray接口的部分。
数组是存储在多维表中的对象的集合。 允许零维数组;请参阅 此常见问题部分。 在最一般的情况下,数组可以包含类型的对象 '任何`。 对于大多数计算目的,数组应该包含更具体类型的对象,例如 'Float64'或 'Int32'。
与许多其他用于技术计算的语言不同,在Julia中,程序通常不必以矢量化风格编写以确保性能。 Julia编译器使用类型推断并为数组的标量索引生成优化代码,这使您能够以更方便和可读的风格编写程序,而不会牺牲性能,有时还可以节省内存。
在Julia中,所有函数参数都是https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing [通过共同使用传递](即通过指针)。 在某些语言中,数组按值传递以进行技术计算。 这可以防止被调用的对象意外地更改调用方中的值,但它使避免不必要的数组复制变得困难。 根据惯例,如果函数的名称以字符’结束!,这意味着它将更改或删除一个或多个参数的值(例如,比较函数 `排序'和 '排序!
). 被调用的函数必须显式创建输入数据的副本,以便它们不会更改。 许多不需要更改参数的函数是通过调用具有相同名称的函数来实现的,但是使用符号`!'在结束时,它返回创建的输入数据的显式副本。
主要功能
功能 | 资料描述 |
---|---|
`A`中的元素类型 |
|
`A`中的元素数 |
|
数组`A`的维数 |
|
包含数组`A`维度的元组 |
|
维度`n’中数组’A’的大小 |
|
包含数组`A`的有效索引的元组 |
|
维度`n`的可接受索引范围 |
|
一个高效的迭代器,用于迭代`A`中的每个位置 |
|
数组(相邻元素的线性索引之间的距离)在维度’k`中的步骤 |
|
每个维度的数组步骤元组 |
创建和初始化
有许多函数可用于创建和初始化数组。 在下面的这些函数列表中,有参数为"dims"的调用。..'可以采用具有维度大小的单个元组,也可以采用作为可变数量参数传递的一组维度大小。 大多数这些函数也采用第一个参数’T',它定义了数组元素的类型。 如果未指定`T’参数,则默认类型为 'Float64'。
功能 | 资料描述 |
---|---|
未初始化的密集阵列 '数组' |
|
只有零的数组 |
|
由一个单元组成的"数组"数组 |
|
阵列 'BitArray'所有值都设置为’true` |
|
所有值都设置为"false"的"BitArray"数组 |
|
具有与`A’中相同数据,但具有不同维度的数组 |
|
复制数组’A` |
|
通过递归复制数组`A`的元素来复制数组’a' |
|
与`A’相同类型的未初始化数组(密集,稀疏等)),但具有指定的元素类型和尺寸。 第二个和第三个参数是可选的;默认情况下,数组的元素类型和维度是’A'。 |
|
与`A’中具有相同二进制数据的数组,但元素类型为`T` |
|
一个具有独立、相同的随机数的数组(iid脚注:1[iid--具有独立和相同的分布。])和均匀分布的值。 对于浮点类型’T`,值在半开放范围内。 . |
|
具有随机独立、相同和标准正态分布值的"数组"数组 |
|
`M`乘’n’的单位矩阵。 为 `I'需要’使用线性代数'。 |
|
从"开始"到"停止"的一系列`n`线性间隔的元素 |
|
用值"x"填充数组"A` |
|
用值`x’填充的数组。 特别是,`fill(x)`函数创建一个值为`x`的零维`数组'。 |
下面是一些示例,说明了将测量传递给这些函数的不同方式。
julia> zeros(Int8, 2, 3)
2×3 Matrix{Int8}:
0 0 0
0 0 0
julia> zeros(Int8, (2, 3))
2×3 Matrix{Int8}:
0 0 0
0 0 0
julia> zeros((2, 3))
2×3 Matrix{Float64}:
0.0 0.0 0.0
0.0 0.0 0.0
这里`(2,3)'是 'Tuple',第一个参数—元素类型—是可选的(默认情况下,Float64
)。
数组文字
数组也可以直接使用方括号创建。 语法'[A,B,C,...]'创建一个一维数组(即向量),其中的元素是用逗号分隔的参数。 元素类型(eltype
)是根据括号内的参数类型自动确定的。 如果所有参数都是相同的类型,这将是’eltype’类型。 如果他们都有一个共同的 提升类型,它们使用函数转换为此类型 'convert',而这个类型就是数组的元素类型(eltype
)。 否则,将创建一个可以包含任何内容的异构数组’Vector{Any}. 它也可以是没有参数的字面
[]。 可以键入数组字面量使用语法'+T[A,B,C,…]+
,其中’T’是类型。
julia> [1, 2, 3] # Массив значений типа `Int`
3-element Vector{Int64}:
1
2
3
朱莉娅>促进(1, 2.3, 4//5) # 这种Int,Float64和Rational值的组合被提升为Float64
(1.0, 2.3, 0.8)
朱莉娅> [1, 2.3, 4//5] # 这将是该数组中元素的类型。
3元素向量{Float64}:
1.0
2.3
0.8
朱莉娅>Float32[1, 2.3, 4//5] # 手动指定项目类型
3元素向量{Float32}:
1.0
2.3
0.8
朱莉娅>[]
任何[]
连接,连接
如果方括号中的参数由单个分号(;
)或换行符分隔,而不是逗号,则它们的内容_被垂直连接(参数本身不用作元素)。
julia> [1:2, 4:5] # Имеет запятую, поэтому конкатенация не производится. Элементами являются сами диапазоны
2-element Vector{UnitRange{Int64}}:
1:2
4:5
julia> [1:2; 4:5]
4-element Vector{Int64}:
1
2
4
5
julia> [1:2
4:5
6]
5-element Vector{Int64}:
1
2
4
5
6
如果参数由制表符、空格或冒号对分隔,则其内容将水平连接。
julia> [1:2 4:5 7:8]
2×3 Matrix{Int64}:
1 4 7
2 5 8
julia> [[1,2] [4,5] [7,8]]
2×3 Matrix{Int64}:
1 4 7
2 5 8
julia> [1 2 3] # Числа также могут объединяться по горизонтали
1×3 Matrix{Int64}:
1 2 3
julia> [1;; 2;; 3;; 4]
1×4 Matrix{Int64}:
1 2 3 4
单个分号(或换行符)可以与空格(或制表符)组合,以水平和垂直连接。
julia> [1 2
3 4]
2×2 Matrix{Int64}:
1 2
3 4
julia> [zeros(Int, 2, 2) [1; 2]
[3 4] 5]
3×3 Matrix{Int64}:
0 0 1
0 0 2
3 4 5
julia> [[1 1]; 2 3; [4 4]]
3×2 Matrix{Int64}:
1 1
2 3
4 4
空格(和制表符)优先于分号,因此首先执行水平连接,然后执行垂直连接。 但是,如果对分号用于水平连接,则首先执行垂直连接,然后将结果水平组合。
julia> [zeros(Int, 2, 2) ; [3 4] ;; [1; 2] ; 5]
3×3 Matrix{Int64}:
0 0 1
0 0 2
3 4 5
julia> [1:2; 4;; 1; 3:4]
3×2 Matrix{Int64}:
1 1
2 3
4 4
正如`;'和';’在第一维和第二维中用于串联一样,您可以使用更多分号:操作的一般原则将是类似的。 分隔符中分号的数量决定了维度``;;;'在第三维中用于连接,;;;;
--在第四个等。 分号越少,优先级越高,因此通常首先组合较低的维度。
julia> [1; 2;; 3; 4;; 5; 6;;;
7; 8;; 9; 10;; 11; 12]
2×3×2 Array{Int64, 3}:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
如前所述,用于水平连接的空格(和制表符)优先于任意数量的分号。 因此,在定义高维数组时,可以先指定字符串,然后指定元素,使文本表示与数组的结构相对应。:
julia> [1 3 5
2 4 6;;;
7 9 11
8 10 12]
2×3×2 Array{Int64, 3}:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
julia> [1 2;;; 3 4;;;; 5 6;;; 7 8]
1×2×2×2 Array{Int64, 4}:
[:, :, 1, 1] =
1 2
[:, :, 2, 1] =
3 4
[:, :, 1, 2] =
5 6
[:, :, 2, 2] =
7 8
朱莉娅> [[1 2;;; 3 4];;;; [5 6];;; [7 8]]
1×2×2×2阵列{Int64, 4}:
[:, :, 1, 1] =
1 2
[:, :, 2, 1] =
3 4
[:, :, 1, 2] =
5 6
[:, :, 2, 2] =
7 8
虽然空格(或制表符)和字符`;’都表示第二维中的串联。 除非双分号用作延续字符,否则它们不能在单个数组表达式中一起使用。 在这种情况下,它们允许在多行上连接(换行符不会被解释为垂直连接)。
julia> [1 2 ;;
3 4]
1×4 Matrix{Int64}:
1 2 3 4
在末尾使用分号,您可以添加另一个长度为1的维度。
julia> [1;;]
1×1 Matrix{Int64}:
1
julia> [2; 3;;;]
2×1×1 Array{Int64, 3}:
[:, :, 1] =
2
3
一个更通用的连接方法是函数 '猫'。 下面是一些辅助函数及其相应的简短表示形式。
语法 | 功能 | 资料描述 |
---|---|---|
按`k`维度组合输入数组 |
||
'[A;B;C;。..]` |
呼叫条目的简短形式是'cat(A。..;dims=1)` |
|
'[A B C...]` |
呼叫条目的简短形式是'cat(A。..;dims=2)` |
|
'[A B;C D;...]` |
垂直和水平连接 |
|
'[A;C;;B;D;;;。..]` |
同时连接n个维度,其中分号的数量表示要连接的维度 |
内含物
内含物是创建数组的通用且有效的方法。 包含语法类似于如何在数学中编写集合的创建。:
A = [ F(x, y, ...) for x=rx, y=ry, ... ]
这个符号的含义是表达式'F(x,y,...)是为变量`x
,y’等计算的。,依次取指定列表中的每个值。 值可以指定为任何可迭代对象,但它们通常是范围,例如`1:n`或`2:(n-1)
,或者显式指定的值数组,例如`[1.2, 3.4, 5.7]. 结果是一个n维密集阵列,其测量值是变量`rx
,`ry`等范围测量值串联的结果。,并与每个计算'F(x,y,...)'返回标量值。
下面的示例根据一维表计算当前元素及其左右相邻元素的加权平均值。 :
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
如在 array literals,结果数组的类型取决于计算元素的类型。 也可以在包含之前显式指定类型。 例如,为了使结果具有单精度,可以编写以下表达式:
Float32[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
生成表达式
包含也可以不将其包含在括号中而写入。 结果是一个称为生成器的对象。 通过迭代这样的对象,您可以根据请求获取值,而无需预先将数组存储在内存中(请参阅该部分 迭代)。 例如,以下表达式总结了不分配内存的序列:
julia> sum(1/n^2 for n=1:1000)
1.6439345666815615
在参数列表中编写具有多个维度的生成器表达式时,必须用括号将生成器与后续参数分开。:
julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;])
ERROR: syntax: invalid iteration specification
"For"之后的所有逗号分隔表达式都被视为范围。 通过添加括号,可以为函数指定第三个参数。 '地图':
julia> map(tuple, (1/(i+j) for i=1:2, j=1:2), [1 3; 2 4])
2×2 Matrix{Tuple{Float64, Int64}}:
(0.5, 1) (0.333333, 3)
(0.333333, 2) (0.25, 4)
生成器通过内部函数实现。 与在语言中使用内部函数的其他情况一样,可以在这些函数中捕获外部作用域的变量。 例如,'sum(p[i]-q[i]for i=1:n)从外部区域捕获三个变量:`p
,q`和`n
。 捕获的变量会影响性能;请参阅 性能提示。
生成器和包含中的范围可能取决于以前的范围;为此使用了几个关键字。:
julia> [(i, j) for i=1:3 for j=1:i]
6-element Vector{Tuple{Int64, Int64}}:
(1, 1)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
(3, 3)
在这种情况下,结果总是一维的。
可以使用`if`关键字过滤生成的值:
julia> [(i, j) for i=1:3 for j=1:i if i+j == 4]
2-element Vector{Tuple{Int64, Int64}}:
(2, 2)
(3, 1)
索引
索引n维数组’A`的语法具有以下一般形式。
X = A[I_1, I_2, ..., I_n]
这里,'I_k’可以是标量整数,整数数组或任何其他 支持的索引。 其中包括 冒号
(':')选择整个维度中的所有索引,范围为`a:c`或’a:b:c`,选择连续段或具有指定步长的段,布尔值数组按索引`true’选择元素。
如果所有索引都是标量的,那么`X`是与数组`A’分开的元素。 否则’X’是一个数组,其维数等于所有索引的维数之和。
例如,如果`I_k’的所有索引都是向量,那么`X’将具有形式'(length(I_1),length(I_2),。..,长度(I_n))',并且在位置'i_1,i_2,。..,i_n`变量’X’将包含值'A[I_1[i_1],I_2[i_2],。..,I_n[i_n]]'。
例子::
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] # только 标量ные индексы
3
julia> A[[1, 2], [1], [1, 2], [1]] # все индексы являются 向量ами
2×1×2×1 Array{Int64, 4}:
[:, :, 1, 1] =
1
2
[:, :, 2, 1] =
5
6
julia> A[[1, 2], [1], [1, 2], 1] # индексы разных типов
2×1×2 Array{Int64, 3}:
[:, :, 1] =
1
2
[:, :, 2] =
5
6
请注意,在最后两种情况下,结果数组的大小是不同的。
如果将’I_1’更改为二维矩阵,则’X’变为形式为`(size(I_1,1),size(I_1,2),length(I_2),的`n+1'维数组。..,长度(I_n))'。 矩阵的引入增加了一个维度。
例子::
julia> A = reshape(collect(1:16), (2, 2, 2, 2));
julia> A[[1 2; 1 2]]
2×2 Matrix{Int64}:
1 2
1 2
julia> A[[1 2; 1 2], 1, 2, 1]
2×2 Matrix{Int64}:
5 6
5 6
在位置`i_1,i_2,i_3,。..,我_{n+1}'包含索引值'A[I_1[i_1,i_2],I_2[i_3],。..,I_n[i_{n+1}]]. 所有具有标量索引的测量都被丢弃。 例如,如果’J’是索引数组,那么`A[2,J,3]`的结果将是大小为`size(J)'的数组。 它的第j个元素填充值’A[2,J[j],3]
。
一个特殊的语法元素是关键字`end',它表示括号中每个维度的最后一个索引,由嵌套最深的索引数组的大小决定。 不带’end`关键字的索引语法等效于调用 'getindex':
X = getindex(A, I_1, I_2, ..., I_n)
例子::
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 Matrix{Int64}:
6 10
7 11
julia> x[1, [2 3; 4 1]]
2×2 Matrix{Int64}:
5 9
13 1
按索引分配
为n维数组`A`的元素赋值的语法具有以下一般形式。
A[I_1, I_2, ..., I_n] = X
这里,'I_k’可以是标量整数,整数数组或任何其他 支持的索引。 其中包括 冒号
(':')选择整个维度中的所有索引,范围为`a:c`或’a:b:c`,选择连续段或具有指定步长的段,布尔值数组按索引`true’选择元素。
如果至少有一个索引’I_k’本身是一个数组,则右侧部分的表达式’X’也必须是与索引`A[I_1,I_2,的结果相同形状的数组。..,I_n],或元素个数相同的向量。 位置
I_1[i_1],I_2[i_2],处的值。..,数组`A`的I_n[i_n]被值
X[i_1,i_2,复盖。..,i_n],其类型在必要时被转换。 元素赋值运算符
。='可用于 翻译`X’到选定的位置:
A[I_1, I_2, ..., I_n] .= X
如在 索引,关键字’end’可以表示括号中每个维度的最后一个索引,由其元素被赋值的数组的大小决定。 没有`end`关键字的索引赋值的语法相当于调用函数 'setindex!`:
setindex!(A, X, I_1, I_2, ..., I_n)
例子::
julia> x = collect(reshape(1:9, 3, 3))
3×3 Matrix{Int64}:
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 Matrix{Int64}:
-1 -4 7
-2 -5 8
3 6 -9
支持的索引类型
在表达式'A[I_1,I_2,。..,I_n]','I_k’的每个元素可以是一个标量索引,一个标量索引数组,也可以是一个表示标量索引数组的对象,可以通过一个函数转化为它 'to_indices`:
-
默认情况下,标量索引包括: **不符合逻辑的整数
-
'CartesianIndex{N}'s,其行为类似于跨越多个维度的整数的`N’元组(详见下文)
-
-
标量索引的数组包括: 向量和整数的多维数组 不用于选择元素的`[]
类型的空数组,例如`A[[]]
(不要与`A[]'混淆) "A:c"或"a:b:c"类型的范围,以选择连续段或具有从"a"到"c"(包括"a")的指定步骤的段 作为`AbstractArray`子类型的标量索引的任何自定义数组 **数组+CartesianIndex{N}+`(详情见下文) -
表示标量索引数组并可由函数转换为该数组的对象 'to_indices',默认参考:
-
'冒号()
(:`), 它表示整个维度或整个数组中的所有索引 **布尔值数组,用于通过"true"索引选择元素(详情见下文)。
-
一些例子:
julia> A = reshape(collect(1:2:18), (3, 3))
3×3 Matrix{Int64}:
1 7 13
3 9 15
5 11 17
julia> A[4]
7
julia> A[[2, 5, 8]]
3-element Vector{Int64}:
3
9
15
julia> A[[1 4; 3 8]]
2×2 Matrix{Int64}:
1 7
5 15
julia> A[[]]
Int64[]
julia> A[1:2:5]
3-element Vector{Int64}:
1
5
9
julia> A[2, :]
3-element Vector{Int64}:
3
9
15
julia> A[:, 3]
3-element Vector{Int64}:
13
15
17
julia> A[:, 3:3]
3×1 Matrix{Int64}:
13
15
17
笛卡尔索引
特殊对象'CartesianIndex{N}'表示作为跨越多个维度的`N`个整数值的元组的标量索引。 例如:
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
这本身可能看起来相当微不足道:CartesianIndex只是将几个整数值组合到表示多维索引的单个对象中。 但是,结合产生CartesianIndex对象的其他形式的索引和迭代器,可以获得非常优雅和高效的代码。 请参阅部分 迭代下面。 一些更复杂的例子可以在https://julialang.org/blog/2016/02/iteration [这个博客条目是关于多维算法和迭代]。
还支持`CartesianIndex'对象的数组。{N}`. 这样的数组表示标量索引的集合,每个索引复盖`N`维。 这使得一种索引形式成为可能,有时称为点索引。 例如,它提供了从数组’A`的第一个"页面"对角定位元素的自上而下的访问:
julia> page = A[:, :, 1]
4×4 Matrix{Int64}:
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 Vector{Int64}:
1
6
11
16
这可以用更简单的方式来表达 点翻译与通常的整数索引相结合(而不是在单独的动作中从数组`A`中提取第一页(page
))。 这种方法也可以与`:`结合使用,同时从两个页面中提取对角线。:
julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), 1]
4-element Vector{Int64}:
1
6
11
16
julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), :]
4×2 Matrix{Int64}:
1 17
6 22
11 27
16 32
'CartesianIndex`和’CartesianIndex’数组与表示维度的最后一个索引的`end`关键字不兼容。 不要在索引可能包含`CartesianIndex`对象或它们的数组的表达式中使用’end'。 |
逻辑索引
基于布尔值数组的索引(通常称为逻辑索引或逻辑掩码索引)允许您通过值为"true"的索引来选择项目。 通过逻辑值`B`的向量进行索引基本上与通过方法返回的整数值的向量进行索引相同 'findall(B)。 同样,通过布尔值的`N`维数组进行索引基本上与通过对象的向量+CartesianIndex进行索引相同{N}+'值为’true'。 逻辑索引必须是与索引维度形状相同的数组,或者它必须是唯一的索引,并且对应于具有索引数组的修改形状的一维表示的形状。 通常,直接使用布尔值数组作为索引比首先调用方法更有效。 'findall'。
julia> x = reshape(1:12, 2, 3, 2)
2×3×2 reshape(::UnitRange{Int64}, 2, 3, 2) with eltype Int64:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
julia> x[:, [true false; false true; true false]]
2×3 Matrix{Int64}:
1 5 9
2 6 10
julia> mask = map(ispow2, x)
2×3×2 Array{Bool, 3}:
[:, :, 1] =
1 0 0
1 1 0
[:, :, 2] =
0 0 0
1 0 0
julia> x[mask]
4-element Vector{Int64}:
1
2
4
8
julia>x[vec(mask)]==x[mask]#索引也可以使用单个逻辑向量来执行。
真的
索引数目
笛卡尔索引
要索引`N’维数组,通常使用精确的`N’索引:每个索引用于选择其维度中的位置。 例如,在三维数组’A=rand(4,3,2)`中,索引`A[2,3,1]`在数组的第一个"页"上的第三列的第二行中选择一个数字。 这种方法通常被称为笛卡尔索引。
线性索引
如果只指定了一个索引’i',则它不表示数组特定维度中的位置。 相反,它用于选择整个数组中的第`i’个元素,按列线性展开。 这种方法称为线性索引。 数组被表示为使用函数将其转换为一维向量 'vec'。
julia> A = [2 6; 4 7; 3 1]
3×2 Matrix{Int64}:
2 6
4 7
3 1
julia> A[5]
7
julia> vec(A)[5]
7
数组`A`中的线性索引可以使用`CartesianIndices(A)转换为`CartesianIndex’进行笛卡尔索引(参见类型描述 'CartesianIndices'),并且一组’N`笛卡尔索引可以使用
+LinearIndices(A)[i_1,i_2,转换为线性索引。..,i_N]+'(参见类型描述 'LinearIndices')。
julia> CartesianIndices(A)[5]
CartesianIndex(2, 2)
julia> LinearIndices(A)[2, 2]
5
需要注意的是,这些转换在性能方面差异很大。 要将线性索引转换为一组笛卡尔索引,需要除法和余数,而反过来只需要乘法和加法。 在现代处理器中,整数除法可以比乘法慢10-50倍。 而一些数组,如基类型 'Array',占用内存的线性区域,并在它们的实现中直接使用线性索引,其他,例如 'Diagonal`,需要全套笛卡尔索引来搜索元素(要确定索引样式,可以使用 'IndexStyle')。
在遍历数组的所有索引时,最好遍历 'eachindex(A)`,而不是`1:length(A)'。 这不仅在`A`是`IndexCartesian`的情况下会更快,而且它还支持自定义索引数组,例如https://github.com/JuliaArrays/OffsetArrays.jl [OffsetArrays]。 如果你只需要值,最好直接迭代数组,即`for a in a'。 |
省略和附加索引
除了线性索引之外,在某些情况下,可以使用更多或更少于`N`的索引来索引`N`维数组。
如果最后一个非索引维度具有单个长度,则可以省略索引。 换句话说,只有当最后一个索引具有索引表达式中可接受范围内的唯一可能值时,才能省略这些索引。 例如,大小的四维数组`(3, 4, 2, 1)` 它可以使用三个索引进行索引,因为被省略的维度(第四个)具有单位长度。 请记住,线性索引优先于此规则。
julia> A = reshape(1:24, 3, 4, 2, 1)
3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64:
[:, :, 1, 1] =
1 4 7 10
2 5 8 11
3 6 9 12
[:, :, 2, 1] =
13 16 19 22
14 17 20 23
15 18 21 24
julia> A[1, 3, 2] # Опускает четвертое измерение (длиной 1)
19
julia> A[1, 3] # Пытается опустить измерения 3 и 4 (длиной 2 и 1)
ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3]
julia> A[19] # Линейное индексирование
19
您可以使用表达式`A[]'省略所有索引-这是获取数组中唯一元素的简单方法,并检查其中是否只有一个元素。
同样,如果数组维度上的所有索引都等于`1`(或者更正式地说,它们是`axes(A,d)`的第一个也是唯一一个元素,其中`d`是相应维度的编号),则可以指定多个`N`索引。 例如,它允许您将向量索引为单列矩阵。:
julia> A = [8, 6, 7]
3-element Vector{Int64}:
8
6
7
julia> A[2, 1]
6
迭代
建议使用以下方法迭代整个数组:
for a in A
# Какие-либо действия с элементом a
end
for i in eachindex(A)
# Какие-либо действия с элементом i и (или) A[i]
end
当需要每个元素的值时,使用第一个构造,而不是其索引。 在第二个构造中,如果`A`是快速线性索引数组,则`i`将是`Int`类型,否则键入`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)
与`for i=1:length(A)`不同,函数 'eachindex'提供对任何类型数组的高效迭代。 此外,它还支持具有自定义索引的通用数组,例如https://github.com/JuliaArrays/OffsetArrays.jl [OffsetArrays]。 |
数组样式
创建自定义类型时 'AbstractArray'您可以指定使用快速线性索引,如下所示:
Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
因此,在`myArray`数组上使用`eachindex`的迭代将使用整数值执行。 如果不指定此样式,则使用默认值’IndexCartesian()'。
数组和向量的运算符和函数
数组支持以下运算符:
-
一元算术:'-','+`
-
二进制算术:
-
,+
,*
,/
,\
,^
-
比较:'==
,'!=
,≈
('isapprox'),'≉`
为了方便数学和其他运算的矢量化,Julia支持 点语法'f.(args...)`,例如`罪。(x)'或’min。(x,y)'。 它提供了数组或数组和标量值的组合的元素操作(操作 广播)。 一个额外的优点是当与其他点调用结合使用时,能够组合成单个循环,例如`sin。(cos.(x)’。
请注意,比较运算符(如`==)应用于整个数组,产生单个逻辑结果。 对于逐元素比较,请使用点运算符,例如
。==`. (在比较操作如'<`的情况下,只有逐元素版本'。<'适用于数组。)
还要注意调用’max.(a,b),即翻译(`广播')功能 'max
by`a`and`b’element by element,and 'maximum(a)`,它在`a’中找到最大的值。 同样,呼叫’min。(a,b)`和`最小值(a)'不同。
广播
有时需要对不同大小的数组执行逐个元素的二进制操作,例如,为矩阵的每列添加一个向量。 将向量复制到矩阵大小是一种低效的方法。
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
当测量值较大时,会消耗大量资源,因此Julia具有功能 'broadcast',它在不使用额外内存的情况下将数组参数中的单个维度扩展为另一个数组的相应维度的大小,然后应用指定的函数元素:
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
点运算符如`。'和'。*'等同于'广播'调用(除了它们被组合为 xref:./arrays.adoc#man-array-and-vectorized-operators-and-functions[如上所述])。 还有一个功能 xref:base/arrays.adoc#Base.Broadcast.broadcast!['广播!']以显式指定应执行翻译的位置(也可以使用赋值运算符`。='用连接调用它)。 实际上,调用是'+f。(args。..)'相当于调用'broadcast(f,args。..),为翻译任何函数提供了方便的语法(点语法)。 嵌套点调用'f.(。..)
(包括呼叫'。+`等。) 自动组合成一个单一的"广播"呼叫。
此外,功能的适用性 'broadcast'不限于数组(参见函数文档)。 它也适用于标量值,元组和其他集合。 默认情况下,只有某些类型的参数被视为标量,包括但不限于`Number`,String',`Symbol
,Type
,`Function`和一些标准的单个对象,如`missing`和`nothing'。 所有其他参数都是逐元素迭代或索引的。
julia> convert.(Float32, [1, 2])
2-element Vector{Float32}:
1.0
2.0
julia> ceil.(UInt8, [1.2 3.4; 5.6 6.7])
2×2 Matrix{UInt8}:
0x02 0x04
0x06 0x07
julia> string.(1:3, ". ", ["First", "Second", "Third"])
3-element Vector{String}:
"1. First"
"2. Second"
"3. Third"
有时在迭代容器的元素(例如数组)时需要防止翻译,这通常允许翻译。 如果将这样的容器放置在另一个容器中(例如,在元素中 'Tuple'),它将在翻译过程中被视为单个值。
julia> ([1, 2, 3], [4, 5, 6]) .+ ([1, 2, 3],)
([2, 4, 6], [5, 7, 9])
julia> ([1, 2, 3], [4, 5, 6]) .+ tuple([1, 2, 3])
([2, 4, 6], [5, 7, 9])
实现
Julia中的基数组类型是抽象类型 '抽象阵列{T,N}`. 它由维度的数量`N`和元素的类型`T`参数化。 'AbstractVector'和 'AbstractMatrix'是一维和二维情况的别名。 使用AbstractArray的操作使用高级运算符和函数定义,以便它们不依赖于数组在内存中的存储方式。 它们通常适用于任何特定的数组实现。
'AbstractArray’类型包括任何甚至远程类似于数组的东西,并且实现可能与传统数组有很大不同。 例如,元素可以根据请求计算而不是存储在存储器中。 但是,任何特定类型的’AbstractArray'{T,N}'通常应该至少实现一个功能 'size(A)(返回类型为’Int’的元素元组),方法 'getindex(A,i)和函数 'getindex(A,i1,...,iN)'。 可变数组也必须实现该函数 'setindex!
. 希望这些操作的时间复杂度接近常数,否则一些用于处理数组的函数可能需要意外的长时间才能执行。 具体类型一般也应提供一种方法。 similar(A,T=eltype(A),dims=size(A)),用于在内存中放置函数的相同数组 '复制'等外部操作。 不管’AbstractArray的内部表示{T,N}`T’是用整数索引返回的对象类型('+a[1,…,1]+`with a non-empty’A),而`N`应该是函数返回的元组的长度 '尺寸'。 有关定义’AbstractArray’的自定义实现的更多信息,请参阅 请参阅接口章节中的数组接口指南。
'DenseArray’是’AbstractArray’类型的抽象子类型,用于所有元素以连续序列形式存储在列中的数组(请参阅 性能提示中的附加说明)。 'Array'是`DenseArray’的特定实例; '向量'及 'Matrix'是一维和二维情况的别名。 除了AbstractArray的所有子类型所需的操作之外,为Array实现的特殊操作非常少;处理数组的库基本上是通用的,以便所有用户数组都以类似的方式起作用。
'SubArray`是’AbstractArray’类型的特化,它用于索引原始数组占用的相同内存区域,而不是复制它。 'SubArray’对象是使用函数创建的 'view',其调用方式与 'getindex'(以数组和一组索引作为参数)。 函数的结果 view'
看起来和结果一样 'getindex',但数据仍然存在。 功能 'view'将输入索引向量存储在’SubArray’对象中,然后可用于间接索引源数组。 如果你把一个宏 @views`在表达式或代码块之前,数组的任何切片'+array[。..此表达式中的]’将被转换为创建"子数组"表示。
'BitArray'是紧凑的"打包"逻辑数组,为每个逻辑值存储一位。 它们可以以与arrays`Array相同的方式使用{Bool}'(用于存储需要一个字节的逻辑值),并分别使用`Array(bitarray)`和`BitArray(array)`转换为此类数组或反之亦然。
具有指定步长的数组是其元素以一定间隔(步长)存储在内存中的数组。 具有指定步骤的数组可以与支持的元素类型一起传递到外部库(与Julia无关),例如BLAS或LAPACK,只需通过传递其指针('pointer')和每个维度的一个步骤。 'stride(A,d)是维度`d’中元素之间的距离。 例如,通过调用`rand(5,7,2)`返回的内置类型`Array`的数组的元素按列连续放置在内存中。 这意味着第一个维度中的步骤,即同一列元素之间的距离,是'1:
julia> A = rand(5, 7, 2);
julia> stride(A, 1)
1
第二维中的步骤是同一行中的元素之间的距离,跳过列中的元素(5
)。 同样,两个"页面"(在第三维)之间的距离需要跳过'5*7==35`元素。 步骤(`strides')数组是这三个数字的元组:
julia> strides(A)
(1, 5, 35)
在这种情况下,在内存中跳过的元素的数量对应于跳过的线性索引的数量。 这仅适用于连续数组,如`Array`(以及`DenseArray`的其他子类型),但通常情况并非如此。 具有范围索引的表示是具有给定步骤的_connected数组的一个很好的示例。 考虑`V=@view A的例子[1:3:4, 2:2:6, 2:-1:1]`. 表示`V’指的是与数组`A’相同的内存区域,但一些元素被跳过并重新排序。 第一次测量`V`的步骤是`3',因为只有每三行从源阵列中选择:
julia> V = @view A[1:3:4, 2:2:6, 2:-1:1];
julia> stride(V, 1)
3
同样,每第二列都是从原始数组`A`中选择的,因此在第二维的索引之间切换时,应该跳过两列五行。:
julia> stride(V, 2)
10
第三个维度更有趣,因为这里的顺序颠倒了! 要从第一个"页面"移动到第二个,必须遵循_back direction_中的内存,因此,这个维度中的步骤是负的!
julia> stride(V, 3)
-35
这意味着表示`V`的`指针’实际上指向数组`A’所占用的内存块的中间,并且指的是内存中的向前和向后的元素。 有关使用给定步骤定义自己的数组的详细信息,请参阅 具有给定步骤的数组的接口指南。 `StridedVector'和 'StridedMatrix'是许多内置数组类型的方便别名,这些类型被视为具有给定步骤的数组。 它们允许您选择特定的实现,这些实现只使用指针和数组步数来调用专门的、优化的BLAS和LAPACK函数。
重要的是要强调,数组步骤与内存偏移有关,而不是索引。 如果您需要将线性(单个)索引转换为笛卡尔(多个)或反之亦然,请参阅类型的描述。 'LinearIndices'和 'CartesianIndices'。