Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الـ Pointers كقيم في الـ Maps

جزء من قسم المنطق وتدفق التنفيذ في رحلة GO على Coddy. الدرس 27 من 68.

رغم أن تخزين البُنى مباشرةً في الخرائط مفيد، فإن تخزين مؤشرات إلى البُنى يتيح قدرة قوية: إمكانية تعديل بيانات البنية الأصلية مباشرةً من خلال الخريطة. ويُعد هذا الأسلوب ضروريًا عندما تحتاج إلى تحديث بياناتك بعد تخزينها.

عندما تخزّن مؤشرًا إلى بنية كقيمة في map، فأنت تخزّن عنوان الذاكرة الخاص بالبنية، وليس نسخةً منها. إليك كيفية التصريح عن مثل هذا map والعمل معه:

type Product struct {
    Price    float64
    Quantity int
}

products := map[string]*Product{
    "laptop": &Product{Price: 999.99, Quantity: 5},
    "mouse":  &Product{Price: 25.50, Quantity: 20},
}

تتضح الميزة الأساسية عندما تحتاج إلى تعديل البيانات. نظرًا لأنك تعمل باستخدام المؤشرات، فإن التغييرات التي تُجرى من خلال map تؤثر في البنية الأصلية:

// حدّث سعر الـ laptop مباشرة من خلال الـ map
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // يطبع: 899.99

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

challenge icon

التحدي

سهل

أنشئ نظامًا بسيطًا لإدارة Inventory يوضّح قوة استخدام المؤشرات إلى البُنى كقيم في map. يوضّح هذا التحدّي كيف تتيح الخرائط المعتمدة على المؤشرات تعديل البيانات المخزّنة مباشرةً، مما يجعل نظام Inventory فعّالًا وسريع الاستجابة للتحديثات.

ستتلقى ثلاثة مُدخلات:

  • سلسلة تمثّل عدد المنتجات (مثلًا، "4")
  • سلسلة تحتوي على بيانات المنتجات بالتنسيق: "name1:price1:quantity1,name2:price2:quantity2,name3:price3:quantity3" (مثلًا، "laptop:999.99:5,mouse:25.50:20,keyboard:75.00:15,monitor:299.99:8")
  • سلسلة تحتوي على عمليات التحديث بالتنسيق: "operation1:product1:value1,operation2:product2:value2" حيث تكون العمليات إما "price" أو "quantity" (مثلًا، "price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00")

مهمتك هي:

  1. تعريف بُنية Product بالحقول التالية:
    • Price (float64) - سعر المنتج
    • Quantity (int) - كمية المنتج المتوفرة في المخزون
  2. إنشاء map تكون مفاتيحها أسماء المنتجات (سلاسل نصية)، وقيمها مؤشرات إلى بُنى Product
  3. تحليل بيانات المنتجات من المُدخل الثاني:
    • قسّم المُدخل باستخدام الفواصل للحصول على كل product entries على حدة
    • لكل entry، قسّمه باستخدام النقطتين لفصل الاسم والسعر والكمية
    • حوّل سلسلة السعر إلى float64 وسلسلة الكمية إلى عدد صحيح
    • أنشئ بُنية Product وخزّن مؤشرًا إليها في map
  4. احتفظ بأسماء المنتجات في slice منفصلة أثناء التحليل، بالترتيب الذي تظهر به في المُدخل. يؤدي التكرار على map في Go إلى زيارة المفاتيح بترتيب عشوائي، ولذلك فإن slice هي التي تحافظ على ظهور كل عنصر بالترتيب الموجود في المُدخل.
  5. اعرض Inventory الأولي مع العنوان "Initial Inventory:"
  6. اعرض كل منتج بالتنسيق: "[name]: $[price] (Stock: [quantity])"
  7. حلّل عمليات التحديث من المُدخل الثالث وطبّقها:
    • قسّم المُدخل باستخدام الفواصل للحصول على كل operation على حدة
    • لكل operation، قسّمه باستخدام النقطتين للحصول على نوع العملية واسم المنتج والقيمة الجديدة
    • حدّث field المناسب مباشرةً من خلال مؤشر map
    • اطبع كل تحديث بالتنسيق: "Updated [product]: [field] changed to [new_value]"
  8. اعرض Inventory المحدّث مع العنوان "Updated Inventory:"
  9. اعرض كل منتج مرة أخرى بالتنسيق نفسه المستخدم في العرض الأولي
  10. احسب واعرض القيمة الإجمالية لـ Inventory: "Total Inventory Value: $[total_value]"

استخدم حزمة strconv لتحويل سلاسل السعر والكمية إلى أنواعهما الرقمية المناسبة. نسّق جميع الأسعار إلى منزلتين عشريتين عند عرضها. يوضّح هذا التحدّي كيف يتيح تخزين المؤشرات في map تعديل بيانات البُنية الأصلية مباشرةً، مما يجعل تحديثات Inventory فورية ودائمة دون الحاجة إلى إعادة إسناد القيم إلى map.

جرّب بنفسك

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// قراءة المدخلات
	var numProductsStr string
	var productDataStr string
	var operationsStr string
	
	fmt.Scanln(&numProductsStr)
	fmt.Scanln(&productDataStr)
	fmt.Scanln(&operationsStr)
	
	// TODO: عرّف هيكل Product هنا
	
	// TODO: أنشئ map لتخزين مؤشرات المنتجات
	
	// TODO: أنشئ slice للحفاظ على ترتيب المنتجات (لضمان مخرجات متسقة)
	
	// TODO: حلّل بيانات المنتجات واملأ الـ map
	// تذكّر أن:
	// - قسّم productDataStr بالفواصل للحصول على إدخالات فردية
	// - لكل إدخال، قسّم بالنقطتين الرأسيتين للحصول على name و price و quantity
	// - حوّل سلاسل price و quantity إلى الأنواع المناسبة
	// - خزّن مؤشراً إلى هيكل Product في الـ map
	// - أضف اسم المنتج إلى order slice
	
	// TODO: Display initial inventory
	// استخدم order slice للتكرار عبر المنتجات بشكل متسق
	// الصيغة: "[name]: $[price] (Stock: [quantity])"
	
	// TODO: حلّل وطبّق عمليات التحديث
	// تذكّر أن:
	// - قسّم operationsStr بالفواصل للحصول على العمليات الفردية
	// - لكل عملية، قسّم بالنقطتين للحصول على type و name و value
	// - حدّث الحقل المناسب مباشرة عبر مؤشر الـ map
	// - اطبع رسالة التحديث لكل عملية
	
	// TODO: Display updated inventory
	// مرة أخرى، استخدم شريحة order لمخرجات متسقة
	// احسب القيمة الإجمالية أثناء العرض
	
	// TODO: Calculate and display total inventory value
	// Format: "Total Inventory Value: $[total_value]"
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس المنطق وتدفق التنفيذ

تدرّب بنفسك: مترجم Go عبر الإنترنت