לדלג לתוכן

2.5 - אלגוריתמים טווחים ותצוגות - פתרון

פתרון - מממשים לפני שהמקור נעלם

הקוד הבא מחזיר תוצאה עצמאית:

#include <cassert>
#include <ranges>
#include <span>
#include <vector>

std::vector<int> positive_doubles(std::span<const int> input) {
    auto selected = input
        | std::views::filter([](int n) { return n > 0; })
        | std::views::transform([](int n) { return n * 2; });

    std::vector<int> output;
    for (int value : selected) output.push_back(value);
    return output;
}

int main() {
    std::vector<int> result;
    {
        const int input[]{-1, 2, 0, 5};
        result = positive_doubles(input);
    }
    assert((result == std::vector<int>{4, 10}));
}

ה־input המקומי ב־main נעלם לפני הבדיקה האחרונה, אבל result מחזיקה את הערכים שלה. הפעולה אינה מחזירה את selected, ולכן אינה משאירה תצוגה אל אחסון מקומי.

הקוד משתמש בקלט קטן. לממשק כללי צריך לבדוק גבול לפני כפל signed או לבחור חוזה שמגביל קלט, בדומה למה שנלמד בפרק הראשון.

תוצאה שתלויה בקורא

אפשר להחזיר view מתוך span שהתקבל:

auto positive_view(std::span<const int> input) {
    return input | std::views::filter([](int n) { return n > 0; });
}

ה־span עצמו מועתק כחלק מהתיאור, אבל הנתונים אינם מועתקים. הקורא צריך לשמור את המערך או המכולה המקוריים בחיים, ולהימנע מפעולות שמפנות או מזיזות את האחסון בזמן המעבר.

הפונקציה בטוחה רק לפי החוזה הזה. עצם העובדה שהפרמטר span הוא קטן וניתן להעתקה אינה מוכיחה שמשך חיי הנתונים מתאים.

יציבות וסוף טווח

האלגוריתם ranges::sort אינו מבטיח יציבות של איברים שקולים. אם רוצים להבטיח את הסדר היחסי, אפשר לבחור stable_sort מתאים או להגדיר שובר שוויון באמצעות id, בהתאם למשמעות הרצויה.

בחיפוש על vector זמני, המכולה עצמה בעלת האחסון עומדת להיעלם בסוף הביטוי, ולכן iterator מוחזר אינו יכול לשמש לאחר מכן. אובייקט span זמני הוא רק תיאור; היעלמותו אינה משמידה את הנתונים. עדיין נדרשת הוכחה שהבעלים האמיתי חי.

ההבחנה מלמדת למה borrowed_range הוא כלי לתיאור חוזים מסוימים ולא מנגנון שמאריך חיי כל מה שנגיש דרך טווח.