Writing Generic Functions

Kategori: C++ , 12 Kasım 2019 , JanFranco


Generic fonksiyonlarda return tipi yani fonksiyon tipi türden bağımsızdır. Yani fonksiyonu nasıl çağırırsak, çağırdığımız şekilde çalışır, tür ona göre belirlenir. Bir örnek görelim:


template<class T>

T median(vector<T> v){
    typedef typename vector<T>::size_type vec_sz;
    vec_sz size = v.size();
    sort(v.begin(), v.end());
    vec_sz mid = size / 2;
    return size % 2 == 0 ? (v[mid] + v[mid-1]) / 2 : v[mid];
}
Generic bir fonksiyon tanımlayacaksak öncesinde bir template tanımlamamız gerekir. Burada template diyerek bir generic tür yaratmış olduk. Fonksiyonu bu sayede T tipinde tanımlayabildik. Sadece fonksiyonu değil v vektörünü de T tipinde oluşturduk. Bu durumda bu fonksiyona int değerler tutan bir vektör gönderilebilir, double değerler tutan bir vektör gönderebilir vs. Nasıl bir vektör gönderilirse fonksiyon o şekilde çalışacaktır. Generic fonksiyonlardaki bir problemden bahsedelim:


template<class T>
T max(const T& left, const T& right){
    return left > right ? left : right;
}
Burada bir değer tam sayı bir değer double tipinde verilirse burada hata alırız. Çünkü fonksiyon T değerinin ne olduğunu anlayamaz. T değerini int tipine mi çevirecek, double tipine mi? Bunu bilemeyeceğinden bu kod compile edilemez.

C++ dilinde çoğu fonksiyon ve iteratorlar generic şekilde yazılmıştır. Iterator'ların türleri vardır, bunları inceleyelim. İlk olak input iterator'lardan başlayalım. Input iterator'lar sadece okuma işlemi yapabilirler. Bir örnek yapalım:


template <class In, class X> 

In find(In begin, In end, const X& x) {
    while (begin != end && *begin != x)
        ++begin;
    return begin;
}
Burada iki generic tip tanımladık. begin, end değerine ve aranan değere eşit olmadığı sürece ++begin işlemi yapılacak. Yani dizide gezineceğiz ve x değerini arayacağız. find(begin, end, x) şeklinde bir kullanım yaptığımızda [begin, end) aralığında x değerini aramış olacağız. Output iterator'ların sadece yazma izinleri vardır. Örnek görelim:


template<class In, class Out>

Out copy(In begin, In end, Out dest) {
    while (begin != end)
	*dest++ = *begin++;
    return dest;
}
Burada başlangıç ve bitiş iterator'larını alıyoruz, [begin, end) aralığında bir dizideki değerleri başka bir diziye aktarıyor ve onu return ediyoruz. Forward iterator örneği görelim:


template<class For, class X>

void replace(For beg, For end, const X& x, const X& y){
    while (beg != end){
	if (*beg == x)
	    *beg = y;
	++beg;
    }
}
Burada [beg, end) aralığında çalışacağız, x değerini okuduğumuz bunu y ile değiştireceğiz. Burada sadece ileri gidebiliriz. Geriye gidemeyiz. Bidirectional iteratorlarda ise geriye de gidebiliriz, örnek görelim:


template<class Bi> 

void reverse(Bi begin, Bi end){
    while (begin != end) {
	--end;
	if (begin != end)
	    swap(*begin++, *end);
    }
}
Burada [begin, end) aralığında end'den başlayarak begin'e doğru gideceğiz ve her adımda begin'deki değer ile end'deği değeri yer değiştireceğiz. Random-access iteratorlarda, p ve q iteratorlar ve n tam sayı olmak üzere aşağıdaki işlemleri yapabiliriz:


p + n, p - n, n + p
p - q
p[n] (anlamına gelir: *(p + n))
p < q, p > q, p <= q, p >= q
Bir örnek görelim:


template<class Ran, class X>

bool binary_search(Ran begin, Ran end, const X& x){
    while (begin < end) {
	Ran mid = begin + (end - begin) / 2;
	if (x < *mid)
	    end = mid;
	else if (*mid < x)
	    begin = mid + 1;
	else 
	    return true;
    }
    return false;
}
Yukarıda generic bir şekilde yazılmış binary search fonksiyonu görüyoruz. İlk adımda begin değerimiz begin, end değerimiz end, mid değerimiz ortadaki değer. Eğer aradığımız değer ortadaki değerden büyük ise begin değerimiz mid, end değerimiz end, mid değerimiz ortadaki değer olacak. Eğer aradığımız deper ortadaki değerden küçük ise begin değerimiz begin, end değerimiz mid olacak. Bu şekilde devam edecek. Son olarak daha önce yazdığımız split fonksiyonunu daha flexible hale getirelim:


template <class Out> // changed

void split(const string& str, Out os){ // changed
    typedef string::const_iterator iter;
    iter i = str.begin();
    while (i != str.end()) {
	i = find_if(i, str.end(), not_space);
	iter j = find_if(i, str.end(), space);
	if (i != str.end())
	    *os++ = string(i, j); // changed
	i = j;
    }
}
Artık kelimeleri nereye yazacağımızı istediğimiz gibi belirleyebiliriz:


split(s, back_inserter(word_list));

int main(){
    string s;
    while (getline(cin, s))
	split(s, ostream_iterator<string>(cout, "\n"));
    return 0;
}


Sonraki Yazı: Defining New Types
Yorumlar

Henüz bir yorum bulunmuyor.