المتجهات

في Scheme، المتجه بنية بيانات أساسية أخرى لتجميع القيم. على عكس القوائم، المتجهات مجموعات ذات حجم ثابت ومفهرسة، ما يوفر وصولًا عشوائيًا وتحديثات أسرع. يمكن أن يكون كل عنصر من أي نوع، بما في ذلك متجه آخر. تُمثَّل المتجهات بـ # متبوعًا بأقواس، مثل #(1 2 3).

رغم تشابه المتجهات والقوائم، تخدم أغراضًا مختلفة في برمجة Scheme:

  • تُستخدم القوائم أكثر للعمليات العودية والهياكل الديناميكية، حيث يتيح تنفيذها المرتبط معالجة فعالة للبدايات والمرور عبر التفكيك العودي.

  • أما المتجهات فمُحسَّنة للسيناريوهات التي تتطلب وصولًا عشوائيًا أو تحديثات عند فهارس محددة، ما يجعلها أنسب لجداول البحث والتكوينات ذات الحجم الثابت والعمليات المفهرسة الحساسة للأداء.

في جوهرها، القوائم الخيار الطبيعي للخوارزميات العودية والبيانات ذات الحجم الديناميكي، بينما تتألق المتجهات عندما تكون أنماط الوصول ذات الحجم الثابت أو المفهرسة حاسمة.

متجه بسيط

(vector 1 2 3)
  • ينشئ متجهًا من ثلاثة عناصر: 1، 2، و3.

النتيجة: #(1 2 3)

الوصول إلى عناصر المتجه

يُصل إلى عناصر المتجه بـ vector-ref، الذي يسترد العنصر عند فهرس محدد (يبدأ من 0).

(define my-vector (vector 1 2 3))
(vector-ref my-vector 0)  ; يسترد العنصر عند الفهرس 0
(vector-ref my-vector 1)  ; يسترد العنصر عند الفهرس 1

التكرار: معالجة كل عنصر في متجه

يمكن المرور عبر متجه بحلقة أو عودية. يوفر Scheme vector-length لتحديد حجم المتجه. حلقة بسيطة لطباعة كل عنصر:

(define (print-elements vec)
  (let loop ((i 0))
    (if (< i (vector-length vec))
      (begin
        (lumi-message (number->string (vector-ref vec i))) ; طباعة العنصر
        (loop (+ i 1)))                                    ; معالجة الفهرس التالي
      (lumi-message "done"))))                             ; نهاية الحلقة
  • الحالة الأساسية: إذا وصل الفهرس i إلى طول المتجه، أوقف الحلقة.
  • الحالة العودية: اطبع العنصر عند i، ثم زد i.

مثال للاستخدام

(print-elements (vector 1 2 3))

النتيجة:

  • "1"
  • "2"
  • "3"

النتيجة: "done"

متجهات مختلطة

يمكن أن تتضمن المتجهات عناصر من أنواع مختلفة، بما في ذلك سلاسل أو قيم منطقية أو أرقام أو متجهات أخرى أو نتيجة تعبيرات:

(vector 42 "hello" #t (vector 1 2) (+ 3 4))

ينشئ متجهًا يحتوي على:

  • رقم (42)
  • سلسلة ("hello")
  • قيمة منطقية (#t)
  • متجه آخر (#(1 2))
  • نتيجة التعبير ((+ 3 4)، تُقيَّم إلى 7)

النتيجة: #(42 "hello" #t #(1 2) 7)

بناء المتجهات

تُنشأ المتجهات بـ vector، أو بـ make-vector لمتجه بحجم ثابت وقيمة أولية.

(make-vector 5 0)

ينشئ متجهًا بالحجم 5 بتهيئة جميع العناصر إلى 0.

النتيجة: #(0 0 0 0 0)

تحديث المتجهات

يحدّث vector-set! عنصرًا في متجه عند فهرس محدد.

(define my-vector (vector 1 2 3))
(vector-set! my-vector 1 42)  ; يعيّن العنصر الثاني إلى 42
my-vector

النتيجة: #(1 42 3)

التحقق من المتجهات

يتحقق vector? مما إذا كانت القيمة المحددة متجهًا.

(vector? (vector 1 2 3))  ; يتحقق مما إذا كان #(1 2 3) متجهًا
(vector? 42)              ; يتحقق مما إذا كان 42 متجهًا

النتيجة:

  • (vector? (vector 1 2 3)) تُرجع #t (صحيح)
  • (vector? 42) تُرجع #f (خطأ)

المتجهات وسلوك التمرير بالمرجع

في Scheme، المتجهات قابلة للتغيير وتُمرَّر بالمرجع. عند تمرير متجه إلى دالة، يمكن للدالة تعديل المتجه الأصلي مباشرة. أي تغيير داخل الدالة ينعكس خارجها أيضًا. هذا مفيد لمشاركة البيانات وتحديثها بكفاءة عبر دوال متعددة، لكنه يتطلب الحذر لتجنّب آثار جانبية غير مقصودة.

مثال: تعديل متجه في دالة

(define (modify-vector vec index new-value)
  (vector-set! vec index new-value))  ; يحدّث المتجه عند الفهرس المحدد

(define my-vector (vector 10 20 30))
(modify-vector my-vector 1 99)         ; يعدّل العنصر الثاني إلى 99
my-vector                              ; المتجه الأصلي محدّث الآن

النتيجة: #(10 99 30)

شرح خطوة بخطوة

  1. إنشاء متجه: يُهيَّأ my-vector بالقيم 10، 20، و30.
  2. التمرير إلى دالة: يُمرَّر my-vector إلى modify-vector مع الفهرس والقيمة الجديدة.
  3. التعديل في الدالة: vector-set! يحدّث القيمة عند الفهرس مباشرة في المتجه الأصلي.
  4. انعكاس التغييرات: بما أن المتجهات تُمرَّر بالمرجع، تنعكس التغييرات في المتجه الأصلي.

آثار التمرير بالمرجع

  • الأداء: تمرير المتجهات بالمرجع فعّال لأنه يتجنّب نسخ هياكل كبيرة.
  • الآثار الجانبية: كن حذرًا عند مشاركة المتجهات عبر الدوال لتجنّب تعديلات غير مقصودة.

العمليات على المتجهات

يوفر Scheme عدة إجراءات مضمّنة للعمل مع المتجهات، بما في ذلك:

  • vector-length: إرجاع عدد العناصر.
  • vector->list: تحويل متجه إلى قائمة.
  • list->vector: تحويل قائمة إلى متجه.
(vector-length (vector 1 2 3))         ; يرجع 3
(vector->list (vector 1 2 3))          ; يحوّل المتجه إلى قائمة: (1 2 3)
(list->vector (list 1 2 3))            ; يحوّل القائمة إلى متجه: #(1 2 3)

النتيجة:

  • (vector-length (vector 1 2 3)) تُرجع 3
  • (vector->list (vector 1 2 3)) تُرجع (1 2 3)
  • (list->vector (list 1 2 3)) تُرجع #(1 2 3)

متجهات متداخلة

يمكن أن تحتوي المتجهات في Scheme على متجهات أخرى كعناصر، ما يُنشئ بنية متداخلة.

(define nested-vector (vector (vector 1 2) (vector 3 4) (vector 5)))

ينشئ متجهًا من ثلاثة عناصر، كل منها متجه.

النتيجة: #(#(1 2) #(3 4) #(5))

الوصول إلى البيانات المتداخلة

للوصول إلى عناصر داخل متجه متداخل، استخدم vector-ref عدة مرات للتنقل في البنية.

مثال: الوصول إلى العناصر

(vector-ref nested-vector 0)              ; يسترد العنصر الأول: #(1 2)
(vector-ref (vector-ref nested-vector 0) 1) ; يسترد العنصر الثاني من المتجه الأول: 2

ملخص

  • المتجهات في Scheme هياكل بيانات مفهرسة ذات حجم ثابت.
  • استخدم vector للإنشاء، وvector-ref للوصول، وvector-set! للتحديث.
  • إجراءات مثل vector-length وvector->list وlist->vector تمكّن عمليات مرنة.
  • المتجهات المتداخلة تسمح بهياكل بيانات معقدة وهرمية.