Ruby'de tekrar için iki yol vardır: while, until, for gibi döngü anahtar kelimeleri ve each, times, upto, loop gibi blok alan metotlar. Günlük Ruby kodunda çoğunlukla ikinci yol kullanılır: bir koleksiyonu dolaşmak için each, belirli sayıda tekrar için times, sonsuz döngü için loop. Döngüden çıkmak için break, sonraki tura geçmek için next, aynı turu yeniden çalıştırmak için redo kullanılır.
Bu yazıda her döngü biçiminin ne zaman çalıştığını, ne döndürdüğünü ve kapsam farklarını Control Expressions, Integer ve Kernel belgeleri temelinde, Ruby 4.0 ile çalıştırılmış örneklerle anlatıyoruz. Serinin diğer konuları için Ruby öğrenme rehberi sayfasına bakın.
Döngü biçimlerinin karşılaştırması

| Yapı | Ne zaman çalışır | Dönüş değeri | Yeni kapsam açar mı |
|---|---|---|---|
| while koşul | Koşul doğru oldukça | nil (break ile değer verilebilir) | Hayır |
| until koşul | Koşul yanlış oldukça | nil (break ile değer verilebilir) | Hayır |
| for x in liste | Her eleman için | Dolaşılan nesne | Hayır |
| liste.each | Her eleman için | Alıcı nesne | Evet (blok) |
| n.times | n kez | n | Evet (blok) |
| a.upto(b) | a dan b ye kadar | a | Evet (blok) |
| loop | break gelene kadar | break değeri veya StopIteration sonucu | Evet (blok) |
while ve until
while, koşul doğru olduğu sürece gövdeyi tekrarlar; koşul döngüye girmeden önce ve her turdan sonra sınanır. until bunun tersidir: koşul yanlış olduğu sürece çalışır. Her ikisinde de do anahtar kelimesi isteğe bağlıdır. Belgelere göre bu döngülerin değeri, break ile bir değer verilmedikçe nil'dir.
i = 1
while i <= 3
puts "Tur #{i}"
i += 1
end
# Çıktı:
# Tur 1
# Tur 2
# Tur 3
geri = 3
until geri.zero?
puts geri
geri -= 1
end
# Çıktı:
# 3
# 2
# 1while ve until da tek satırlık modifier biçimde yazılabilir. Gövdenin koşuldan önce en az bir kez çalışmasını istiyorsanız begin ... end while kullanılır; burada koşul ancak ilk turdan sonra sınanır:
n = 1
n *= 2 while n < 100
p n # çıktı: 128
deneme = 10
begin
puts "en az bir kez çalıştı" # çıktı: en az bir kez çalıştı
deneme += 1
end while deneme < 5for ... in
for döngüsü, verilen nesne üzerinde each ile dolaşır. Belgelerin belirttiği önemli fark, for döngüsünün each bloğu gibi yeni bir kapsam açmamasıdır: döngü değişkeni ve gövdede tanımlanan değişkenler döngü bittikten sonra da erişilebilir kalır. Belgeler ayrıca for döngüsünün modern Ruby programlarında nadiren kullanıldığını not eder.
for meyve in ["elma", "armut"]
puts meyve
end
# Çıktı:
# elma
# armut
p meyve # çıktı: "armut"
["elma", "armut"].each do |m|
ic = m.upcase
end
p defined?(m) # çıktı: nil
p defined?(ic) # çıktı: nilBu kapsam farkı, döngü değişkeninin yanlışlıkla dışarıdaki bir değişkeni ezmesine yol açabilir. Blok kapsamının ayrıntıları değişkenler yazısında anlatılıyor.
each ve diğer yineleyici metotlar

Ruby'de bir koleksiyonu dolaşmanın olağan yolu each metodudur. Dizide her elemanı, hash'te her anahtar-değer çiftini bloğa verir. Sıra numarası da gerekiyorsa each_with_index kullanılır. Bloklar ve yield konusunun ayrıntıları Ruby blok ve yield yazısında.
fiyatlar = { elma: 30, armut: 45 }
fiyatlar.each do |urun, fiyat|
puts "#{urun}: #{fiyat} TL"
end
# Çıktı:
# elma: 30 TL
# armut: 45 TL
%w[a b c].each_with_index do |harf, i|
puts "#{i}=#{harf}"
end
# Çıktı:
# 0=a
# 1=b
# 2=ctimes, upto, downto ve step
Belirli sayıda tekrar için Integer sınıfının metotları kullanılır. times bloğu 0'dan n - 1'e kadar sayılarla çağırır. upto yukarı, downto aşağı doğru sayar. Numeric#step ise by: ile adım, to: ile sınır alır. Bu metotların hepsi blok verilmeden çağrıldığında bir Enumerator döndürür; bu nesneler Ruby Enumerator ve lazy yazısında ele alınıyor.
p 3.times.to_a # çıktı: [0, 1, 2]
kareler = []
1.upto(4) { |n| kareler << n * n }
p kareler # çıktı: [1, 4, 9, 16]
geri = []
5.downto(3) { |n| geri << n }
p geri # çıktı: [5, 4, 3]
adimlar = []
0.step(by: 25, to: 100) { |n| adimlar << n }
p adimlar # çıktı: [0, 25, 50, 75, 100]
p 5.upto(4) { |n| raise "çalışmaz" } # çıktı: 5Son satır belgelerdeki bir ayrıntıyı gösteriyor: başlangıç sınırdan büyükse upto bloğu hiç çağırmaz ve yalnızca alıcıyı döndürür. Aralıklar da ((1..5).each) aynı işi görebilir; Ruby Range yazısına bakın.
loop: sonsuz döngü
Kernel#loop verilen bloğu durmadan tekrarlar; döngüden break ile çıkılır. break bir değer alırsa loop ifadesinin sonucu o değer olur. Belgelere göre blok içinde StopIteration istisnası yükseltilirse döngü sessizce biter ve loop istisnada saklanan sonucu döndürür; bu, harici bir Enumerator üzerinde next ile ilerlerken döngünün kendiliğinden durmasını sağlar.
sayi = 0
sonuc = loop do
sayi += 1
next if sayi.odd?
break sayi * 10 if sayi > 5
end
p sonuc # çıktı: 60
harfler = %w[x y].each
donen = loop do
puts harfler.next
end
# Çıktı:
# x
# y
p donen # çıktı: ["x", "y"]İkinci örnekte harfler.next üçüncü çağrıda StopIteration yükseltir; loop bunu yakalar ve dizinin each metodunun dönüş değerini, yani dizinin kendisini döndürür.
break, next ve redo
break döngüyü veya bloğu hemen bitirir ve isteğe bağlı bir değer döndürür. next o turun geri kalanını atlar; bir değer verilirse bu değer o turun blok sonucu olur, bu da map gibi metotlarda o elemanın yeni değerini belirler. redo ise koşulu yeniden sınamadan aynı turu baştan çalıştırır; kontrolsüz kullanılırsa sonsuz döngüye yol açabileceği için sınır koymak gerekir.
ilk_cift = [3, 7, 8, 11, 12].each do |n|
break n if n.even?
end
p ilk_cift # çıktı: 8
sonuc = [1, 2, 3, 4].map do |n|
next 0 if n.even?
n * n
end
p sonuc # çıktı: [1, 0, 9, 0]
denemeler = 0
[1, 2, 3].each do |n|
denemeler += 1
redo if n == 2 && denemeler < 4
puts "#{n} işlendi, toplam deneme: #{denemeler}"
end
# Çıktı:
# 1 işlendi, toplam deneme: 1
# 2 işlendi, toplam deneme: 4
# 3 işlendi, toplam deneme: 5Belgelere göre eski Ruby 1.8'de redo yerine retry da kullanılabiliyordu; bugün rescue dışında retry yazmak SyntaxError verir. retry kullanımı Ruby hata yönetimi yazısında anlatılıyor.
İç içe döngülerden çıkmak: catch ve throw
break yalnızca içinde bulunduğu döngüyü veya bloğu bitirir. İç içe birden fazla döngüden tek seferde çıkmak için catch ve throw kullanılabilir. Belgelere göre bu ikili, istisnalardan farklı olarak beklenen akış değişiklikleri için tasarlanmıştır; throw ile verilen ikinci değer catch bloğunun sonucu olur.
bulunan = catch(:bulundu) do
[[1, 2], [3, 4], [5, 6]].each do |satir|
satir.each do |hucre|
throw :bulundu, hucre if hucre > 3
end
end
nil
end
p bulunan # çıktı: 4Döngü değerleri
Ruby'de döngüler de birer ifadedir. Yukarıdaki tablodaki dönüş değerleri pratikte şöyle görünür; özellikle each metodunun alıcıyı döndürmesi, ardından başka bir metot zincirlemenize imkân verir:
p [1, 2].each { |x| x * 10 } # çıktı: [1, 2]
p 3.times { |i| i } # çıktı: 3
r = for k in [7, 8] do end
p r # çıktı: [7, 8]
w = while false do end
p w # çıktı: nilSık yapılan hatalar

- Sayacı artırmayı unutmak:
whileveuntilkoşulu hiç değişmezse döngü bitmez. Ctrl+C ile programı durdurabilirsiniz. fordöngüsünün değişkeni sızdırdığını bilmemek: döngü değişkeni döngüden sonra da yaşar ve dışarıdaki aynı adlı değişkenin üzerine yazar.do ... endbloğunu argümansızpveyaputsile kullanmak: blok yanlış metoda bağlanır; parantez veya süslü parantez kullanın.breakile tüm iç içe döngülerden çıkılacağını sanmak: yalnızca en içteki döngü biter; dıştaki döngüler içincatch/throwveya bir metot içindereturnkullanın.- Sınırsız
redo: koşulu yeniden sınamadığı için kolayca sonsuz döngü üretir.
Sık Sorulan Sorular
Ruby'de for mu each mi kullanmalıyım?
Belgeler for döngüsünün modern Ruby kodunda nadiren kullanıldığını belirtir. each blok kapsamı sayesinde değişken sızdırmaz ve diğer koleksiyon metotlarıyla aynı yazım biçimini paylaşır; bu yüzden genellikle tercih edilir.
Ruby'de do-while döngüsü var mı?
Ayrı bir do-while anahtar kelimesi yoktur, ancak begin ... end while koşul biçimi gövdeyi koşuldan önce bir kez çalıştırır ve aynı işi görür. Alternatif olarak loop içinde sonda break unless koşul yazabilirsiniz.
break ile return arasındaki fark nedir?
break içinde bulunduğu döngüyü veya bloğu bitirir ve kod döngüden sonraki satırdan devam eder. return ise içinde bulunduğu metodun tamamından çıkar. Bloklarda ve lambdalarda return davranışı farklılaşır; bu konu Ruby proc ve lambda farkı yazısında.
Döngüde sıra numarasına nasıl ulaşırım?
each_with_index bloğa eleman ile birlikte sıfırdan başlayan sırayı verir. Dönüşüm yaparken sıraya ihtiyacınız varsa each_with_index.map veya map.with_index kullanılabilir; bu zincirlerin nasıl çalıştığı Enumerator yazısında açıklanıyor.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Ruby öğrenme rehberi · Ruby if, unless ve case · Ruby blok ve yield · Ruby Enumerable · Ruby Enumerator ve lazy