Bütünleştirme ve final hazırlığı
Bu hafta yeni veri yapısı veya yeni algoritma öğrenmeyeceğiz. Amaç, dönem boyunca öğrendiğimiz kavramları tek bir problem üzerinde birlikte kullanmak ve hangi kararı neden verdiğimizi açıklamaktır.
Bu bölümde küçük bir kütüphane kayıt sistemi üzerinden şu becerileri birleştireceğiz:
- kayıtları uygun koleksiyonlarda tutma,
- benzersizlik ve üyelik kontrolü,
- koda göre erişim,
- doğrusal arama,
- sıralama,
- kuyruk davranışı,
- referans ilişkilerini fark etme,
- sınır durumu kontrolü,
- kısa maliyet gerekçesi.
Bölümün sonunda ise farklı bir bağlama geçerek transfer görevi yapacağız. Böylece yalnızca kütüphane örneğini ezberleyip ezberlemediğinizi değil, aynı düşünme biçimini yeni bir probleme taşıyıp taşıyamadığınızı sınayacağız.
1 Bu hafta neleri yapabilmelisiniz?
Bölümün sonunda:
- bir problem metninden veri ve işlem gereksinimlerini çıkarabilmeli,
- birden çok koleksiyonu birlikte kullanabilmeli,
- hazır kod tabanında sınırlı bir değişiklik yapabilmeli,
- arama ve sıralama yöntemlerini uygun bağlamda seçebilmeli,
- sınır durumlarını
assertile doğrulayabilmeli, - çözümünüzü veri yapısı ve maliyet açısından kısa biçimde gerekçelendirebilmeli,
- aynı karar yöntemini daha önce görmediğiniz yeni bir problem bağlamına aktarabilmelisiniz.
2 Problem: küçük kütüphane sistemi
Sistemde kitaplar şu alanlara sahiptir:
idtitlecategoryavailable
Ayrıca öğrenciler ödünç almak istedikleri kitaplar için bekleme sırasına girebilir.
Başlangıç verisi:
#| edit: false
#| completion: false
books = [
{"id": "B101", "title": "Python'a Giriş", "category": "Programlama", "available": True},
{"id": "B205", "title": "Ağ Temelleri", "category": "Ağ", "available": False},
{"id": "B310", "title": "Veri Yapıları", "category": "Programlama", "available": True},
{"id": "B415", "title": "Linux Araçları", "category": "Sistem", "available": True},
]
print(books)
3 1. Gereksinimleri ayıralım
Sistem şu işlemleri yapacak:
- bütün kitapları sırayla göstermek,
- kitap kimliğine göre sık erişim yapmak,
- belirli kategorideki kitapları filtrelemek,
- başlığa göre sıralı rapor üretmek,
- ödünçteki kitap için öğrencileri geliş sırasıyla bekletmek.
Tek bir veri yapısı bütün gereksinimler için en iyi değildir.
| Gereksinim | Uygun yapı / yaklaşım |
|---|---|
| kitapların genel listesi | list |
| kimliğe göre erişim | dict indeks |
| kategori filtresi | doğrusal dolaşma |
| başlığa göre rapor | sorted(..., key=...) |
| bekleme sırası | deque |
Bu tablo, dönem boyunca vurguladığımız gereksinim → seçim ilişkisini gösterir.
4 2. Kimliğe göre indeks oluşturma
#| edit: false
#| completion: false
books = [
{"id": "B101", "title": "Python'a Giriş", "category": "Programlama", "available": True},
{"id": "B205", "title": "Ağ Temelleri", "category": "Ağ", "available": False},
{"id": "B310", "title": "Veri Yapıları", "category": "Programlama", "available": True},
]
books_by_id = {}
for book in books:
books_by_id[book["id"]] = book
print(books_by_id["B310"])
Bu indeks, aynı liste üzerinde kimliğe göre tekrar tekrar doğrusal arama yapma ihtiyacını azaltır.
5 3. Kategoriye göre filtreleme
#| edit: false
#| completion: false
def books_in_category(books, category):
result = []
for book in books:
if book["category"] == category:
result.append(book)
return result
books = [
{"id": "B101", "title": "Python'a Giriş", "category": "Programlama"},
{"id": "B205", "title": "Ağ Temelleri", "category": "Ağ"},
{"id": "B310", "title": "Veri Yapıları", "category": "Programlama"},
]
print(books_in_category(books, "Programlama"))
Bu işlem bütün kayıtları görebilmek zorunda olduğu için doğrusal dolaşma doğaldır.
6 4. Sıralı rapor
#| edit: false
#| completion: false
books = [
{"id": "B101", "title": "Python'a Giriş"},
{"id": "B205", "title": "Ağ Temelleri"},
{"id": "B310", "title": "Veri Yapıları"},
]
ordered = sorted(books, key=lambda book: book["title"])
for book in ordered:
print(book["title"])
sorted() kullandığımız için orijinal books listesinin sırası değişmez.
7 5. Bekleme sırası
Bir kitap ödünçteyse öğrenciler FIFO sırasıyla beklesin:
#| edit: false
#| completion: false
from collections import deque
waiting = deque()
waiting.append("Ayşe")
waiting.append("Bora")
waiting.append("Cem")
print("Sıradaki:", waiting[0])
print("Kitabı alan:", waiting.popleft())
print("Kalan:", list(waiting))
Bu problem yığın değil kuyruk davranışı ister; ilk gelen öğrencinin önce hizmet alması gerekir.
8 6. Aynı verinin birden çok görünümü
Bir kitabın aynı sözlük nesnesi hem books listesinde hem books_by_id sözlüğünde tutulabilir:
#| edit: false
#| completion: false
books = [
{"id": "B101", "title": "Python'a Giriş", "available": True},
{"id": "B205", "title": "Ağ Temelleri", "available": False},
]
books_by_id = {book["id"]: book for book in books}
books_by_id["B101"]["available"] = False
print(books[0])
print(books_by_id["B101"])
Neden iki yerde de değişiklik görünür? Çünkü iki koleksiyon aynı kitap sözlüğüne referans verir.
Bu davranış bazen yararlıdır; bazen bağımsız kopya bekleyen programcı için hata kaynağı olabilir. Referans sezgisini unutmayın.
9 7. Hazır kod tabanına özellik ekleme
Aşağıdaki kod küçük bir başlangıç sistemidir:
#| edit: false
#| completion: false
from collections import deque
books = [
{"id": "B101", "title": "Python'a Giriş", "category": "Programlama", "available": True},
{"id": "B205", "title": "Ağ Temelleri", "category": "Ağ", "available": False},
{"id": "B310", "title": "Veri Yapıları", "category": "Programlama", "available": True},
]
books_by_id = {book["id"]: book for book in books}
waiting_lists = {
"B101": deque(),
"B205": deque(),
"B310": deque(),
}
def find_book(book_id):
return books_by_id.get(book_id)
def add_to_waiting_list(book_id, student_name):
book = find_book(book_id)
if book is None:
return False
waiting_lists[book_id].append(student_name)
return True
print(find_book("B310"))
print(add_to_waiting_list("B205", "Ayşe"))
print(list(waiting_lists["B205"]))
Şimdi buna “kitap iade edildiğinde sıradaki öğrenciye ver” özelliği ekleyeceğiz.
10 Alıştırma — return_book fonksiyonu
Kurallar:
- kitap kimliği yoksa
Nonedöndür, - bekleme listesinde öğrenci varsa ilk öğrenciyi çıkar ve adını döndür,
- bekleyen yoksa kitabı
available = Trueyap veNonedöndür, - bekleyen öğrenciye kitap verildiğinde kitap kullanılabilir (
available=True) olmamalıdır.
#| exercise: hafta14-return-book
#| completion: false
#| persist: true
from collections import deque
books = [
{"id": "B101", "title": "Python'a Giriş", "available": False},
{"id": "B205", "title": "Ağ Temelleri", "available": False},
]
books_by_id = {book["id"]: book for book in books}
waiting_lists = {
"B101": deque(["Ayşe", "Bora"]),
"B205": deque(),
}
def return_book(book_id):
# Burayı tamamlayın.
pass
print(return_book("B101"))
print(list(waiting_lists["B101"]))
print(books_by_id["B101"]["available"])
#| exercise: hafta14-return-book
#| check: true
scope = {}
try:
exec(user_code, scope)
from collections import deque
scope["books_by_id"] = {
"X": {"id": "X", "title": "X", "available": False},
"Y": {"id": "Y", "title": "Y", "available": False},
}
scope["waiting_lists"] = {
"X": deque(["Ali", "Ece"]),
"Y": deque(),
}
fn = scope.get("return_book")
first = fn("X")
x_ok = (
first == "Ali"
and list(scope["waiting_lists"]["X"]) == ["Ece"]
and scope["books_by_id"]["X"]["available"] is False
)
second = fn("Y")
y_ok = second is None and scope["books_by_id"]["Y"]["available"] is True
missing_ok = fn("NOPE") is None
correct = callable(fn) and x_ok and y_ok and missing_ok
except Exception:
correct = False
feedback = (
{"correct": True, "message": "İade akışı bekleme kuyruğu ve kitap durumu açısından doğru çalışıyor."}
if correct
else {"correct": False, "message": "Önce kitabı get() ile bulun. Bekleyen varsa popleft() ile ilk öğrenciyi döndürün; yoksa available=True yapın."}
)
feedback
book = books_by_id.get(book_id) ile başlayın. if book is None: return None. Sonra waiting_lists[book_id] kuyruğunu kontrol edin.
Solution.
def return_book(book_id):
book = books_by_id.get(book_id)
if book is None:
return None
queue = waiting_lists[book_id]
if queue:
book["available"] = False
return queue.popleft()
book["available"] = True
return None11 8. Davranışı doğrulama
Yeni özelliği yalnızca bir örnekle denemek yeterli değildir. Sınır durumlarını da kontrol edin:
#| edit: false
#| completion: false
from collections import deque
books_by_id = {
"B1": {"id": "B1", "available": False},
"B2": {"id": "B2", "available": False},
}
waiting_lists = {
"B1": deque(["Ayşe", "Bora"]),
"B2": deque(),
}
def return_book(book_id):
book = books_by_id.get(book_id)
if book is None:
return None
queue = waiting_lists[book_id]
if queue:
book["available"] = False
return queue.popleft()
book["available"] = True
return None
assert return_book("B1") == "Ayşe"
assert list(waiting_lists["B1"]) == ["Bora"]
assert books_by_id["B1"]["available"] is False
assert return_book("B2") is None
assert books_by_id["B2"]["available"] is True
assert return_book("B999") is None
print("Tüm kontroller geçti")
Bu testler üç önemli durumu kapsar:
- bekleyen var,
- bekleyen yok,
- kitap yok.
12 9. Kısa maliyet yorumu
Bu sistem için birkaç temel işlem:
12.1 Kimliğe göre kitap bulma
books_by_id.get(book_id) sözlük erişimidir. Ortalama durumda O(1) sezgisiyle düşünürüz.
12.2 Kategoriye göre bütün kitapları bulma
Tüm books listesini dolaşmak gerekir: O(n).
12.3 Bekleme listesinden sıradaki öğrenciyi alma
deque.popleft() kuyruk için uygun ve yaklaşık O(1) davranışlı işlemdir.
12.4 Başlığa göre rapor sıralama
sorted() genel amaçlı verimli bir sıralama aracıdır; sıralamanın maliyeti veri boyutuyla birlikte doğrusalın üzerinde büyür. Bu derste yerleşik sıralamayı uygulamada tercih ederiz.
13 10. Mini karar tablosu
Aşağıdaki tabloyu kapatıp her satırın cevabını kendiniz üretmeye çalışın:
| İhtiyaç | Seçim | Kısa gerekçe |
|---|---|---|
| kimliğe göre sık erişim | dict |
anahtar tabanlı erişim |
| benzersiz üye numaraları | set |
benzersizlik + üyelik |
| son yapılan işlemi geri al | yığın | LIFO |
| gelen talepleri sırayla işle | deque |
FIFO |
| sırasız küçük veride bir kez ara | doğrusal arama | sıralama maliyetine gerek yok |
| sıralı büyük veride çok kez ara | ikili arama | O(log n) arama sezgisi |
14 11. Transfer görevi — destek talepleri sistemi
Şimdi kütüphane örneğini bırakıyoruz. Aşağıdaki problem bu kitapta daha önce bütünleşik biçimde çözülmedi.
Bir teknik destek sistemi şu gereksinimlere sahip:
- Her talebin benzersiz bir
ticket_iddeğeri var. - Talepler geldikleri sırayla işlenecek.
ticket_idile belirli bir talebe çok sık erişilecek.- Daha önce kapatılmış kimliklerin yeniden kullanılmasına izin verilmeyecek.
- Yönetici, açık talepleri öncelik puanına göre sıralı rapor olarak görmek isteyecek.
- Son yapılan yönetim değişikliğinin geri alınabilmesi istenecek.
14.1 Görev A — yapı seçimi
Her gereksinim için uygun yapı veya yaklaşımı seçin:
Açık taleplerin geliş sırası: ______________________
Ticket ID ile erişim: ______________________________
Kapatılmış kimlikler: ______________________________
Öncelik raporu: ____________________________________
Geri alma geçmişi: _________________________________
14.2 Görev B — üç adımlı gerekçe
- haftadaki yöntemi kullanarak ticket ID ile erişim için kısa gerekçe yazın:
1. Temel işlem: ____________________________________
2. Sıklık / maliyet: _______________________________
3. Seçim: __________________________________________
Gerekçe: ___________________________________________
14.3 Görev C — yanlış çözümü eleştirin
Bir AI aracı şu çözümü öneriyor:
open_tickets = []
# yeni talep
open_tickets.append(ticket)
# sıradaki talep
current = open_tickets.pop(0)Bu kod FIFO açısından çalışabilir. Ancak on binlerce talebin bulunduğu yoğun bir sistemde hangi satırı neden sorgularsınız? Daha uygun Python yapısı nedir?
14.4 Görev D — arama kararı
Açık talepler ticket_id değerine göre sıralı tutulmuyor. Buna rağmen bir öğrenci ikili arama kullanmayı öneriyor. Bu önerideki temel ön koşul hatasını açıklayın.
Transfer görevinin amacı belirli bir kodu ezberlemek değildir. Beklenen düşünme sırası şudur:
gereksinimi çıkar → temel işlemi belirle → uygun yapı/algoritmayı seç → ön koşulu kontrol et → maliyeti kısa gerekçelendir
15 Final için kendinizi kontrol edin
Aşağıdaki soruların her birine birkaç cümleyle yanıt verebiliyorsanız dersin ana omurgasını büyük ölçüde kurmuşsunuz demektir:
list,dictvesethangi gereksinimlerde birbirinden ayrılır?- Aliasing ile sığ kopya arasındaki fark nedir?
- Yığın ile kuyruğun çıkarma sırası nasıl farklıdır?
deque.popleft()nedenlist.pop(0)yerine tercih edilebilir?- Doğrusal arama ve ikili arama hangi ön koşullarda uygundur?
- Insertion sort’un sıralı ve ters sıralı girdide davranışı neden farklıdır?
sorted()ile.sort()arasındaki fark nedir?- O(1), O(log n), O(n), O(n log n) ve O(n²) büyümelerini sezgisel olarak açıklayabilir misiniz?
- Bir veri yapısını seçerken hangi üç adımı izlemelisiniz?
- Çalışan bir çözümün neden yine de zayıf bir veri yapısı seçimi olabileceğini açıklayabilir misiniz?
16 Bölüm özeti
- Gerçek problemler çoğu zaman birden çok veri yapısını birlikte gerektirir.
list,dict,setvedequefarklı işlem gereksinimlerine hizmet eder.- Bir indeks oluşturmak, tekrar eden aramalarda önemli avantaj sağlayabilir.
- Referans ilişkileri, aynı kaydın birden fazla yapıdan görülmesini sağlayabilir.
- Sınır durumlarını
assertile doğrulamak çözümün güvenilirliğini artırır. - Finalde yalnızca kod üretmek değil, neden bu yapı ve neden bu algoritma? sorusunu yanıtlamak önemlidir.
- Transfer görevi, aynı düşünme modelini daha önce görülmemiş bir bağlama taşıyabilmeyi ölçer.