İçeriğe geç
academia.sh

Ders 10 / 16

Kavrama Sözdizimi

Aynı bin öğeden liste kavraması 1000, küme ve sözlük kavraması 100 öğe tutar; süzme koşulunun üretimden önce mi sonra mı yazıldığı var edilen nesneyi 5'ten 10'a çıkarır.

İçindekiler

Önceki iki ders köşeli ayraç ile parantez arasındaki farkı ölçtü ve tembelliğin neyi azaltıp neyi azaltmadığını sayıyla ayırdı. Ama köşeli ayracın kendisi hiç açılmadı: kavrama her ölçümde yalnız “hepsini şimdi üreten biçim” olarak durdu.

Oysa aynı yazım üç ayrı kap kurabilir. Köşeli ayraç liste, küme ayracı küme, aynı ayraç içinde iki nokta üst üste ile yazılan çift sözlük üretir. Üçü de aynı kaynaktan aynı öğeleri alır, ama üçü aynı sayıda öğe tutmaz. Bu dersin sorduğu iki soru şudur: üç kavrama biçimi aynı bin öğeden kaçını tutar, ve süzme koşulunun nereye yazıldığı var edilen nesne sayısını değiştirir mi?

Üç Kap, Tek Yazım

Kavrama bir döngü ile bir kap kurulumunun tek deyime sıkıştırılmış hâlidir. Üç biçimin sözdizimi neredeyse aynıdır ve aralarındaki tek fark ayraç ile öğe ifadesidir. Kavrama yazımı üreteç ifadesinden yalnız ayraçla ayrılır ve bu tek karakter kurulum davranışını tamamen değiştirir; önceki ders bunu ölçtü.

Üç kabın ayrıldığı yer öğeyi nasıl kabul ettikleridir. Liste her öğeyi sırayla kabul eder ve hiçbirini elemez. Küme öğeyi karma değerine göre yerleştirir ve eşiti varsa yenisini almaz. Sözlük aynı işi anahtar üzerinde yapar, ama değeri değiştirir: aynı anahtar ikinci kez geldiğinde eski değer düşer.

Veri Yapıları kursu bu üç kabın mekanizmasını — karma tablosu, çakışma, yerleşim — kurmuştu; burada mekanizma tekrarlanmaz. Ölçülen şey aynı kaynağın üç kapta kaç öğeye indiğidir.

Koşulun İki Yeri

Kavrama iki ayrı yerde koşul kabul eder ve ikisi aynı şey değildir.

Süzme koşulu yazımın sonuna gelir ve döngü değişkeni üzerinde çalışır: koşulu geçmeyen tur öğe ifadesini hiç çalıştırmaz. Koşullu ifade ise öğe ifadesinin kendi içindedir ve her turda bir sonuç seçer; hiçbir turu elemez.

İkisi karıştırıldığında sonuç aynı görünebilir ama sayı görünmez. Ölçüm bunu ayırır.

Ölçümün varsayımları:

  • YN28 — Ortak tanımın Oge sınıfı sayma düzeniyle aynen kullanılır; kâhin kurgunun kendisidir.
  • YN29 — Üç kavrama aynı bin öğelik kaynak üzerinde çalışır ve kaynak ölçümden önce kurulmuştur; bu yüzden üçünün de “var edilen” sütunu sıfırdır. Ölçülen şey tutmadır.
  • YN30 — Küme ve sözlük kavramaları öğeleri deger % 100 üzerinden gruplar; sayı keyfîdir ve yalnız çakışmanın gözlenebilmesi için seçilmiştir.
  • YN31 — “Paylaşılan Oge”, kabın içinde kaynaktaki nesnelerin kendisinin durup durmadığıdır; türetilmiş bir sayı tutan kapta bu sütun sıfırdır.
  • YN32 — Koşul ölçümü on öğede yapılır ve koşul öğelerin yarısını geçirir; ölçülen şey büyüklük değil, iki yazım arasındaki üretim farkıdır.
  • YN33 — İç içe ölçümde tablo dört satır, üç sütundur; toplam öğe üç yazımda da 12’dir. “Kurulan kap” sayısına dış kap da dahildir.
  • YN34 — Kapsam ölçümünde kavramanın döngü adı, dışarıda aynı adı taşıyan bir değişkenle kasıtlı olarak çakıştırılır.
  • YN35 — Son ölçümde iki yazım birebir aynıdır; değişen tek şey iç kaynağın liste mi üreteç ifadesi mi olduğudur. Dış döngü üç turludur ve iç kaynak iki öğelidir.

Ölçüm

"""Kavrama: uc kap, ayni kaynak, ayri tutma; ve icice kavramanin ara nesnesi."""

SAYAC = {"uretilen": 0}


class Oge:
    """Her uretimi sayan oge."""

    def __init__(self, deger):
        SAYAC["uretilen"] += 1
        self.deger = deger

    def __repr__(self):
        return f"Oge({self.deger})"


def sifirla():
    SAYAC["uretilen"] = 0


def uretilen():
    return SAYAC["uretilen"]


N = 1000
KAYNAK = [Oge(i) for i in range(N)]
KAVRAMALAR = (
    ("liste kavraması", lambda: [o for o in KAYNAK]),
    ("küme kavraması", lambda: {o.deger % 100 for o in KAYNAK}),
    ("sözlük kavraması", lambda: {o.deger % 100: o for o in KAYNAK}))

print(f"{'kavrama':<18s} {'var edilen':>11s} {'kapta tutulan':>14s}"
      f" {'paylaşılan Oge':>15s}")
for ad, kur in KAVRAMALAR:
    sifirla()
    kap = kur()
    ogeler = list(kap.values()) if isinstance(kap, dict) else list(kap)
    ortak = sum(1 for o in ogeler if isinstance(o, Oge))
    print(f"  {ad:<16s} {uretilen():11d} {len(kap):14d} {ortak:15d}")


def kosul_once(n=10):
    sifirla()
    liste = [Oge(i) for i in range(n) if i % 2 == 0]
    return uretilen(), len(liste)


def kosul_sonra(n=10):
    sifirla()
    liste = [o for o in (Oge(i) for i in range(n)) if o.deger % 2 == 0]
    return uretilen(), len(liste)


def kosullu_ifade(n=10):
    sifirla()
    liste = [Oge(i) if i % 2 == 0 else Oge(-i) for i in range(n)]
    return uretilen(), len(liste)


print()
print(f"{'koşulun yeri':<28s} {'var edilen':>11s} {'tutulan':>8s}")
for ad, f in (("üretimden önce süzme", kosul_once),
              ("üretimden sonra süzme", kosul_sonra),
              ("koşullu ifade (süzme yok)", kosullu_ifade)):
    u, t = f()
    print(f"  {ad:<26s} {u:11d} {t:8d}")


def ic_ice(m=4, k=3):
    sifirla()
    tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)]
    return uretilen(), 1 + len(tablo), sum(len(s) for s in tablo)


def duz(m=4, k=3):
    sifirla()
    liste = [Oge(i * k + j) for i in range(m) for j in range(k)]
    return uretilen(), 1, len(liste)


def duzlestirme(m=4, k=3):
    sifirla()
    tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)]
    liste = [o for satir in tablo for o in satir]
    return uretilen(), 2 + len(tablo), len(liste)


print()
print(f"{'iç içe yazım':<24s} {'var edilen':>11s} {'kurulan kap':>12s}"
      f" {'öğe':>4s}")
for ad, f in (("satır satır tablo", ic_ice), ("tek düzeyde kavrama", duz),
              ("tabloyu düzleştirme", duzlestirme)):
    u, kap, oge = f()
    print(f"  {ad:<22s} {u:11d} {kap:12d} {oge:4d}")

i = "dışarıdaki değer"
kareler = [i * i for i in range(4)]
print()
print(f"kavramadan sonra dışarıdaki ad: {i!r}, sonuç {kareler}")

sifirla()
sozluk = {o.deger: o for o in (Oge(i % 4) for i in range(12))}
print(f"anahtar çakışması: üretilen {uretilen()}, tutulan {len(sozluk)},"
      f" son değer kazandı -> {sozluk[0].deger}")


def ic_kaynak_liste():
    sifirla()
    ic = [0, 1]
    liste = [Oge(i * 2 + j) for i in range(3) for j in ic]
    return uretilen(), len(liste)


def ic_kaynak_akis():
    sifirla()
    ic = (x for x in [0, 1])
    liste = [Oge(i * 2 + j) for i in range(3) for j in ic]
    return uretilen(), len(liste)


print()
print(f"{'iç kaynağın türü':<28s} {'var edilen':>11s} {'tutulan':>8s}")
for ad, f in (("liste (yeniden gezilebilir)", ic_kaynak_liste),
              ("üreteç ifadesi (tek geçişlik)", ic_kaynak_akis)):
    u, t = f()
    print(f"  {ad:<30s} {u:9d} {t:8d}")
kavrama             var edilen  kapta tutulan  paylaşılan Oge
  liste kavraması            0           1000            1000
  küme kavraması             0            100               0
  sözlük kavraması           0            100             100

koşulun yeri                  var edilen  tutulan
  üretimden önce süzme                 5        5
  üretimden sonra süzme               10        5
  koşullu ifade (süzme yok)           10       10

iç içe yazım              var edilen  kurulan kap  öğe
  satır satır tablo               12            5   12
  tek düzeyde kavrama             12            1   12
  tabloyu düzleştirme             12            6   12

kavramadan sonra dışarıdaki ad: 'dışarıdaki değer', sonuç [0, 1, 4, 9]
anahtar çakışması: üretilen 12, tutulan 4, son değer kazandı -> 0

iç kaynağın türü              var edilen  tutulan
  liste (yeniden gezilebilir)            6        6
  üreteç ifadesi (tek geçişlik)          2        2

Üç Kabın Tuttuğu

Üst tabloda “var edilen” sütunu üç satırda da 0. Bu bir tuhaflık değil, kurgunun kendisidir: bin öğe ölçümden önce kurulmuştu ve üç kavrama da yeni bir Oge üretmiyor, yalnız var olanları alıyor. Ölçülen şey burada üretim değil, tutma.

Liste kavraması 1000 öğe tutuyor ve bunların 1000’i kaynaktaki nesnenin kendisi. Yeni bir kap kuruldu, içine bin başvuru yazıldı, hiçbir öğe çoğaltılmadı — ilk dersin ölçtüğü kap kurma işleminin ta kendisi.

Küme kavraması 100 öğe tutuyor ve paylaşılan 0. İki sayı birlikte okunmalı: kavramanın öğe ifadesi o.deger % 100 bir sayı üretiyor, Oge değil; kap kaynaktaki nesneleri hiç almıyor. Bin turdan yalnız yüz ayrı değer çıkıyor, çünkü küme eşitini ikinci kez kabul etmiyor. Bin öğe okundu, yüz öğe tutuldu — ve okunan ile tutulan arasındaki fark 900.

Sözlük kavraması da 100 tutuyor ama paylaşılan 100. Anahtar türetilmiş bir sayı, değer ise kaynaktaki nesnenin kendisi. Yani sözlük hem eledi hem paylaştı: yüz anahtarın her biri, o anahtara düşen son Oge nesnesine bağlı.

Son satır bunun sonucunu doğrudan gösteriyor. On iki öğe üretildi, dördü tutuldu ve çakışan anahtarlarda son değer kazandı. Sözlük kavramasında elemeyi yapan şey sessizdir: düşen öğeler için ne uyarı vardır ne istisna. Bin satırlık bir kaynaktan yüz satırlık bir sözlük çıktığında yazım aynı kalır, kaybın izi ancak sayılırsa görünür.

Koşul Nereye Yazılırsa

Orta tablo üç satırında da aynı on turu döner ama üretim sayıları ayrılır.

Üretimden önce süzme — koşul yazımın sonunda ve döngü değişkeni üzerinde — 5 nesne var ediyor. Koşulu geçmeyen beş tur öğe ifadesine hiç ulaşmıyor; Oge çağrısı yapılmıyor.

Üretimden sonra süzme aynı beş öğeyi tutuyor ama 10 nesne var ediyor. Burada koşul öğenin kendisine bakıyor, dolayısıyla öğenin önce var olması gerekiyor. Beş nesne doğuyor, süzülüyor ve hiçbir kaba girmediği için erişilemez oluyor.

İki satırın tuttuğu sayı aynı: 5. Sonuç da aynı. Ayrılan tek şey elemenin hangi tarafta yapıldığı. Süzme koşulu kaynağın diliyle yazılabiliyorsa üretimden kaçınılır; yalnız üretilmiş nesnenin özelliğine bakılabiliyorsa kaçınılamaz. Bu, önceki dersin erken bırakma ölçümünün kavrama içindeki karşılığıdır — tembellik yine bir olanaktır, koşulun nereye yazılabildiğine bağlı.

Koşullu ifade satırı üçüncü davranışı gösteriyor: 10 üretim, 10 tutma. Buradaki if bir süzgeç değil, bir seçicidir; hiçbir turu elemez, yalnız hangi nesnenin üretileceğini seçer. İki yazım birbirine benzer ve konumları farklıdır: seçici öğe ifadesinin içinde, süzgeç yazımın sonundadır. Ayrımı sayı verir — süzgeç tutulanı azaltır, seçici azaltmaz.

İç İçe Kavramanın Ara Kapları

Alt tablo aynı on iki öğeyi üç yazımla kuruyor. “Var edilen” sütunu üçünde de 12 ve öğe sayısı üçünde de 12; ayrılan sütun kap sayısıdır.

Satır satır tablo — bir kavramanın içinde başka bir kavrama — 5 kap kuruyor: dört satır listesi ve onları taşıyan dış liste. İç kavrama her dış turda yeniden çalışıyor ve her seferinde yeni bir liste üretiyor. Bu listeler ara nesne değil, sonucun kendisidir; tablo yapısı isteniyorsa gereklidir.

Tek düzeyde kavrama — iki for yan yana, tek öğe ifadesi — aynı on iki öğeyi 1 kap kuruyor. İki döngü tek kavrama içinde iç içe dönüyor ve ara liste hiç doğmuyor. Sıra da aynı: soldaki for dış döngüdür.

Tabloyu düzleştirme üçüncü yolu gösteriyor: önce tablo kurulur, sonra düzleştirilir. Sonuç ikinci satırla aynı, kap sayısı 6. Beş kap kuruldu, biri daha eklendi ve kurulan dört satır listesi düzleştirmeden sonra hiçbir işe yaramıyor — bunlar gerçek ara nesnelerdir. Aynı sonucun tek kapla kurulabildiği yerde beşi fazladan.

Ayrım şuraya oturur: iç içe kavrama yapıyı kurar, yan yana for geziyi kurar. Sonuç düz bir dizi olacaksa yapıyı kurup sonra yıkmak, hiç kurmamaktan pahalıdır.

İç Kaynak Kaç Kez Gezilir

Son tablo yan yana iki for yazımının en kolay gözden kaçan yanını ölçüyor: iki yazım birebir aynı, sonuçlar 6’ya karşı 2.

Fark iç kaynağın türünden geliyor. Sağdaki for her dış turda yeniden gezilir; üç dış tur için iç kaynaktan üç kez baştan geçilmesi gerekir. İç kaynak bir listeyse bu sorunsuz olur — liste her istendiğinde taze bir yineleyici verir ve üç kez ikişer öğe, toplam 6 öğe çıkar.

İç kaynak bir üreteç ifadesiyse olmaz. İlk dış turda akış tüketilir; ikinci ve üçüncü turlarda aynı akıştan yeniden geçilmek istenir ve akış boş döner. Sonuç 2 öğe, ve ortada ne istisna vardır ne uyarı. Birinci derste ölçülen tek geçişlilik burada bir sonuç üretiyor: tükenmiş akış boş akıştan ayırt edilemediği için kavrama sessizce eksik bir liste kuruyor.

Bu, üç dersin biriktirdiği ayrımın pratikteki karşılığıdır. Bir akış yeniden gezilecek bir yere konuyorsa — iç içe bir döngünün iç kaynağı, iki kez okunacak bir değişken — önce listeye alınmalıdır. Alınmazsa ikinci geçiş hata değil, sessiz bir eksik üretir.

Kavramanın Kendi Kapsamı

Son iki satır Python’a özgü bir ayrıntıyı ölçüyor. Kavramanın döngü değişkeni dışarıdaki aynı adlı değişkeni ezmiyor: kavramadan sonra i hâlâ kendi değerini taşıyor, kavrama ise kendi i adıyla [0, 1, 4, 9] üretiyor.

Bunun nedeni kavramanın kendi kapsamına sahip olmasıdır. Python Temelleri kursunun kapsam dersi adın hangi düzeyde arandığını kurmuştu; burada eklenen tek şey kavramanın bu düzeylerden birini kendisi açtığıdır. Döngü adı kavramanın içinde doğar ve orada ölür.

Sıradan bir for döngüsünde durum böyle değildir: orada döngü değişkeni çevreleyen kapsama yazılır ve döngü bittikten sonra da yaşar. Aynı işi yapan iki yazımdan biri ad bırakıyor, öbürü bırakmıyor. Bu, kavramanın yalnız bir kısaltma olmadığını gösterir — ad görünürlüğü bakımından ayrı bir düzenektir.

Özet

  • Aynı bin öğelik kaynaktan liste kavraması 1000, küme ve sözlük kavraması 100 öğe tutar; paylaşılan Oge sayısı sırasıyla 1000, 0 ve 100’dür. Kap ne kadarını saklayacağına kendisi karar verir ve eleme sessizdir.
  • Süzme koşulu üretimden önce yazılabiliyorsa var edilen 5’te kalır; yalnız üretilmiş nesneye bakılabiliyorsa 10 olur. Tutulan iki yazımda da 5’tir.
  • Öğe ifadesinin içindeki koşullu ifade bir süzgeç değildir: hiçbir turu elemez, 10 üretir ve 10 tutar.
  • İç içe kavrama on iki öğe için 5 kap kurar, yan yana iki for aynı öğeleri 1 kapla verir; önce kurup sonra düzleştirmek 6 kap eder ve dördü ara nesnedir.
  • Kavramanın kendi kapsamı vardır: döngü adı dışarıdaki aynı adlı değişkeni ezmez ve kavrama bitince kaybolur; sıradan for döngüsünde ad geride kalır.
  • Yan yana iki for yazımında sağdaki kaynak her dış turda yeniden gezilir: liste 6 öğe verir, aynı yazımda üreteç ifadesi 2. Tükenmiş akış sessizce boş davrandığı için eksik sonuç istisna üretmez.

Sonraki Adım

Bu derste süzme koşulu da öğe ifadesi de kavramanın içine yazıldı; her ikisi de yazıldığı yere gömülü kaldı ve başka bir kavramada yeniden kullanılamadı. Oysa ikisi de bir kural ifade ediyor — “şunu geçir”, “şunu şuna dönüştür” — ve kurallar adlandırılabilir. Sonraki ders kuralı bir nesne olarak ele alır: bir işlev bir başka işleve argüman olarak geçirilebilir mi, ve geçirildiğinde kaç kez çağrılır? Sıralamada anahtar hesabı öğe başına bir kez mi yapılır, yoksa her karşılaştırmada yeniden mi?

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat