Elimizde bulunan belirli sayıda harf ile oluşturulabilecek ingilizce sözlüğümüzde yer alan tüm sözcükleri bulan bir program yazıyoruz. Örneğin sözlüğümüzde bulunan sözcüklerden “ankara” harfleriyle oluşturabileceklerimiz şunlar:
1 2 3 |
ankara, kanara, arank, rakan, akan, akra, arak, arna, kana, karn, knar, kran, krna, nark, rana, rank, aaa, aka, ana, ara, ark, arn, kan, kra, naa, nak, nar, ran, aa, ak, an, ar, ka, kn, kr, na, nr, ra, rn, a, k, n, r, p |
Kodumuz şöyle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <string> #include <set> #include <iostream> #include <fstream> #include <vector> #include <algorithm> #include <iterator> bool is_buildable(const std::string &str, const std::string &key) { if (str.size() > key.size()) return false; std::multiset<char> check_set{ key.begin(), key.end() }; for (char c : str) { auto iter = check_set.find(c); if (iter == check_set.end()) { return false; } check_set.erase(iter); } return true; } int main() { using namespace std; string str; cout << "anahtar sozcugu giriniz : "; cin >> str; ifstream ifs("dict.txt"); if (!ifs) { cerr << "dosya acilamadi" << endl; return 1; } vector<string> svec{ istream_iterator<string>{ifs}, istream_iterator<string>{} }; struct LocalPred { bool operator()(const string &r1, const string &r2) { return r1.size() > r2.size() || r1.size() == r2.size() && r1 < r2; }; }; set<string, LocalPred> rset; copy_if(svec.begin(), svec.end(), inserter(rset, rset.begin()), [str](const string &s){return is_buildable(s, str); }); cout << "toplam " << rset.size() << " sozcuk bulundu" << endl; for (const auto &rs : rset) { cout << rs << "\n"; } return 0; } |
is_buildable işlevi birinci parametresine geçilen stringin (str) ikinci parametresine geçilen stringin (key) karakterleri kullanılarak elde edilip elde edilemeyeceğini sınıyor. Eğer str‘nin uzunluğu key‘in uzunluğundan fazla ise bu mümkün değil:
1 2 |
if (str.size() > key.size()) return false; |
Arama işlemini kolaylaştırmak için key‘in karakterlerini bir multiset kabında tutuyoruz. multiset kabının aynı değere sahip öğelerden birden fazlasını tutabileceğini hatırlayalım. multiset türünden check_set nesnesini sınıfın aralık (range) parametreli kurucu işleviyle oluşturduk:
1 |
std::multiset<char> check_set{ key.begin(), key.end() }; |
Bir aralık tabanlı for döngüsüyle (range based for loop) str stringinin tüm karakterlerini dolaşıp her bir karakterin check_set içinde bulunup bulunmadığını test ettik. multiset sınıfının find işlevi başarsızılık durumunda aralık sonu adımlayıcısını yani check_set.end() değerini döndürüyor. find işlevinin geri dönüş değerini tutacak iter isimli değişkenin tanımlanmasında auto belirleyicisini kullandık. auto belirleyicisi olmasaydı iter değişkeni şöyle tanımlanacaktı:
1 |
std::multiset<char>::iterator iter = check_set.find(c); |
Eğer aradığımız karakter check_set içinde yok ise false değeriyle geri döndük. Aradığımız karakterin bulunması durumunda ise bu karakteri check_set‘ten sildik. İşlevin akışı for döngüsünün dışına çıkar ise str stringi key stringinin karakterleriyle olışturulabilir demektir.
main işlevinde yaptıklarımız özetle şöyle:
i)Kullanıcıdan alınacak teste konu karakterleri almak tutması için str isimli string nesnesini kullanıldı. operator>> işleviyle yapılan okumalarda boşluk karakterlerinin ayıraç olarak kullanıldığını, boşluk karakterlerinin stringe yazılmayacağını hatırlayalım.
ii) dict.txt dosyasındaki sözcükleri tutacak svec isimli bir yerel vector nesnesi olluşturduk. Bu vectorde bulunan sözcüklerden bizim koşulumuza uygun olanları bir sete kopyalayacağız. set‘in sıralama ilişkisini belirlemek için LocalPred isimli yerel bir sınıf (local class) oluşturduk. set sınıf şablonunun ikinci şablon tür parametresi olarak bu sınıfı kullanarak rset isimli bir set nesnesi yanımladık.
ii) copy_if algoritmasıyla svec vectorunde tutulan sözcüklerden koşulumuza uyanları rset setine kopyaladık. Setimiz boş olduğu için copy_if algoritmanın setimize ekleme yapabilmesi için iterator başlık dosyasında bildirilen inserter adımlayıcı uyumlandırıcısını (iterator adaptor) kullandık. Koşulumuzu sınayan işlevin oluşturulabilmesi için, copy_if algortimasının son parametresine ise bir lambda ifadesinden elde edilen geçici nesneyi gönderdik. lamba ifadesi yerel str stringini yakalayarak (capture) işlev içinde kullanılmasını sağladı.
iv) Aralık tabanlı for döngüsüyle setimizi dolaşarak bulunan tüm sözcükleri kullanıcı ekranına yazdırdık.
Son olarak, yazdığımız kodda kullanılan C++11 ile gelen tüm öğeleri ya da özellikleri bir listeleyelim:
uniform initializer (ilk değer vermede { } kullanımı)
auto belirleyicisi
aralık tabalı for döngüsü
lambda ifadeleri
yerel sınıf nesnelerinin algoritmalarda kullanımı