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

FunctionDescription
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.

FunctionDescription
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. هذه الصيغ هي اختصارات لاستدعاءات الدوال التي هي بدورها دوال ملائمة:

SyntaxFunctionDescription
catconcatenate input arrays along dimension(s) k
[A; B; C; ...]vcatshorthand for cat(A...; dims=1)
[A B C ...]hcatshorthand for cat(A...; dims=2)
[A B; C D; ...]hvcatsimultaneous vertical and horizontal concatenation
[A; C;; B; D;;; ...]hvncatsimultaneous 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:

  1. مؤشر عددي. بشكل افتراضي، يتضمن هذا:

    • أعداد غير بوليانية
    • CartesianIndex{N}، والتي تتصرف مثل N-tuple من الأعداد الصحيحة التي تمتد عبر أبعاد متعددة (انظر أدناه لمزيد من التفاصيل)
  2. مصفوفة من المؤشرات السلمية. وهذا يشمل:

    • المتجهات والمصفوفات متعددة الأبعاد من الأعداد الصحيحة
    • المصفوفات الفارغة مثل []، التي لا تختار أي عناصر مثل A[[]] (لا تخلط بينها وبين A[])
    • نطاقات مثل a:c أو a:b:c، التي تختار مقاطع متجاورة أو متباعدة من a إلى c (شاملة)
    • أي مصفوفة مخصصة من مؤشرات قياسية تكون من نوع فرعي لـ AbstractArray
    • مصفوفات CartesianIndex{N} (انظر أدناه لمزيد من التفاصيل)
  3. كائن يمثل مصفوفة من مؤشرات قياسية ويمكن تحويله إلى مثل هذه بواسطة 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
Warning

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 لاستكشاف أيهما أي).

Warning

عند التكرار على جميع الفهارس لمصفوفة، من الأفضل التكرار على 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)
Note

بالمقارنة مع for i = 1:length(A)، يوفر التكرار باستخدام eachindex وسيلة فعالة للتكرار على أي نوع من المصفوفات. بالإضافة إلى ذلك، يدعم هذا أيضًا المصفوفات العامة مع الفهرسة المخصصة مثل OffsetArrays.

Array traits

إذا كتبت نوعًا مخصصًا AbstractArray، يمكنك تحديد أنه يحتوي على فهرسة خطية سريعة باستخدام

Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()

سيؤدي هذا الإعداد إلى جعل تكرار eachindex على MyArray يستخدم الأعداد الصحيحة. إذا لم تحدد هذه السمة، سيتم استخدام القيمة الافتراضية IndexCartesian().

Array and Vectorized Operators and Functions

تدعم المشغلين التالية للمصفوفات:

  1. الحساب الأحادي – -، +
  2. الحساب الثنائي – -، +، *، /، \، ^
  3. المقارنة – ==، !=، (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 broadcasts 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 ليس محدودًا بالمصفوفات (انظر وثائق الدالة)؛ بل يتعامل أيضًا مع القيم الفردية، والصفوف، ومجموعات أخرى. بشكل افتراضي، يتم اعتبار بعض أنواع المعاملات فقط كقيم فردية، بما في ذلك (ولكن لا تقتصر على) NumberStringSymbolTypeFunctions وبعض الكائنات الفردية الشائعة مثل 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 بخلاف تلك المطلوبة لجميع AbstractArrays؛ تم تنفيذ معظم مكتبة المصفوفات بطريقة عامة تسمح لجميع المصفوفات المخصصة بالتصرف بشكل مشابه.

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.