من الأهداف الأساسية للغة جوليا القدرة على إجراء عمليات حسابية على البيانات العددية بسرعة وكفاءة. وعلى كميات هائلة من البيانات العددية.
عادةً ما تُخزَّن هذه البيانات في المصفوفات (أو في نوع مرتبط بها)، لذا من المنطقي القول إن المصفوفات هي قلب لغة جوليا.
يمكن أن تكون المصفوفات بأي حجم (رهنًا بقيود الذاكرة في عتادك فقط)، ويمكن أن تمتلك أي عدد من الأبعاد.
لبعض أنواع المصفوفات أسماء خاصة (مأخوذة من الجبر الخطي).
فالمصفوفة أحادية البعد تُسمى Vector، والمصفوفة ثنائية الأبعاد تُسمى Matrix.
وكلاهما نوع فرعي من Array في جوليا.
يركّز هذا المفهوم التمهيدي على أساسيات المتجهات. وسنغطي المصفوفات متعددة الأبعاد لاحقًا في المنهج.
في جوليا، المتجه متتالية مرتبة من القيم يمكن الوصول إلى كل منها عبر الفهرس.
أبسط طريقة لإنشاء متجه هي سرد القيم بين الأقواس المربعة:
julia> num_vec = [1, 4, 9]
3-element Vector{Int64}:
1
4
9
julia> str_vec = ["arrays", "are", "important"]
3-element Vector{String}:
"arrays"
"are"
"important"
يتوافق نوع المتجه مع نوع كل عنصر فيه.
من الناحية التقنية، هو متجه عمودي، لكن تجاهل ذلك الآن إن لم تكن على دراية بالجبر الخطي. وسيتضح الأمر أكثر بعد مفهوم المصفوفات متعددة الأبعاد.
إذن يجب أن تكون المتجهات متجانسة (أن تكون كل العناصر من النوع نفسه)؟
إلى حدٍّ ما، نعم، ويُستحسن أن تسعى إلى ذلك إن أردت أفضل أداء.
غير أن جوليا تستطيع التحايل على هذا القيد (عند الحاجة) باستخدام النوع Any:
julia> mixed_vector = [1, "str", 'c']
3-element Vector{Any}:
1
"str"
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
عند إنشاء متجه فارغ، تجعل جوليا نوع العنصر الافتراضي Any، لكن يمكن تحديده بشكل أدق بتعريف نوع العنصر المطلوب على المصفوفة الفارغة.
julia> empty_vec = []
Any[]
julia> empty_int_vec = Int64[]
Int64[]
من الشائع جدًا أن تبدأ من متجهات قيمها كلها أصفار أو كلها آحاد. ولهذا الغرض توجد دالتان تُسمّيان (كما هو متوقع) zeros() وones()، وتأخذان حجم المتجه معاملًا.
والنوع الافتراضي هو Float64، لكن يمكن اختياريًا تحديد أنواع عددية أخرى.
وعلى المنوال نفسه، تأخذ الدالة fill() قيمةً تُكرَّر وحجم المتجه المطلوب.
julia> zeros(3)
3-element Vector{Float64}:
0.0
0.0
0.0
julia> ones(3)
3-element Vector{Float64}:
1.0
1.0
1.0
julia> ones(Int64, 3)
3-element Vector{Int64}:
1
1
1
julia> fill(42, 3)
3-element Vector{Int64}:
42
42
42
هناك خيارات أخرى كثيرة: راجع الدليل.
اقرأ هذه القاعدة وتذكّرها:
هذا مألوف لكل من استخدم لغات أخرى لعلوم البيانات (R وMatlab وFortran...)، لكنه قد يبدو صادمًا لعلماء الحاسوب ذوي الخلفية في اللغات الشبيهة بـ C.
بخلاف ذلك، تعمل الفهارس كما قد تتوقع: ضعها فقط بين الأقواس المربعة:
squares = [0, 1, 4, 9, 16]
squares[1] # 0
squares[3] # 4
squares[begin] # 0 ("begin" is synonym for 1)
squares[end] # 16 ("end" is synonym for length(squares))
لاحظ سهولة الفهرسة باستخدام end (وهو مفيد جدًا) وbegin (وهو على الأرجح ليس كذلك).
قد يتساءل مبرمجو Python عن الفهارس السالبة.
لا تفعل ذلك: فهي ليست جزءًا من جوليا، وستؤدي إلى رفع BoundsError، وكذلك أي فهرس أصغر من 1 أو أكبر من length(squares).
المتجهات في جوليا قابلة للتعديل: يمكننا تغيير محتوى الخلايا الفردية.
julia> vals = [1, 3, 5, 7]
4-element Vector{Int64}:
1
3
5
7
julia> vals[2] = 4 # reassign the value of this element
4
# Only the value at position 2 changes:
julia> vals
4-element Vector{Int64}:
1
4
5
7
هناك دوال كثيرة متاحة للتعامل مع المتجهات، وإن كانت وثائق جوليا تعمّمها على «المجموعات» بدلًا من المتجهات.
لاحظ أن الدوال التي تُعدّل مدخلاتها تحمل، بحسب العُرف، علامة ! في اسمها.
لن يفرض المترجم ذلك، لكنه عُرف قوي جدًا في عالم جوليا.
وفيما يلي بعض الدوال المفيدة:
push!().pop!().pushfirst!() وpopfirst!().insert!() وdeleteat!().julia> vals = [1, 3]
2-element Vector{Int64}:
1
3
julia> push!(vals, 5, 6) # can add multiple values
4-element Vector{Int64}:
1
3
5
6
julia> pop!(vals) # mutates vals, return popped value
6
julia> vals
3-element Vector{Int64}:
1
3
5
الدمج بسيط إلى حد كبير بالنسبة للمتجهات، وإن كانت نظرة سريعة إلى الدليل تنبّه إلى تعقيدات تخبّئها المصفوفات متعددة الأبعاد.
يمكن للدالة append!() أن تأخذ مزيجًا من عدة متجهات وعناصر مفردة كمدخل.
julia> append!([1, 2], [3, 4], [-1, -2], 15)
7-element Vector{Int64}:
1
2
3
4
-1
-2
15