ال

الصفوف في Julia

1 تمرين

نبذة عن الصفوف

يشبه Tuple إلى حد بعيد Vector (مصفوفة أحادية البعد array) في نواحٍ كثيرة. لكن توجد بعض الفروق المهمة.

  • تُكتب الـtuples بين الأقواس الهلالية ( ) بدلًا من الأقواس المربعة [ ]، وإن كان يمكن حذف الأقواس الهلالية حين لا يكون المعنى ملتبسًا.
  • لا يُشترط في الـtuples أن تكون متجانسة: إذ يُخزَّن نوع كل عنصر على حدة.
  • الـtuples غير قابلة للتغيير.

الثبات يتيح مزيدًا من تحسينات المترجِم، ويجعل الـtuples أعلى أداءً من المصفوفات، لذا يُفضَّل استخدامها حين لا تكون القابلية للتغيير ضرورية.

julia> t = (3, 5.2, "xyz")
(3, 5.2, "xyz")

# Non-homogeneous:
julia> dump(t)
Tuple{Int64, Float64, String}
  1: Int64 3
  2: Float64 5.2
  3: String "xyz"

julia> t[2]
5.2

# Immutable:
julia> t[2] = 7.3
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Float64, String}

يوجد أيضًا مُنشئ Tuple() يحوّل أي مجموعة قابلة للتكرار إلى tuple. ومن المفيد أنه يعامل السلسلة النصية كمجموعة قابلة للتكرار من المحارف.

julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')

وبالنسبة إلى أي tuple، يمكن الوصول إلى العناصر عبر الفهرس أو المرور عليها بحلقة.

ويمكن أيضًا استخدام أي tuple رقمي بحت في الدوال الرياضية مثل sum()، تمامًا مثل المصفوفات.

التفكيك

يمكن تفكيك tuple إلى عناصر فردية باستخدام الإسناد المتعدد. وللعناصر غير المطلوبة، استخدم الشرطة السفلية _ كمتغير وهمي.

julia> tup = (1, "Julia", true)
(1, "Julia", true)

julia> num, name, _ = tup
(1, "Julia", true)

julia> uppercase(name), 2num
("JULIA", 2)

لنسخ كل عناصر tuple إلى tuple آخر (أو إلى مجموعة على وجه العموم)، يوجد عامل splat ... لتفكيكه. وسنغطي هذا بمزيد من التفصيل في مفهوم Functions.

julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')

الـtuples المسمّاة

لا يحتوي Tuple إلا على القيم، أما NamedTuple فيقرن كل قيمة باسم.

ويمكن بعد ذلك الوصول إلى الحقول الفردية باستخدام ترميز "النقطة":

julia> nt = (a = 1, b = 7.3)
(a = 1, b = 7.3)

julia> dump(nt)
@NamedTuple{a::Int64, b::Float64}
  a: Int64 1
  b: Float64 7.3

julia> nt.b
7.3

يُخزَّن الاسم داخليًا على هيئة Symbol (وهو نوع غير قابل للتغيير، ويُسبق الاسم بنقطتين رأسيتين :)، لذا توجد صيغة بديلة:

julia> nt[:b]
7.3

قد يفيد أحيانًا تقسيم إنشاء الـtuples المسمّاة إلى خطوتين:

  1. عرّف الحقول كنوع بيانات مخصص.
  2. استخدمه كمُنشئ لتحويل tuple إلى tuple مسمّى.
# Step 1
julia> Student = NamedTuple{(:name, :grade), Tuple{String, Int}}
@NamedTuple{name::String, grade::Int64}

julia> typeof(Student)
DataType

# Step 2
julia> s = Student( ("Helen", 3) )
(name = "Helen", grade = 3)

julia> dump(s)
@NamedTuple{name::String, grade::Int64}
  name: String "Helen"
  grade: Int64 3

توجد، بشكل مفاجئ، طرق أخرى كثيرة لإنشاء tuple مسمّى، فراجع التوثيق إذا كنت مهتمًا.

ومن السهل الحصول على الأسماء (وتسمى أيضًا "المفاتيح") أو على القيم بشكل منفصل:

julia> keys(s)
(:name, :grade)

julia> values(s)
("Helen", 3)

لاحظ أن التكرار على tuple مسمّى لا ينتج إلا القيم، أما الأسماء فيجري تجاهلها.

وإذا كانت الأسماء مفيدة أيضًا في التكرار، فستحتاج إلى مزيد من الخطوات. يمكن تحويل NamedTuple إلى Vector من Pairs (وPairs هي موضوع مفهوم آخر).

julia> pairs(s)
pairs(::NamedTuple) with 2 entries:
  :name  => "Helen"
  :grade => 3

julia> collect(pairs(s))
2-element Vector{Pair{Symbol, Any}}:
  :name => "Helen"
  :grade => 3

وبذلك يشبه الـtuple المسمّى القاموس كثيرًا. لكنه يختلف عن [Dict][dict] في Julia في نواحٍ مهمة:

  • Dict قابل للتغيير، أما NamedTuple، مثل أي tuple، فغير قابل للتغيير.
  • Dict مرن في أنواع المفاتيح التي يقبلها، أما NamedTuple فلا يمكنه استخدام إلا الرموز كمفاتيح.

تُعد الـtuples، مسمّاة كانت أم لا، وسيلة شائعة جدًا لنقل البيانات المهيكلة من الدوال وإليها. والثبات ميزة في هذه الحالة، إذ يوفّر أمان البيانات ويتيح للمترجِم القيام بتحسينات أكثر جرأة.

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم الصفوف