الرئيسية قاعدة المعرفة البرمجة والمنطق المجموعات والمكررات في Rust: معالجة بيانات خط الإنتاج بكفاءة
البرمجة والمنطق

المجموعات والمكررات في Rust: معالجة بيانات خط الإنتاج بكفاءة

Vec: المصفوفة الديناميكية

Vec هو أكثر أنواع المجموعات استخداماً في Rust. يُخزّن عناصر من نوع واحد في ذاكرة متجاورة. في المصانع، نستخدمه لتخزين قراءات المستشعرات المتتالية.

// إنشاء مصفوفة لتخزين قراءات درجة حرارة الفرن
let mut readings: Vec<f64> = Vec::new();

// إضافة قراءات جديدة
readings.push(72.5);
readings.push(73.1);
readings.push(71.8);

// الوصول بالفهرس
let first = readings[0]; // 72.5

// الوصول الآمن - يُرجع Option
let maybe = readings.get(10); // None بدل انهيار البرنامج

// حذف آخر عنصر
let last = readings.pop(); // Some(71.8)

// إنشاء سريع بالماكرو
let alarms = vec![false, false, true, false];

println!("عدد القراءات: {}", readings.len());

HashMap<K, V>: تخزين المفتاح والقيمة

HashMap يربط مفتاحاً بقيمة، مثل ربط معرّف المستشعر بآخر قراءة له.

use std::collections::HashMap;

// ربط معرّف المستشعر بآخر قراءة
let mut last_reading: HashMap<String, f64> = HashMap::new();

last_reading.insert("TEMP-01".to_string(), 72.5);
last_reading.insert("PRESS-03".to_string(), 1.2);
last_reading.insert("HUMID-07".to_string(), 45.0);

// البحث عن قراءة مستشعر
if let Some(temp) = last_reading.get("TEMP-01") {
    println!("درجة الحرارة: {temp}");
}

// التحديث فقط إذا لم يكن موجوداً
last_reading.entry("TEMP-01".to_string()).or_insert(0.0);

// تعديل قيمة موجودة
last_reading
    .entry("TEMP-01".to_string())
    .and_modify(|v| *v = 74.0);

// المرور على جميع العناصر
for (sensor_id, value) in &last_reading {
    println!("{sensor_id} => {value}");
}

مجموعات أخرى: VecDeque و BTreeMap و HashSet

Rust يوفّر مجموعات متخصصة لحالات مختلفة:

use std::collections::{VecDeque, BTreeMap, HashSet};

// VecDeque: طابور ذو طرفين - مثالي لسجل الأحداث الدائري
let mut event_log: VecDeque<String> = VecDeque::with_capacity(100);
event_log.push_back("بدء التشغيل".to_string());
event_log.push_back("قراءة مستشعر".to_string());
// حذف الأقدم عند الامتلاء
if event_log.len() >= 100 {
    event_log.pop_front();
}

// BTreeMap: خريطة مرتّبة - مفيدة لترتيب المستشعرات أبجدياً
let mut sorted_sensors: BTreeMap<String, f64> = BTreeMap::new();
sorted_sensors.insert("A-TEMP".to_string(), 70.0);
sorted_sensors.insert("B-PRESS".to_string(), 1.1);
// العناصر دائماً مرتبة بالمفتاح

// HashSet: مجموعة بدون تكرار - لتتبع الأجهزة النشطة
let mut active_machines: HashSet<u32> = HashSet::new();
active_machines.insert(101);
active_machines.insert(102);
active_machines.insert(101); // لا يُضاف مرة ثانية
println!("أجهزة نشطة: {}", active_machines.len()); // 2

المكررات: خط معالجة كسول

المكررات في Rust تشبه خط الإنتاج: كل خطوة تعالج عنصراً واحداً فقط عند الطلب. لا يتم حساب أي شيء حتى نستدعي عملية جمع مثل collect().

let readings = vec![72.5, 73.1, 71.8, 85.0, 69.2];

// .iter() - ينشئ مكرراً على المرجعيات
// .map() - يحوّل كل عنصر
// .filter() - يحتفظ بالعناصر التي تحقق الشرط
// .collect() - يجمع النتائج في مجموعة

// تحويل من سلسيوس إلى فهرنهايت
let fahrenheit: Vec<f64> = readings
    .iter()
    .map(|c| c * 9.0 / 5.0 + 32.0)
    .collect();

// عدّ القراءات المرتفعة
let high_count = readings
    .iter()
    .filter(|&&r| r > 75.0)
    .count();

println!("قراءات مرتفعة: {high_count}");

سلسلة محولات المكررات

القوة الحقيقية تظهر عند سلسلة عدة عمليات معاً:

struct SensorReading {
    sensor_id: String,
    value: f64,
    is_valid: bool,
}

let readings = vec![
    SensorReading { sensor_id: "T-01".into(), value: 72.5, is_valid: true },
    SensorReading { sensor_id: "T-02".into(), value: -999.0, is_valid: false },
    SensorReading { sensor_id: "T-03".into(), value: 85.3, is_valid: true },
    SensorReading { sensor_id: "T-04".into(), value: 71.0, is_valid: true },
];

// سلسلة كاملة: تصفية ثم تحويل ثم جمع
let valid_labels: Vec<String> = readings
    .iter()
    .filter(|r| r.is_valid)             // فقط القراءات الصالحة
    .filter(|r| r.value > 72.0)         // فقط المرتفعة
    .map(|r| format!("{}: {:.1}°", r.sensor_id, r.value))
    .collect();
// ["T-01: 72.5°", "T-03: 85.3°"]

// إيجاد أعلى قيمة صالحة
let max_valid = readings
    .iter()
    .filter(|r| r.is_valid)
    .map(|r| r.value)
    .fold(f64::NEG_INFINITY, f64::max);

println!("أعلى قراءة صالحة: {max_valid}");

مثال عملي: تحليل دفعة قراءات إنتاجية

/// تحليل دفعة من قراءات خط الإنتاج:
/// حساب المتوسط، إيجاد القيم الشاذة، تصنيف الحالة
fn analyze_batch(readings: &[f64], threshold: f64) {
    let count = readings.len() as f64;

    // حساب المتوسط
    let avg = readings.iter().sum::<f64>() / count;

    // إيجاد القيم الشاذة (بعيدة عن المتوسط بأكثر من الحد)
    let outliers: Vec<(usize, f64)> = readings
        .iter()
        .enumerate()
        .filter(|(_, &val)| (val - avg).abs() > threshold)
        .map(|(i, &val)| (i, val))
        .collect();

    // أدنى وأعلى قيمة
    let min = readings.iter().cloned().fold(f64::INFINITY, f64::min);
    let max = readings.iter().cloned().fold(f64::NEG_INFINITY, f64::max);

    println!("--- تقرير الدفعة ---");
    println!("العدد: {} | المتوسط: {:.2}", readings.len(), avg);
    println!("الأدنى: {:.2} | الأعلى: {:.2}", min, max);
    println!("قيم شاذة: {}", outliers.len());
    for (idx, val) in &outliers {
        println!("  القراءة [{}] = {:.2} (انحراف: {:.2})", idx, val, val - avg);
    }
}

fn main() {
    let batch = vec![72.5, 73.1, 71.8, 85.0, 69.2, 72.0, 73.5, 90.1, 71.5];
    analyze_batch(&batch, 5.0);
}

الخلاصة

  • Vec هو المجموعة الأساسية لتخزين سلاسل القراءات المتتالية
  • HashMap مثالي لربط معرّفات المستشعرات بقيمها الحالية
  • VecDeque لسجلات الأحداث الدائرية، BTreeMap للبيانات المرتّبة، HashSet لتتبع العناصر الفريدة
  • المكررات تعمل بالتقييم الكسول: لا حساب حتى استدعاء collect() أو count()
  • سلسلة المكررات تبني خط معالجة نظيف وفعّال بدون تخصيص ذاكرة وسيطة
  • في الدرس القادم سنتعلم كيف ننظّم هذا الكود في وحدات وحزم مستقلة
Vec HashMap iterators map filter collect المتجهات خريطة التجزئة المكررات التصفية التحويل معالجة البيانات