نسخه قابل چاپ

بخش ۱۷ - کتابخانه‌ی STL

در این بخش مثال‌هایی از استفاده از کتابخانه‌ی استاندارد STL مورد بررسی قرار می‌گیرند.

فهرست مثال‌ها

نوشتن عناصر بردار با تکرارکننده

در این مثال، عناصر یک بردار با استفاده از تکرارکننده پیمایش می‌شوند.

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v;
    v.push_back(10);
    v.push_back(123);
    v.push_back(87);

    vector<int>::iterator it = v.begin();
    while (it != v.end()) {
        cout << *it << ' ';
        ++it;
    }
    cout << endl;

    for (vector<int>::iterator it2 = v.begin(); it2 != v.end(); ++it2)
        cout << *it2 << ' ';
    cout << endl;
}
					
تابع find

در این مثال، جستجو به دنبال یک عنصر در یک بردار توسط تابع find که در کتابخانه‌ی STL تعریف شده صورت می‌گیرد. همین تابع را برای یافتن یک کلمه در یک لیست پیوندی از رشته‌ها و یک عدد اعشاری در آرایه‌ای از اعداد نیز به‌کار خواهیم برد.

#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <algorithm>
using namespace std;

/*
template<typename I, typename T>
I find(I first, I last, const T& val) {
    while (first!=last && *first != val) 
        ++first;
    return first; 
}
*/

int main() {
    vector<int> v;
    v.push_back(10);
    v.push_back(123);
    v.push_back(87);

    vector<int>::iterator p = find(v.begin(), v.end(), 123);
    if (p != v.end())
        cout << "Found " << *p << endl;
    else
        cout << "Not found" << endl;

    list<string> words;
    words.push_back("Money");
    words.push_back("For");
    words.push_back("Nothing");

    list<string>::iterator q = find(words.begin(), words.end(), "Chicks");
    if (q != words.end())
        cout << "Found " << *q << endl;
    else
        cout << "Not found" << endl;

    double nums[] = {1.4, 50.1, 3.5};
    double *r = find(nums, nums + sizeof(nums)/sizeof(double), 50.1);
    if (r != nums + sizeof(nums)/sizeof(double))
        cout << "Found " << *r << endl;
    else
        cout << "Not found" << endl;
}
					
الگوی لیست پیوندی با تکرارکننده‌ی به سبک STL

در این مثال لیست پیوندی که در بخش ۱۵ نوشته شد را به تکرارکننده‌ای به سبک STL مجهز می‌کنیم. به این ترتیب از این لیست در بسیاری از الگوریتم‌های STL می‌شود استفاده کرد.

#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <string>
using namespace std;

template<typename T>
class List {
private:
    //Definition of class Node is the same as before
    class Node {
    public:
        Node(T d, Node *n = NULL, Node *p = NULL)
            : data(d), next(n), prev(p) {}

        T data;
        Node *next;
        Node *prev;
    };

public:
    class Iterator : public iterator<input_iterator_tag, T> {
    private:
        Iterator(Node* n) { current = n; }
        friend class List;
    public:
        Iterator& operator++() { 
            if (current != NULL)
                current = current->next;
            return *this;
        }

        T& operator*() {
            return current->data;
        }

        bool operator==(const Iterator& it) {
            return current == it.current;
        }

        bool operator!=(const Iterator& it) {
            return current != it.current;
        }
    private:
        Node *current;
    };

    //Definition of List members are the same as before
public:
    List();
    ~List();
    void print();
    void push_front(T x);
    void push_back(T x);
    Iterator begin() {
        return Iterator(_head);
    }
    Iterator end() {
        return Iterator(NULL);
    }
private:
    Node* _head;
    Node* _last;
};

template<typename T>
List<T>::List() {
    _head = NULL;
    _last = NULL;
}

template<typename T>
void List<T>::print() {
    for (Node* p = _head; p != NULL; p = p->next) {
        cout << p->data << ' ';
    }
}

template<typename T>
void List<T>::push_front(T x) {
    Node *new_node = new Node(x);
    new_node->next = _head;
    if (_head != NULL)
        _head->prev = new_node;
    _head = new_node;
    if (_last == NULL)
        _last = new_node;
}

template<typename T>
void List<T>::push_back(T x) {
    if (_head == NULL)
        push_front(x);
    else {
        Node *new_node = new Node(x);
        new_node->prev = _last;
        _last->next = new_node;
        _last = new_node;
    }
}

template<typename T>
List<T>::~List() {
    Node *p = _head;
    while (p != NULL) {
        Node *q = p;
        p = p->next;
        delete q;
    }
    _head = NULL;
}

int main() {
    List<int> l;
    
    l.push_back(86);
    l.push_front(43);
    l.push_front(12);
    
    cout << accumulate(l.begin(), l.end(), 0) << endl;

    List<int>::Iterator it = find(l.begin(), l.end(), 43);
    if (it != l.end()) {
        cout << "found\n";
        *it = 56;
    } else
        cout << "not found\n";

    vector<string> v;
    v.push_back("gholi");
    v.push_back("aroosi");
    v.push_back("naraft!");

    cout << accumulate(v.begin(), v.end(), string("")) << endl;
    
    double dd[3] = {1.2, 3.4, 67485.2};
    cout << accumulate(dd, dd + 3, 0.0) << endl;
}
					
مسندها و شیءتابع‌ها در STL

این مثال نحوه‌ی استفاده از تابع find_if در کتابخانه‌ی STL را نشان می‌دهد. این تابع در یک مجموعه از عناصر به دنبال اولین عنصری می‌گردد که «شرط خاصی» را ارضاء کند. این شرط ‏(که مسند یا predicate نام دارد) نامیده می‌شود، می‌تواند اشاره‌گر به تابع یا اصطلاحاً شیءتابع (function object) باشد که به معنی کلاسی است که عملگر پرانتز را سربارگذاری کرده است.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

/*
template<class In, class Pred>
In find_if(In first, In last, Pred pred) {
    while (first!=last && !pred(*first)) 
        ++first;
    return first; 
}
*/

bool odd(int x) { return x % 2; }

class OddSelector {
public:
    bool operator()(int x) { return x % 2; }
};

int main() {
    vector<int> v;
    v.push_back(10);
    v.push_back(39);
    v.push_back(42);

    vector<int>::iterator it = find_if(v.begin(), v.end(), odd);
    cout << *it << endl;

    vector<int>::iterator it2 = find_if(v.begin(), v.end(), OddSelector());
    cout << *it2 << endl;
}
					
استفاده‌ی پیشرفته‌تر از شیءتابع‌ها

این مثال، نشان می‌دهد استفاده از شیءتابع‌ها می‌تواند پیشرفته‌تر از مثال قبل باشد و شیء تابع مربوطه علاوه بر عملگر پرانتز اعضای دیگری هم داشته باشد. شیءتابع تعریف شده در این مثال، دانشجویان را بر اساس نمره cutoff فیلتر می‌کند. دقت کنید که اگر می‌خواستیم از اشاره‌گر به تابع استفاده کنیم لازم بود برای هر مقدار cutoff باید یک تابع مجزا می‌نوشتیم.

#include <iostream>
#include <vector>
using namespace std;

/*
template<class In, class Pred>
In find_if(In first, In last, Pred pred) {
    while (first!=last && !pred(*first)) 
        ++first;
    return first; 
}
*/

class student {
public:
    student(string n, double d) : name(n), grade(d) {}
    string get_name() const { return name; }
    double get_grade() const { return grade; }
private:
    string name;
    double grade;
};

class GradeBelow {
public:
    GradeBelow(double c) : cutoff(c) {}
    bool operator()(const student& s) {
        return s.get_grade() < cutoff;
    }
private:
    double cutoff;
};

int main() {
    vector<student> v;
    v.push_back(student("Moez", 8.0));
    v.push_back(student("Gholam", 17.3));
    v.push_back(student("Ghamar", 4.5));

    vector<student>::iterator it;
    it = find_if(v.begin(), v.end(), GradeBelow(10));
    cout << it->get_name() << endl;
    
    it = find_if(v.begin(), v.end(), GradeBelow(5));
    cout << it->get_name() << endl;
}
					
تمرین‌های کوتاه
  1. هدف این سؤال نوشتن تابعی است با امضای زیرکه برداری ازتاریخ‌ها رامی‌گیرد و اولین تاریخی را که در فصل تابستان است برمی‌گرداند. فرض کنید تعریف کلاس Date مطابق بخش ۷ است.

    Date first_in_summer(vector v);

    • 
این تابع را به کمک تکرارکننده روی بردار پیاده‌سازی کنید.
    • این تابع را به کمک تابع find_if و اشاره‌گر به تابع پیاده‌سازی کنید.
    • این تابع را به کمک تابع find_if و شیءتابع پیاده‌سازی کنید.
  2. ‌تابع تعمیم‌یافته‌ای بنویسید با امضای زیر که شماره‌ی فصل (۱ تا ۴) را به عنوان پارامتر دوم می‌گیرد و اولین تاریخی را که در آن فصل است برمی‌گرداند. این تابع را به کمک شیءتابع پیاده‌سازی کنید.

    Date first_in_season(vector v, int season);

  3. تابعی به نام count بنویسید که بتوان با آن تعداد عناصر یک container را با استفاده از تکرارکننده‌ها به دست آورد. در تعریف پارامترهای این تابع دقت کنید (می‌توانید از تابع find ایده بگیرید).
  4. تابعی به نام count_if بنویسید که علاوه بر پارامترهای تابع count در مثال قبل، یک مسند نیز دریافت کند و تعداد عناصری را برگرداند که آن مسند را ارضاء می‌کنند (می‌توانید از تابع find_if ایده بگیرید).