Go'da bir koleksiyonu gezmenin tek yolu uzun süre for ... range idi ve bu yalnızca dilimler, diziler, haritalar, kanallar ve stringler için çalışıyordu. Kendi veri yapını yazdığında — bir ağaç, bir bağlı liste, sayfalanmış bir API istemcisi — onu gezmek için ya tüm elemanları bir dilime toplaman ya da geri çağırım (callback) alan bir metot sunman gerekiyordu. İlki büyük koleksiyonlarda belleği şişiriyor, ikincisi ise break ve continue kullanmayı imkânsız kılıyordu.
Go 1.23 bu boşluğu range over func ile doldurdu: Artık belirli imzalara sahip fonksiyonlar da range ile gezilebiliyor. Bu, dile eklenmiş küçük ama çok şey değiştiren bir özelliktir. Sonucunda slices ve maps paketleri iteratör döndüren fonksiyonlarla genişledi, standart kütüphane genelinde tembel (lazy) gezinme mümkün hâle geldi ve kendi tiplerin de yerleşik koleksiyonlarla aynı biçimde gezilebilir oldu.
Bu derste iteratör fonksiyonlarının imzalarını, yield fonksiyonunun nasıl çalıştığını ve erken çıkışın nasıl ele alındığını, iter.Seq ile iter.Seq2 arasındaki farkı, standart kütüphanenin hazır iteratörlerini, kendi tipin için iteratör yazmayı ve çekmeli (pull) iteratörleri öğreneceksin.
range over func nedir?
range üç fonksiyon imzasını kabul eder:
Buradaki yield, derleyicinin senin için ürettiği bir fonksiyondur. İteratörün her eleman için yield çağırması, o elemanın döngü gövdesine verilmesi demektir. yield false döndürürse döngü erken sonlanmıştır (break, return veya bir hata) ve iteratörün derhal durması gerekir.
package main
import (
"fmt"
"iter"
)
// Countdown, n'den 1'e kadar sayan bir iteratör döndürür.
func Countdown(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := n; i > 0; i-- {
if !yield(i) { // tüketici vazgeçti
return
}
}
}
}
// Enumerate, elemanları sıra numaralarıyla birlikte verir.
func Enumerate[T any](items []T) iter.Seq2[int, T] {
return func(yield func(int, T) bool) {
for i, v := range items {
if !yield(i+1, v) {
return
}
}
}
}
func main() {
for n := range Countdown(5) {
fmt.Print(n, " ")
}
fmt.Println()
// break, yield'in false döndürmesine yol açar
for n := range Countdown(100) {
if n < 97 {
break
}
fmt.Print(n, " ")
}
fmt.Println()
for i, name := range Enumerate([]string{"Ali", "Ayşe", "Can"}) {
fmt.Printf("%d. %s\n", i, name)
}
}5 4 3 2 1 100 99 98 97 1. Ali 2. Ayşe 3. Can
Dikkat edilecek nokta şudur: Countdown(5) çağrısı hiçbir şey hesaplamaz; yalnızca bir fonksiyon döndürür. Gerçek iş, range döngüsü çalışmaya başladığında yapılır. Bu tembellik, sonsuz dizileri bile ifade edebilmeni sağlar — tüketici break dediğinde üretim durur.
yield sözleşmesi
İteratör yazarken uyman gereken kurallar kısadır ama ihmal edilirse sessiz hatalara yol açar.
yield false döndürdüğünde hemen dön. Devam edersen tüketici artık dinlemiyor olmasına rağmen iş yapmaya devam edersin; daha kötüsü, bazı durumlarda çalışma zamanı panik oluşturur.
yield'i döngüden sonra çağırma. İteratör fonksiyonu döndükten sonra yield çağrılamaz; bunu yapan kod panikler.
Kaynakları defer ile temizle. İteratörün bir dosya, bağlantı veya kilit tutuyorsa, erken çıkışta da serbest bırakıldığından emin ol. defer, erken çıkış dâhil her yolda çalışır.
package main
import (
"fmt"
"iter"
"strings"
)
// Lines, metni satır satır verir ve kapanışta temizlik yaptığını raporlar.
func Lines(text string, log *[]string) iter.Seq[string] {
return func(yield func(string) bool) {
*log = append(*log, "iteratör başladı")
defer func() { *log = append(*log, "iteratör temizlendi") }()
for line := range strings.SplitSeq(text, "\n") {
if line == "" {
continue
}
if !yield(line) {
return // erken çıkış: defer yine de çalışır
}
}
}
}
func main() {
text := "birinci\nikinci\n\nüçüncü\ndördüncü"
var log []string
count := 0
for line := range Lines(text, &log) {
count++
fmt.Println(count, "→", line)
if count == 2 {
break // erken çıkış
}
}
fmt.Println("okunan satır:", count)
fmt.Println("günlük:", strings.Join(log, " | "))
}1 → birinci 2 → ikinci okunan satır: 2 günlük: iteratör başladı | iteratör temizlendi
strings.SplitSeq fonksiyonuna dikkat et: Go 1.24 ile gelen bu fonksiyon, strings.Split gibi bir dilim ayırmaz; bir iteratör döndürür. Büyük metinlerde tüm parçaları bellekte tutmadan gezinmeni sağlar. Aynı mantıkla strings.FieldsSeq de eklendi.
Standart kütüphanedeki iteratörler
slices ve maps paketleri, iteratör üreten ve iteratör tüketen fonksiyonlarla genişledi. En sık kullanacakların şunlardır:
package main
import (
"fmt"
"maps"
"slices"
"strings"
)
func main() {
words := []string{"kiraz", "elma", "armut", "zeytin"}
// Sondan başa gezinme: ayrı bir ters çevirme gerekmez
for i, w := range slices.Backward(words) {
fmt.Printf("%d:%s ", i, w)
}
fmt.Println()
// Map anahtarlarını sıralı dilime çevirme: en yaygın kullanım
stock := map[string]int{"elma": 12, "armut": 7, "kiraz": 30}
keys := slices.Sorted(maps.Keys(stock))
fmt.Println("sıralı anahtarlar:", keys)
// Değerleri toplayıp sıralama
amounts := slices.Sorted(maps.Values(stock))
fmt.Println("sıralı miktarlar:", amounts)
// Özel ölçütle sıralama: miktara göre azalan
type entry struct {
name string
count int
}
var entries []entry
for k, v := range maps.All(stock) {
entries = append(entries, entry{k, v})
}
slices.SortFunc(entries, func(a, b entry) int { return b.count - a.count })
fmt.Println("miktara göre:", entries)
// İteratörü dilime toplama
upper := slices.Collect(func(yield func(string) bool) {
for _, w := range words {
if !yield(strings.ToUpper(w)) {
return
}
}
})
fmt.Println("büyük harf:", upper)
}3:zeytin 2:armut 1:elma 0:kiraz
sıralı anahtarlar: [armut elma kiraz]
sıralı miktarlar: [7 12 30]
miktara göre: [{kiraz 30} {elma 12} {armut 7}]
büyük harf: [KIRAZ ELMA ARMUT ZEYTIN]slices.Sorted(maps.Keys(m)) deyimi, Go kodunda artık standart hâline geldi. Haritayı deterministik sırayla gezmenin en kısa yoludur ve önceki yöntemin (anahtarları elle toplayıp sort.Strings çağırmanın) yerini almıştır.
Kendi tipin için iteratör yazmak
İteratörlerin asıl değeri kendi veri yapılarında ortaya çıkar. Bir ağacı veya bağlı listeyi, tüm elemanları kopyalamadan gezdirebilirsin:
package main
import (
"fmt"
"iter"
"slices"
)
type Node struct {
Value int
Left, Right *Node
}
type Tree struct{ root *Node }
func (t *Tree) Insert(v int) {
t.root = insert(t.root, v)
}
func insert(n *Node, v int) *Node {
if n == nil {
return &Node{Value: v}
}
if v < n.Value {
n.Left = insert(n.Left, v)
} else if v > n.Value {
n.Right = insert(n.Right, v)
}
return n
}
// InOrder, ağacı küçükten büyüğe gezer. Elemanlar kopyalanmaz.
func (t *Tree) InOrder() iter.Seq[int] {
return func(yield func(int) bool) {
var walk func(*Node) bool
walk = func(n *Node) bool {
if n == nil {
return true
}
return walk(n.Left) && yield(n.Value) && walk(n.Right)
}
walk(t.root)
}
}
// Leaves, yalnızca yaprak düğümleri verir.
func (t *Tree) Leaves() iter.Seq[int] {
return func(yield func(int) bool) {
var walk func(*Node) bool
walk = func(n *Node) bool {
if n == nil {
return true
}
if n.Left == nil && n.Right == nil {
return yield(n.Value)
}
return walk(n.Left) && walk(n.Right)
}
walk(t.root)
}
}
func main() {
var t Tree
for _, v := range []int{50, 30, 70, 20, 40, 60, 80} {
t.Insert(v)
}
for v := range t.InOrder() {
fmt.Print(v, " ")
}
fmt.Println()
fmt.Println("yapraklar:", slices.Collect(t.Leaves()))
// Erken çıkış: 50'yi görünce dur
var seen []int
for v := range t.InOrder() {
if v > 45 {
break
}
seen = append(seen, v)
}
fmt.Println("45'ten küçükler:", seen)
}20 30 40 50 60 70 80 yapraklar: [20 40 60 80] 45'ten küçükler: [20 30 40]
walk(n.Left) && yield(n.Value) && walk(n.Right) satırı, özyinelemeli iteratörlerin en zarif yazımıdır. Go'da && kısa devre yaptığı için, yield false döndürdüğü anda sağdaki çağrılar hiç yapılmaz ve özyineleme tüm katmanlardan geriye doğru çözülür. Erken çıkış böylece tek bir satırla doğru biçimde ele alınmış olur.
Ağaçları ve gezinme türlerini Ağaçlar dersinde ayrıntısıyla göreceksin.
Pull iteratörleri: iter.Pull
Şimdiye kadar gördüğün iteratörler itmeli (push) iteratörlerdir: Kontrol iteratördedir, o da elemanları yield ile gövdeye iter. Bazen bunun tersine ihtiyaç duyarsın — elemanları sen istediğinde almak. İki diziyi paralel gezmek, birleştirme (merge) yapmak veya bir eleman "ileriye bakıp" karar vermek istediğinde itmeli model yetmez.
iter.Pull, itmeli bir iteratörü çekmeli (pull) bir iteratöre çevirir:
package main
import (
"fmt"
"iter"
"slices"
)
// Merge, iki sıralı diziyi tek bir sıralı diziye birleştirir.
func Merge(a, b []int) []int {
nextA, stopA := iter.Pull(slices.Values(a))
defer stopA()
nextB, stopB := iter.Pull(slices.Values(b))
defer stopB()
var out []int
va, okA := nextA()
vb, okB := nextB()
for okA && okB {
if va <= vb {
out = append(out, va)
va, okA = nextA()
} else {
out = append(out, vb)
vb, okB = nextB()
}
}
for okA {
out = append(out, va)
va, okA = nextA()
}
for okB {
out = append(out, vb)
vb, okB = nextB()
}
return out
}
func main() {
a := []int{1, 4, 7, 9}
b := []int{2, 3, 8, 10, 12}
fmt.Println(Merge(a, b))
// Pull ile elemanları tek tek almak
next, stop := iter.Pull(slices.Values([]string{"bir", "iki", "üç"}))
defer stop()
v1, _ := next()
v2, _ := next()
fmt.Println("ilk iki:", v1, v2)
v3, ok3 := next()
_, ok4 := next()
fmt.Println("üçüncü:", v3, ok3, "| dördüncü var mı:", ok4)
}[1 2 3 4 7 8 9 10 12] ilk iki: bir iki üçüncü: üç true | dördüncü var mı: false
iter.Pull bir next ve bir stop fonksiyonu döndürür. next her çağrıldığında bir sonraki elemanı ve "eleman var mı" bilgisini verir. stop, iteratörü erken sonlandırmak içindir ve mutlaka çağrılmalıdır — defer stop() yazmak standart alışkanlıktır; aksi hâlde arka planda tutulan goroutine sızabilir.
Pull iteratörleri güçlüdür ama daha pahalıdır; varsayılan olarak itmeli biçimi tercih et, ancak gerçekten ihtiyacın olduğunda çevir.
İteratörleri ne zaman kullanmalı?
İteratörler dile yeni katıldı ve her yerde kullanma eğilimi doğal olarak arttı. Oysa dilim döndürmek hâlâ çoğu durumda doğru cevaptır. Karar verirken şu ölçütlere bak.
Koleksiyon büyük mü ya da sınırsız mı? Milyonlarca satırlık bir dosyayı ya da sayfalanmış bir uzak servisi gezerken tüm elemanları belleğe almak makul değildir. Burada iteratör gerçek bir kazanç sağlar: bellek kullanımı sabit kalır ve ilk sonuç anında gelir.
Tüketici erken vazgeçebilir mi? Aradığını bulunca duracak bir arama, yalnızca ilk on sonucu isteyen bir sayfalama ya da bir koşul sağlandığında biten bir döngü, iteratörlerin parladığı yerlerdir. Dilim döndüren bir fonksiyon, tüketici ilk elemanda vazgeçse bile tüm işi yapmıştır.
Sonuç küçük ve tamamı zaten hesaplanıyor mu? Öyleyse dilim döndür. Dilim, indekslenebilir, uzunluğu bilinir, tekrar tekrar gezilebilir ve hata ayıklaması kolaydır. İteratör ise tek kullanımlıktır: İki kez gezmek istersen ya yeniden üretirsin ya da toplarsın.
API'nin kullanıcıları kim? İteratör döndüren bir fonksiyon, çağıranı slices.Collect çağırmaya zorlayabilir. Kütüphanenin tipik kullanımı "hepsini al ve işle" biçimindeyse, doğrudan dilim döndürmek çağıranın işini kolaylaştırır.
Pratik bir orta yol vardır: Veri yapın için hem iteratör hem toplayıcı sun. İteratör esnekliği, toplayıcı da kolaylığı sağlar; ikisi arasındaki dönüşüm tek satırdır.
Perde arkası ve maliyet
İteratörler dile eklenirken en çok tartışılan konu performanstı. Her eleman için bir fonksiyon çağrısı yapmak, doğrudan bir dilim gezmekten pahalı görünür. Gerçekte durum daha iyidir: Derleyici, iteratör fonksiyonunu ve döngü gövdesini çoğu zaman satır içine alabilir; bu durumda üretilen makine kodu elle yazılmış bir döngüye çok yaklaşır.
Yine de sihir beklememelisin. Gövde büyükse veya iteratör başka bir iteratörü sarmalıyorsa satır içine alma gerçekleşmeyebilir ve her eleman için gerçek bir çağrı maliyeti doğar. Çok sıcak döngülerde — saniyede milyonlarca eleman işleyen kodlarda — bu fark ölçülebilir hâle gelir. Böyle yerlerde en iyi yaklaşım, önce okunabilir hâli yazıp sonra ölçmektir; erken iyileştirme burada da kötü bir fikirdir.
İkinci bir ayrıntı, çekmeli iteratörlerin maliyetiyle ilgilidir. İtmeli bir iteratörü çekmeliye çevirmek, kontrolü iki taraf arasında gidip gelen bir yapıya dönüştürür ve bu, çalışma zamanının özel desteğini gerektirir. Basit bir dilimi tek tek gezmek için bu dönüşümü yapmak israftır; doğrudan indeksle gezmek çok daha ucuzdur. Çekmeli biçimi yalnızca iki kaynağı paralel ilerletmek gibi başka türlü yazılamayan durumlarda kullan.
Son olarak hata ayıklamayı düşün. Bir iteratör zinciri — süz, dönüştür, sınırla — okunması hoş bir kod üretir ama yığın izleri (stack trace) daha derin ve daha soyut olur. Zincirin ortasında bir sorun çıktığında hangi aşamanın sorumlu olduğunu bulmak, sıradan bir döngüye göre biraz daha uğraştırır. Bu, iteratörlerden kaçınmak için bir gerekçe değil; zinciri makul uzunlukta tutmak için bir hatırlatmadır.
Sık yapılan hatalar
yieldfalsedöndürdüğünde durmamak. Erken çıkış sinyalini yok saymak, gereksiz iş ve bazen panik demektir.- İteratör döndükten sonra
yieldçağırmak. Panik oluşturur;yield'i yalnızca iteratör gövdesi çalışırken kullan. iter.Pull'dan dönenstop'u çağırmamak. Kaynak ve goroutine sızabilir;defer stop()yaz.- İteratörü iki kez gezmeye çalışmak. Çoğu iteratör tek kullanımlıktır; tekrar gerekiyorsa
slices.Collectile dilime çevir. - Küçük sonuçlar için gereksiz yere iteratör döndürmek. Dilim daha kullanışlıdır.
- İteratör içinde kaynağı
deferile kapatmayı unutmak. Erken çıkışta dosya veya bağlantı açık kalır. maps.Keys'in dilim döndürdüğünü sanmak. İteratör döndürür; dilim içinslices.Collectveyaslices.Sortedgerekir.
Alıştırmalar
Bir iter.Seq[int] alıp yalnızca koşulu sağlayan elemanları veren bir Filter fonksiyonu yaz. Ardından 1–20 arasındaki sayılardan 3'e bölünenleri süzüp yazdır ve slices.Collect ile dilime çevir.
İpucu
Filter, yeni bir iter.Seq[int] döndürmelidir. İçeride kaynağı gezerken koşulu sağlayanları yield et ve yield false dönerse hemen çık.
Çözümü göster
package main
import (
"fmt"
"iter"
"slices"
)
func Filter[T any](seq iter.Seq[T], keep func(T) bool) iter.Seq[T] {
return func(yield func(T) bool) {
for v := range seq {
if !keep(v) {
continue
}
if !yield(v) {
return
}
}
}
}
func Range(from, to int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := from; i <= to; i++ {
if !yield(i) {
return
}
}
}
}
func main() {
divisibleByThree := Filter(Range(1, 20), func(n int) bool { return n%3 == 0 })
for n := range divisibleByThree {
fmt.Print(n, " ")
}
fmt.Println()
// İteratör tek kullanımlık değildir: Range her seferinde baştan üretir
collected := slices.Collect(Filter(Range(1, 20), func(n int) bool { return n%3 == 0 }))
fmt.Println("dilim:", collected, "uzunluk:", len(collected))
}3 6 9 12 15 18 dilim: [3 6 9 12 15 18] uzunluk: 6
Filter tembeldir: Hiçbir eleman, tüketici istemeden hesaplanmaz. Bu yüzden sonsuz bir kaynakla bile güvenle kullanılabilir. Bu örnekteki Range fonksiyonu yeniden çağrıldığı için iki kez gezilebildi; genel olarak bir iteratör değerini ikinci kez gezebileceğini varsayma.
Bir metindeki kelimelerin geçiş sayısını hesapla ve sonucu önce alfabetik, sonra frekansa göre azalan biçimde yazdır. strings.FieldsSeq, maps.All ve slices.SortedFunc kullan.
İpucu
maps.Keys bir iteratör döndürür; slices.Sorted ile sıralı dilime çevirebilirsin. Frekansa göre sıralamak için önce çiftleri bir dilime toplamalısın.
Çözümü göster
package main
import (
"fmt"
"maps"
"slices"
"strings"
)
func main() {
text := "go hızlı go basit go güçlü basit dil go"
counts := make(map[string]int)
for word := range strings.FieldsSeq(text) {
counts[strings.ToLower(word)]++
}
// 1) Alfabetik
fmt.Println("alfabetik:")
for _, w := range slices.Sorted(maps.Keys(counts)) {
fmt.Printf(" %-8s %d\n", w, counts[w])
}
// 2) Frekansa göre azalan, eşitlikte alfabetik
type pair struct {
word string
count int
}
var pairs []pair
for w, c := range maps.All(counts) {
pairs = append(pairs, pair{w, c})
}
slices.SortFunc(pairs, func(a, b pair) int {
if a.count != b.count {
return b.count - a.count
}
return strings.Compare(a.word, b.word)
})
fmt.Println("frekansa göre:")
for _, p := range pairs {
fmt.Printf(" %-8s %d\n", p.word, p.count)
}
}alfabetik: basit 2 dil 1 go 4 güçlü 1 hızlı 1 frekansa göre: go 4 basit 2 dil 1 güçlü 1 hızlı 1
strings.FieldsSeq, metni boşluklara göre ayırırken ara dilim oluşturmaz; çok büyük metinlerde bu fark önemlidir. Sıralamada eşitlik durumunu da tanımlamak kritiktir: Yalnızca frekansa göre sıralasaydın aynı frekanslı kelimelerin sırası belirsiz kalır ve çıktı her çalıştırmada değişebilirdi.
Sayfa sayfa veri döndüren bir kaynağı (uzak bir API'yi taklit eden bir fonksiyon) tek bir düz iteratör gibi gösteren AllItems fonksiyonu yaz. Tüketici istediği kadar eleman alabilsin; yalnızca gerçekten gerekli sayfalar "çekilsin". Kaç sayfa çekildiğini raporla.
İpucu
İteratör gövdesi, gerektikçe bir sonraki sayfayı isteyen bir döngü olmalı. yield false döndüğünde yeni sayfa istememelisin — kazanç tam olarak budur.
Çözümü göster
package main
import (
"fmt"
"iter"
"slices"
)
// fetchPage, uzak bir servisi taklit eder: her sayfa 3 eleman döndürür.
func fetchPage(page int, fetched *int) ([]string, bool) {
*fetched++
if page > 4 {
return nil, false
}
items := make([]string, 0, 3)
for i := range 3 {
items = append(items, fmt.Sprintf("kayıt-%d", (page-1)*3+i+1))
}
return items, true
}
func AllItems(fetched *int) iter.Seq[string] {
return func(yield func(string) bool) {
for page := 1; ; page++ {
items, ok := fetchPage(page, fetched)
if !ok {
return
}
for _, item := range items {
if !yield(item) {
return // tüketici vazgeçti: yeni sayfa çekilmez
}
}
}
}
}
func main() {
// 1) Yalnızca ilk 4 eleman: 2 sayfa yetmeli
fetched := 0
var first []string
for item := range AllItems(&fetched) {
first = append(first, item)
if len(first) == 4 {
break
}
}
fmt.Println("ilk dört:", first)
fmt.Println("çekilen sayfa sayısı:", fetched)
// 2) Hepsi: son boş sayfa da istenir
fetched = 0
all := slices.Collect(AllItems(&fetched))
fmt.Println("toplam eleman:", len(all))
fmt.Println("çekilen sayfa sayısı:", fetched)
fmt.Println("son eleman:", all[len(all)-1])
}ilk dört: [kayıt-1 kayıt-2 kayıt-3 kayıt-4] çekilen sayfa sayısı: 2 toplam eleman: 12 çekilen sayfa sayısı: 5 son eleman: kayıt-12
Bu, iteratörlerin en ikna edici kullanım alanıdır. Eski yaklaşımda AllItems bir dilim döndürseydi, tüketici yalnızca dört eleman istese bile tüm sayfalar çekilirdi: gereksiz ağ trafiği, gereksiz bekleme, gereksiz bellek. İteratörle ise yalnızca gerçekten ihtiyaç duyulan sayfalar istenir ve bu karar tüketiciye aittir.
Gerçek bir istemcide bu fonksiyona bir context.Context de geçirilir; böylece istek iptal edildiğinde devam eden sayfa çağrısı da durdurulabilir. İptal desenlerini Context ve Eşzamanlılık Desenleri dersinde bulabilirsin.
Kısa sınav
Bir iteratör fonksiyonunda yield false döndürdüğünde ne yapmalısın?
iter.Seq[V] hangi fonksiyon imzasının takma adıdır?
maps.Keys(m) ne döndürür?
iter.Pulldan dönen stop fonksiyonu neden çağrılmalıdır?
Hangi durumda dilim döndürmek iteratörden daha uygundur?
Özyinelemeli bir ağaç iteratöründe walk(n.Left) && yield(n.Value) && walk(n.Right) yazımı neden doğrudur?
Özet
- Go 1.23'ten beri belirli imzalara sahip fonksiyonlar
rangeile gezilebilir:iter.Seq[V]veiter.Seq2[K, V]. yieldderleyicinin ürettiği bir fonksiyondur;falsedöndürdüğünde iteratör hemen dönmelidir.- İteratörler tembeldir: İş, döngü çalışırken yapılır. Sonsuz diziler ve erken çıkış doğal biçimde desteklenir.
- Kaynak tutan iteratörlerde temizliği
deferile yap; erken çıkışta da çalışır. slices.All/Values/Backward/Collect/Sortedvemaps.Keys/Values/Allgünlük kodda en çok kullanacağın iteratör araçlarıdır.slices.Sorted(maps.Keys(m)), haritayı deterministik sırayla gezmenin standart deyimidir.- Özyinelemeli iteratörlerde
&&zinciri erken çıkışı tüm katmanlara doğru yayar. iter.Pullitmeli iteratörü çekmeli hâle getirir;stopmutlaka çağrılmalıdır.- Küçük ve tamamı hesaplanan sonuçlarda dilim, büyük veya sınırsız kaynaklarda iteratör tercih edilir.