Single- and multi-dimensional Arrays
جوليا، مثل معظم لغات الحوسبة التقنية، توفر تنفيذًا متقدمًا للمصفوفات. تولي معظم لغات الحوسبة التقنية اهتمامًا كبيرًا لتنفيذ المصفوفات على حساب الحاويات الأخرى. لا تعالج جوليا المصفوفات بطريقة خاصة. تم تنفيذ مكتبة المصفوفات تقريبًا بالكامل في جوليا نفسها، وتستمد أدائها من المترجم، تمامًا مثل أي كود آخر مكتوب في جوليا. وبالتالي، من الممكن أيضًا تعريف أنواع مصفوفات مخصصة من خلال الوراثة من AbstractArray
. راجع manual section on the AbstractArray interface لمزيد من التفاصيل حول تنفيذ نوع مصفوفة مخصص.
مصفوفة هي مجموعة من الكائنات المخزنة في شبكة متعددة الأبعاد. يُسمح بالمصفوفات ذات الأبعاد الصفرية، انظر this FAQ entry. في الحالة الأكثر عمومية، قد تحتوي المصفوفة على كائنات من النوع Any
. لأغراض الحوسبة الأكثر شيوعًا، يجب أن تحتوي المصفوفات على كائنات من نوع أكثر تحديدًا، مثل Float64
أو Int32
.
بشكل عام، على عكس العديد من لغات البرمجة التقنية الأخرى، لا تتوقع جوليا أن تُكتب البرامج بأسلوب متجه لتحقيق الأداء. يستخدم مترجم جوليا استنتاج النوع وينتج شيفرة محسّنة لفهرسة المصفوفات السليمة، مما يسمح بكتابة البرامج بأسلوب مريح وقابل للقراءة، دون التضحية بالأداء، وأحيانًا باستخدام ذاكرة أقل.
في جوليا، جميع المعاملات في الدوال هي passed by sharing (أي عن طريق المؤشرات). بعض لغات الحوسبة التقنية تمرر المصفوفات بالقيمة، وعلى الرغم من أن هذا يمنع التعديل العرضي من قبل الدوال على قيمة في المتصل، إلا أنه يجعل تجنب النسخ غير المرغوب فيه للمصفوفات صعبًا. وفقًا للتقاليد، فإن اسم الدالة الذي ينتهي بـ !
يشير إلى أنها ستقوم بتغيير أو تدمير قيمة واحدة أو أكثر من معاملاتها (قارن، على سبيل المثال، sort
و sort!
). يجب على الدوال المستدعاة أن تقوم بعمل نسخ صريحة لضمان عدم تعديل المدخلات التي لا تنوي تغييرها. يتم تنفيذ العديد من الدوال غير المتغيرة عن طريق استدعاء دالة بنفس الاسم مع إضافة !
في النهاية على نسخة صريحة من المدخل، وإرجاع تلك النسخة.
Basic Functions
Function | Description |
---|---|
eltype(A) | the type of the elements contained in A |
length(A) | the number of elements in A |
ndims(A) | the number of dimensions of A |
size(A) | a tuple containing the dimensions of A |
size(A,n) | the size of A along dimension n |
axes(A) | a tuple containing the valid indices of A |
axes(A,n) | a range expressing the valid indices along dimension n |
eachindex(A) | an efficient iterator for visiting each position in A |
stride(A,k) | the stride (linear index distance between adjacent elements) along dimension k |
strides(A) | a tuple of the strides in each dimension |
Construction and Initialization
تتوفر العديد من الوظائف لبناء وتهيئة المصفوفات. في القائمة التالية من هذه الوظائف، يمكن أن تأخذ الاستدعاءات التي تحتوي على وسيط dims...
إما مجموعة واحدة من أحجام الأبعاد أو سلسلة من أحجام الأبعاد الممررة كعدد متغير من الوسائط. تقبل معظم هذه الوظائف أيضًا إدخالًا أوليًا T
، وهو نوع عنصر المصفوفة. إذا تم حذف النوع T
، فسيكون الافتراضي هو Float64
.
Function | Description |
---|---|
Array{T}(undef, dims...) | an uninitialized dense Array |
zeros(T, dims...) | an Array of all zeros |
ones(T, dims...) | an Array of all ones |
trues(dims...) | a BitArray with all values true |
falses(dims...) | a BitArray with all values false |
reshape(A, dims...) | an array containing the same data as A , but with different dimensions |
copy(A) | copy A |
deepcopy(A) | copy A , recursively copying its elements |
similar(A, T, dims...) | an uninitialized array of the same type as A (dense, sparse, etc.), but with the specified element type and dimensions. The second and third arguments are both optional, defaulting to the element type and dimensions of A if omitted. |
reinterpret(T, A) | an array with the same binary data as A , but with element type T |
rand(T, dims...) | an Array with random, iid [1] and uniformly distributed values. For floating point types T , the values lie in the half-open interval $[0, 1)$. |
randn(T, dims...) | an Array with random, iid and standard normally distributed values |
Matrix{T}(I, m, n) | m -by-n identity matrix. Requires using LinearAlgebra for I . |
range(start, stop, n) | a range of n linearly spaced elements from start to stop |
fill!(A, x) | fill the array A with the value x |
fill(x, dims...) | an Array filled with the value x . In particular, fill(x) constructs a zero-dimensional Array containing 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
.
Array literals
يمكن أيضًا إنشاء المصفوفات مباشرة باستخدام الأقواس المربعة؛ فإن الصيغة [A, B, C, ...]
تنشئ مصفوفة أحادية البعد (أي، متجه) تحتوي على الوسائط المفصولة بفواصل كعناصر لها. يتم تحديد نوع العنصر (eltype
) للمصفوفة الناتجة تلقائيًا بواسطة أنواع الوسائط داخل الأقواس. إذا كانت جميع الوسائط من نفس النوع، فإن ذلك هو eltype
الخاص بها. إذا كانت جميعها تحتوي على promotion type مشترك، فإنها تتحول إلى ذلك النوع باستخدام convert
ويكون ذلك النوع هو eltype
للمصفوفة. خلاف ذلك، يتم إنشاء مصفوفة غير متجانسة يمكن أن تحتوي على أي شيء — Vector{Any}
— وهذا يشمل الحرفي []
حيث لا يتم إعطاء أي وسائط. Array literal can be typed مع الصيغة T[A, B, C, ...]
حيث T
هو نوع.
julia> [1, 2, 3] # An array of `Int`s
3-element Vector{Int64}:
1
2
3
julia> promote(1, 2.3, 4//5) # This combination of Int, Float64 and Rational promotes to Float64
(1.0, 2.3, 0.8)
julia> [1, 2.3, 4//5] # Thus that's the element type of this Array
3-element Vector{Float64}:
1.0
2.3
0.8
julia> Float32[1, 2.3, 4//5] # Specify element type manually
3-element Vector{Float32}:
1.0
2.3
0.8
julia> []
Any[]
Concatenation
إذا كانت الحجج داخل الأقواس المربعة مفصولة بفواصل منقوطة مفردة (;
) أو أسطر جديدة بدلاً من الفواصل، فإن محتوياتها تُجمع عمودياً معًا بدلاً من استخدام الحجج كعناصر بنفسها.
julia> [1:2, 4:5] # Has a comma, so no concatenation occurs. The ranges are themselves the elements
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] # Numbers can also be horizontally concatenated
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
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
على الرغم من أن كلاهما يعني الربط في البعد الثاني، لا يمكن أن تظهر المسافات (أو علامات التبويب) و ;;
في نفس تعبير المصفوفة ما لم يكن الفاصلة المنقوطة المزدوجة ببساطة تعمل كحرف "استمرار السطر". وهذا يسمح لربط أفقي واحد أن يمتد عبر عدة أسطر (دون أن يتم تفسير فاصل السطر على أنه ربط عمودي).
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
بشكل أكثر عمومية، يمكن تحقيق التراص من خلال دالة cat
. هذه الصيغ هي اختصارات لاستدعاءات الدوال التي هي بدورها دوال ملائمة:
Syntax | Function | Description |
---|---|---|
cat | concatenate input arrays along dimension(s) k | |
[A; B; C; ...] | vcat | shorthand for cat(A...; dims=1) |
[A B C ...] | hcat | shorthand for cat(A...; dims=2) |
[A B; C D; ...] | hvcat | simultaneous vertical and horizontal concatenation |
[A; C;; B; D;;; ...] | hvncat | simultaneous n-dimensional concatenation, where number of semicolons indicate the dimension to concatenate |
Typed array literals
يمكن إنشاء مصفوفة بنوع عنصر محدد باستخدام الصيغة T[A, B, C, ...]
. ستقوم هذه الصيغة بإنشاء مصفوفة أحادية البعد بنوع عنصر T
، مُهيأة لتحتوي على العناصر A
و B
و C
، إلخ. على سبيل المثال، Any[x, y, z]
تنشئ مصفوفة غير متجانسة يمكن أن تحتوي على أي قيم.
يمكن أن يتم تمييز بناء الجملة للتجميع بشكل مشابه بنوع لتحديد نوع عنصر النتيجة.
julia> [[1 2] [3 4]]
1×4 Matrix{Int64}:
1 2 3 4
julia> Int8[[1 2] [3 4]]
1×4 Matrix{Int8}:
1 2 3 4
Comprehensions
توفر الفهم طريقة عامة وقوية لبناء المصفوفات. صياغة الفهم مشابهة لكتابة مجموعة البناء في الرياضيات:
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-d بأبعاد هي دمج أبعاد نطاقات المتغيرات 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 ]
Generator Expressions
يمكن أيضًا كتابة الفهمات بدون الأقواس المربعة المحيطة، مما ينتج عنه كائن يُعرف باسم مولد. يمكن تكرار هذا الكائن لإنتاج القيم عند الطلب، بدلاً من تخصيص مصفوفة وتخزينها مسبقًا (انظر Iteration). على سبيل المثال، التعبير التالي يجمع سلسلة دون تخصيص الذاكرة:
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
على أنها نطاقات. يسمح لنا إضافة الأقواس بإضافة حجة ثالثة إلى map
:
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
من النطاق المحيط. يمكن أن تقدم المتغيرات الملتقطة تحديات في الأداء؛ انظر performance tips.
يمكن أن تعتمد النطاقات في المولدات والفهمات على النطاقات السابقة من خلال كتابة عدة كلمات for
:
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)
في مثل هذه الحالات، تكون النتيجة دائمًا 1-d.
يمكن تصفية القيم المولدة باستخدام الكلمة الرئيسية 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)
Indexing
الصيغة العامة لفهرسة مصفوفة n-dimensional A
هي:
X = A[I_1, I_2, ..., I_n]
حيث يمكن أن يكون كل I_k
عدد صحيح قياسي، أو مصفوفة من الأعداد الصحيحة، أو أي شيء آخر supported index. يشمل ذلك Colon
(:
) لاختيار جميع الفهارس ضمن البعد بالكامل، ونطاقات من الشكل a:c
أو a:b:c
لاختيار مقاطع متجاورة أو متباعدة، ومصفوفات من القيم المنطقية لاختيار العناصر في فهارسها true
.
إذا كانت جميع المؤشرات عددية، فإن النتيجة X
هي عنصر واحد من المصفوفة A
. خلاف ذلك، فإن X
هو مصفوفة بنفس عدد الأبعاد مثل مجموع أبعاد جميع المؤشرات.
إذا كانت جميع المؤشرات I_k
متجهات، على سبيل المثال، فإن شكل X
سيكون (length(I_1), length(I_2), ..., length(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] # 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
لاحظ كيف أن حجم المصفوفة الناتجة مختلف في الحالتين الأخيرتين.
إذا تم تغيير I_1
إلى مصفوفة ثنائية الأبعاد، فإن X
يصبح مصفوفة بعدد أبعاد n+1
بشكل (size(I_1, 1), size(I_1, 2), length(I_2), ..., length(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, ..., i_{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
Indexed Assignment
الصيغة العامة لتعيين القيم في مصفوفة متعددة الأبعاد A
هي:
A[I_1, I_2, ..., I_n] = X
حيث يمكن أن يكون كل I_k
عدد صحيح قياسي، أو مصفوفة من الأعداد الصحيحة، أو أي شيء آخر supported index. يشمل ذلك Colon
(:
) لاختيار جميع الفهارس ضمن البعد بالكامل، ونطاقات من الشكل a:c
أو a:b:c
لاختيار مقاطع متجاورة أو متباعدة، ومصفوفات من القيم المنطقية لاختيار العناصر في فهارسها true
.
إذا كانت جميع المؤشرات I_k
أعداد صحيحة، فإن القيمة في الموقع I_1, I_2, ..., I_n
من A
يتم الكتابة فوقها بقيمة X
convert
إلى eltype
من A
إذا لزم الأمر.
إذا كان أي فهرس I_k
هو نفسه مصفوفة، يجب أن تكون الجهة اليمنى X
أيضًا مصفوفة بنفس شكل نتيجة الفهرسة A[I_1, I_2, ..., I_n]
أو متجه بنفس عدد العناصر. يتم الكتابة فوق القيمة في الموقع I_1[i_1], I_2[i_2], ..., I_n[i_n]
من A
بالقيمة X[i_1, i_2, ..., i_n]
، مع التحويل إذا لزم الأمر. يمكن استخدام عامل الإسناد العنصري .=
لـ broadcast X
عبر المواقع المختارة:
A[I_1, I_2, ..., I_n] .= X
تمامًا كما في Indexing، يمكن استخدام الكلمة الرئيسية 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
Supported index types
في التعبير A[I_1, I_2, ..., I_n]
، قد يكون كل I_k
فهرسًا عدديًا، أو مصفوفة من الفهارس العددية، أو كائنًا يمثل مصفوفة من الفهارس العددية ويمكن تحويله إلى ذلك بواسطة to_indices
:
مؤشر عددي. بشكل افتراضي، يتضمن هذا:
- أعداد غير بوليانية
CartesianIndex{N}
، والتي تتصرف مثلN
-tuple من الأعداد الصحيحة التي تمتد عبر أبعاد متعددة (انظر أدناه لمزيد من التفاصيل)
مصفوفة من المؤشرات السلمية. وهذا يشمل:
- المتجهات والمصفوفات متعددة الأبعاد من الأعداد الصحيحة
- المصفوفات الفارغة مثل
[]
، التي لا تختار أي عناصر مثلA[[]]
(لا تخلط بينها وبينA[]
) - نطاقات مثل
a:c
أوa:b:c
، التي تختار مقاطع متجاورة أو متباعدة منa
إلىc
(شاملة) - أي مصفوفة مخصصة من مؤشرات قياسية تكون من نوع فرعي لـ
AbstractArray
- مصفوفات
CartesianIndex{N}
(انظر أدناه لمزيد من التفاصيل)
كائن يمثل مصفوفة من مؤشرات قياسية ويمكن تحويله إلى مثل هذه بواسطة
to_indices
. بشكل افتراضي، يتضمن هذا:Colon()
(:
)، والذي يمثل جميع الفهارس ضمن بُعد كامل أو عبر المصفوفة بأكملها- مصفوفات من القيم المنطقية، التي تختار العناصر عند مؤشرات
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
Cartesian indices
يمثل الكائن الخاص 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
، يمكن أن ينتج عن ذلك كود أنيق وفعال للغاية. انظر Iteration أدناه، وللحصول على بعض الأمثلة الأكثر تقدمًا، انظر this blog post on multidimensional algorithms and 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
يمكن التعبير عن هذا بشكل أبسط بكثير باستخدام dot broadcasting ومن خلال دمجه مع فهرس عدد صحيح عادي (بدلاً من استخراج page
الأولى من A
كخطوة منفصلة). يمكن حتى دمجه مع :
لاستخراج كل من القطرين من الصفحتين في نفس الوقت:
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
لتمثيل آخر فهرس بعد بعد. لا تستخدم end
في تعبيرات الفهرسة التي قد تحتوي على إما CartesianIndex
أو مصفوفات منها.
Logical indexing
غالبًا ما يُشار إليه بالت indexing المنطقي أو indexing باستخدام قناع منطقي، يقوم indexing بواسطة مصفوفة بوليانية باختيار العناصر في الفهارس حيث تكون قيمها true
. يعتبر indexing بواسطة متجه بولياني B
فعليًا هو نفسه indexing بواسطة متجه الأعداد الصحيحة الذي يتم إرجاعه بواسطة findall(B)
. وبالمثل، فإن indexing بواسطة مصفوفة بوليانية ذات N
أبعاد هو فعليًا نفس indexing بواسطة متجه من 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] # we can also index with a single Boolean vector
true
Number of indices
Cartesian indexing
الطريقة العادية للوصول إلى مصفوفة ذات N
أبعاد هي استخدام N
مؤشرات بالضبط؛ كل مؤشر يحدد الموقع (المواقع) في بعده الخاص. على سبيل المثال، في المصفوفة ثلاثية الأبعاد A = rand(4, 3, 2)
، سيختار A[2, 3, 1]
الرقم في الصف الثاني من العمود الثالث في "الصفحة" الأولى من المصفوفة. وغالبًا ما يُشار إلى ذلك باسم الفهرسة الكارتيزية.
Linear indexing
عندما يتم توفير فهرس واحد فقط 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
إلى CartesianIndex
من أجل الفهرسة الكارتيزية باستخدام CartesianIndices(A)[i]
(انظر 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
، ولكنها ستدعم أيضًا المصفوفات ذات الفهرسة المخصصة، مثل OffsetArrays. إذا كانت القيم فقط هي المطلوبة، فمن الأفضل التكرار على المصفوفة مباشرة، أي for a in A
.
Omitted and extra indices
بالإضافة إلى الفهرسة الخطية، يمكن فهرسة مصفوفة ذات 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] # Omits the fourth dimension (length 1)
19
julia> A[1, 3] # Attempts to omit dimensions 3 & 4 (lengths 2 and 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] # Linear indexing
19
عند حذف جميع الفهارس باستخدام A[]
، توفر هذه الدلالة أسلوبًا بسيطًا لاسترجاع العنصر الوحيد في مصفوفة وضمان في الوقت نفسه أنه كان هناك عنصر واحد فقط.
بالمثل، يمكن توفير أكثر من N
فهرس إذا كانت جميع الفهارس التي تتجاوز أبعاد المصفوفة هي 1
(أو بشكل أكثر عمومية هي العنصر الأول والوحيد من axes(A, d)
حيث d
هو رقم البعد المعين). وهذا يسمح بفهرسة المتجهات مثل المصفوفات ذات العمود الواحد، على سبيل المثال:
julia> A = [8, 6, 7]
3-element Vector{Int64}:
8
6
7
julia> A[2, 1]
6
Iteration
الطرق الموصى بها للتكرار على مصفوفة كاملة هي
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
يتم استخدام البناء الأول عندما تحتاج إلى القيمة، ولكن ليس الفهرس، لكل عنصر. في البناء الثاني، سيكون i
من نوع Int
إذا كان A
نوع مصفوفة مع فهرسة خطية سريعة؛ خلاف ذلك، سيكون i
من نوع 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
وسيلة فعالة للتكرار على أي نوع من المصفوفات. بالإضافة إلى ذلك، يدعم هذا أيضًا المصفوفات العامة مع الفهرسة المخصصة مثل OffsetArrays.
Array traits
إذا كتبت نوعًا مخصصًا AbstractArray
، يمكنك تحديد أنه يحتوي على فهرسة خطية سريعة باستخدام
Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
سيؤدي هذا الإعداد إلى جعل تكرار eachindex
على MyArray
يستخدم الأعداد الصحيحة. إذا لم تحدد هذه السمة، سيتم استخدام القيمة الافتراضية IndexCartesian()
.
Array and Vectorized Operators and Functions
تدعم المشغلين التالية للمصفوفات:
- الحساب الأحادي –
-
،+
- الحساب الثنائي –
-
،+
،*
،/
،\
،^
- المقارنة –
==
،!=
،≈
(isapprox
)،≉
لتمكين التوجيه المريح للعمليات الرياضية وغيرها، تستخدم جوليا provides the dot syntax f.(args...)
، مثل sin.(x)
أو min.(x, y)
، للعمليات العنصرية على المصفوفات أو مزيج من المصفوفات والقياسات (عملية Broadcasting)؛ هذه لها الميزة الإضافية المتمثلة في "الدمج" في حلقة واحدة عند دمجها مع مكالمات نقطية أخرى، مثل sin.(cos.(x))
.
أيضًا، كل عامل ثنائي يدعم dot version الذي يمكن تطبيقه على المصفوفات (ومجموعات من المصفوفات والسكالار) في مثل fused broadcasting operations، على سبيل المثال z .== sin.(x .* y)
.
لاحظ أن المقارنات مثل ==
تعمل على المصفوفات الكاملة، مما يعطي إجابة منطقية واحدة. استخدم عوامل التشغيل النقطية مثل .==
للمقارنات العنصرية. (بالنسبة لعمليات المقارنة مثل <
، فإن النسخة العنصرية فقط .<
هي القابلة للتطبيق على المصفوفات.)
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)
.
Broadcasting
من المفيد أحيانًا إجراء عمليات ثنائية على العناصر بشكل فردي على مصفوفات بأحجام مختلفة، مثل إضافة متجه إلى كل عمود من مصفوفة. ستكون طريقة غير فعالة للقيام بذلك هي تكرار المتجه ليصبح بحجم المصفوفة:
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
هذا مضيعة عندما تصبح الأبعاد كبيرة، لذا توفر جوليا 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
Dotted operators مثل .+
و .*
تعادل استدعاءات broadcast
(باستثناء أنها تدمج، كما في described above). هناك أيضًا دالة broadcast!
لتحديد وجهة صريحة (يمكن الوصول إليها أيضًا بطريقة دمج من خلال تعيين .=
). في الواقع، f.(args...)
تعادل broadcast(f, args...)
، مما يوفر بناء جملة مريح لبث أي دالة (dot syntax). "استدعاءات النقطة" المتداخلة f.(...)
(بما في ذلك الاستدعاءات إلى .+
وما إلى ذلك) automatically fuse تدمج في استدعاء broadcast
واحد.
بالإضافة إلى ذلك، broadcast
ليس محدودًا بالمصفوفات (انظر وثائق الدالة)؛ بل يتعامل أيضًا مع القيم الفردية، والصفوف، ومجموعات أخرى. بشكل افتراضي، يتم اعتبار بعض أنواع المعاملات فقط كقيم فردية، بما في ذلك (ولكن لا تقتصر على) Number
s، String
s، Symbol
s، Type
s، Function
s وبعض الكائنات الفردية الشائعة مثل 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])
Implementation
نوع المصفوفة الأساسية في جوليا هو النوع المجرد AbstractArray{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))
، والتي تُستخدم لتخصيص مصفوفة مشابهة لـ copy
وعمليات أخرى خارج المكان. بغض النظر عن كيفية تمثيل AbstractArray{T,N}
داخليًا، فإن T
هو نوع الكائن الذي يتم إرجاعه بواسطة الفهرسة الصحيحة (A[1, ..., 1]
، عندما لا تكون A
فارغة) و N
يجب أن يكون طول المجموعة التي يتم إرجاعها بواسطة size
. لمزيد من التفاصيل حول تعريف تطبيقات AbstractArray
المخصصة، انظر إلى array interface guide in the interfaces chapter.
DenseArray
هو نوع مجرد من AbstractArray
يهدف إلى تضمين جميع المصفوفات حيث يتم تخزين العناصر بشكل متجاور بترتيب عمودي (انظر additional notes in Performance Tips). نوع Array
هو حالة محددة من DenseArray
؛ Vector
و Matrix
هما أسماء مستعارة للحالات ذات البعد الواحد والبعدين. تم تنفيذ عدد قليل جداً من العمليات بشكل محدد لـ Array
بخلاف تلك المطلوبة لجميع AbstractArray
s؛ تم تنفيذ معظم مكتبة المصفوفات بطريقة عامة تسمح لجميع المصفوفات المخصصة بالتصرف بشكل مشابه.
SubArray
هو تخصص لـ AbstractArray
يقوم بعمليات الفهرسة من خلال مشاركة الذاكرة مع المصفوفة الأصلية بدلاً من نسخها. يتم إنشاء SubArray
باستخدام دالة view
، والتي يتم استدعاؤها بنفس الطريقة مثل getindex
(مع مصفوفة وسلسلة من وسائط الفهرسة). نتيجة 4d61726b646f776e2e436f64652822222c2022766965772229_40726566
تبدو مشابهة لنتيجة 4d61726b646f776e2e436f64652822222c2022676574696e6465782229_40726566
، باستثناء أن البيانات تظل في مكانها. يقوم 4d61726b646f776e2e436f64652822222c2022766965772229_40726566
بتخزين متجهات الفهرسة المدخلة في كائن SubArray
، والذي يمكن استخدامه لاحقًا لفهرسة المصفوفة الأصلية بشكل غير مباشر. من خلال وضع ماكرو @views
أمام تعبير أو كتلة من الشيفرة، سيتم تحويل أي شريحة array[...]
في ذلك التعبير لإنشاء عرض SubArray
بدلاً من ذلك.
BitArray
هي مصفوفات بوليانية "معبأة" فعالة من حيث المساحة، والتي تخزن بتًا واحدًا لكل قيمة بوليانية. يمكن استخدامها بشكل مشابه لمصفوفات Array{Bool}
(التي تخزن بايتًا واحدًا لكل قيمة بوليانية)، ويمكن تحويلها من/إلى الأخيرة عبر Array(bitarray)
و BitArray(array)
، على التوالي.
مصفوفة "مُتباعدة" إذا كانت مخزنة في الذاكرة مع مسافات محددة جيدًا (تباعدات) بين عناصرها. يمكن تمرير مصفوفة متباعدة مع نوع عنصر مدعوم إلى مكتبة خارجية (غير جوليا) مثل BLAS أو LAPACK ببساطة عن طريق تمرير pointer
والتباعد لكل بُعد. stride(A, d)
هو المسافة بين العناصر على طول البُعد d
. على سبيل المثال، المصفوفة المدمجة Array
التي يتم إرجاعها بواسطة rand(5,7,2)
مرتبة عناصرها بشكل متجاور في ترتيب رئيسي عمودي. هذا يعني أن تباعد البُعد الأول - المسافة بين العناصر في نفس العمود - هو 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
الفرعية الأخرى) وليست صحيحة بشكل عام. تعتبر العروض ذات مؤشرات النطاق مثالًا جيدًا على المصفوفات المتباينة غير المتجاورة؛ اعتبر 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
البعد الثالث مثير للاهتمام لأن ترتيبه معكوس! لذلك للانتقال من "الصفحة" الأولى إلى الثانية يجب أن يذهب إلى الوراء في الذاكرة، وبالتالي فإن خطوته في هذا البعد سلبية!
julia> stride(V, 3)
-35
هذا يعني أن pointer
لـ V
يشير فعليًا إلى منتصف كتلة الذاكرة لـ A
، ويشير إلى العناصر في الذاكرة سواء للخلف أو للأمام. راجع interface guide for strided arrays لمزيد من التفاصيل حول كيفية تعريف مصفوفاتك الخاصة ذات الخطوات. StridedVector
و StridedMatrix
هي أسماء مستعارة ملائمة للعديد من أنواع المصفوفات المدمجة التي تعتبر مصفوفات ذات خطوات، مما يسمح لها بإرسال الطلبات إلى تنفيذات متخصصة مختارة تستدعي وظائف BLAS و LAPACK المحسّنة باستخدام فقط المؤشر والخطوات.
من المهم التأكيد على أن الخطوات تتعلق بالإزاحات في الذاكرة بدلاً من الفهرسة. إذا كنت تبحث عن تحويل بين الفهرسة الخطية (فهرسة ذات مؤشر واحد) والفهرسة الكارتيزية (فهرسة متعددة المؤشرات)، انظر LinearIndices
و CartesianIndices
.
- 1iid, independently and identically distributed.