Kontrol Yapıları
Önceki
İçindekiler
İçindekiler
Nesneye yöncelik düşünme
Sonraki

 Ruby Kullanıcı KılavuzuTekrarlayıcılar (Iterators) 

Tekrarlayıcılar sadece Ruby'ye özgü bir kavram değildir. Genel olarak çoğu nesneye yönelik programlama dilinde kullanımaktadır. Lisp'te de tekrarlayıcılar olarak adlandırılmasalar da kullanılmaktadır. Ancak tekrarlayıcı kavramı neredeyse her dilde değişik bir anlam kazandığı için önce bu kavramı daha detaylı anlatmaya çalışalım:

iterate sözcüğü aynı şeyi birçok kez tekrarlamak anlamına gelir.

Kod yazarken değişik durumlarda döngülere ihtiyacımız olur. C'de for ya da while kullanarak işimizi hallederiz. Örneğin,

char *str;
for (str = "abcdefg"; *str != '\0'; str++) {
  /* burada bir karakter işleyin */
}

C'nin for(...) dizimi döngünün yaratılmasında soyutlama sağlayarak yardımcı olsa da *str'nin bir boş bir karaktere karşı test edilmesi programcının dizgi yapısı hakkında daha detaylı bilgi bilmesini gerektirir. Bu C'nin düşük-seviyeli olduğunu hissettiren nedenlerden biridir. Yüksek seviyeli diller tekrarlayıcılara uyumluluklarıyla ün kazanmışlardır. Aşağıdaki sh kabuk betiğini göz önünde bulunduralım:

#!/bin/sh

for i in *.[ch]; do
  # ...  örnegin; her dosya icin yapilacak birkac işlem yer alabilir.
done

Bulunulan dizindeki tüm C kaynak ve başlık dosyaları çalıştırıldı ve komut satırı detayları tuttu. C'den daha yüksek seviyeli olduğunu düşünüyorum, öyle değil mi?

Ancak göz önüne alınması gereken başka bir nokta daha var: bir dilin gömülü veri yapıları için tekrarlayıcıleri desteklemesi güzel birşey olsa da, geri dönüp kendi veri yapılarımızı tekrarlatacak düşük seviyeli döngüler yazmak hayalkırıklığı yaratacak bir iş olacaktır. Nesneye yönelik programlamada, kullanıcılar çoğu kez ardı ardına veri tipleri tanımlarlar ve bu ciddi bir problem olabilir.

Her nesneye yönelik programlama dili iterasyonlar için kolaylıklar içerir. Bazı diller bu iş için özel sınıflar tanımlarken, Ruby tekrarlayıcıleri doğrudan tanımlamayı tercih eder.

Ruby'nin string tipi bazı yararlı tekrarlayıcılara sahiptir.

ruby> "abc".each_byte{|c| printf "<%c>", c}; print "\n"
<a><b><c>
   nil

each_byte, dizgideki her karakter için bir tekrarlayıcıdır. Her bir karakter yerel bir değişken olan c yerine konur. Bu daha çok C koduna benzeyen birşeyle açıklanabilir...

ruby> s="abc";i=0
   0
ruby> while i<s.length
    |    printf "<%c>", s[i]; i+=1
    | end; print "\n"
<a><b><c>
   nil

Buna rağmen each_byte tekrarlayıcısı hem kabul edilebilir bir basitlikte hem de String sınıfı radikal bir değişikliğe uğrarsa çalışmaya devam edilmesi hoş olacak bir tekrarlayıcıdır. Tekrarlayıcıların başka bir yayarı da değişiklere karşı sağlam durmasıdır ki bu da iyi bir kodun karakteristik özelliklerinden biridir (evet, sabırlı olun, sınıflar hakkında da konuşacağız.).

String'in başka bir tekrarlayıcısı da each_line'dır.

ruby> "a\nb\nc\n".each_line{|l| print l}
a
b
c
   nil

C'de satır sınırlayıcıları bulmak, alt dizgiler üretmek gibi güç işler tekrarlayıcılarla kolayca üstesinden gelinebilir.

Geçen bölümdeki for döngüsü, each operatörünü kullanarak tekrarlanmayı sağlamaktaydı. String'in each'i aynı each_line gibi görev görür, o yüzden yukarıdaki örneği for ile tekrar yazalım:

ruby> for l in "a\nb\nc\n"
    |   print l 
    | end
a
b
c
   nil

Bulunulan tekrarlamayı döngünün başından itibaren tekrar ettirmek için retry kontrol yapısını kullanabiliriz.

ruby> c=0
   0
ruby> for i in 0..4
    |   print i
    |   if i == 2 and c == 0
    |     c = 1
    |     print "\n"
    |     retry
    |   end
    | end; print "\n"
012
01234
   nil

Tekrarlayıcı tanımlamasında bazen yield'le karşılaşırız. yield, kontrolü tekrarlayıcıya parametre olarak geçilen kod bloğuna verir (bu konu prosedür nesneleri bölümünde daha detaylı anlatılacaktır). Aşağıdaki örnekte, argümanda verilen sayı kadar bir kod bloğunu tekrarlayan repeat tekrarlayıcısı tanımlanmıştır.

ruby> def repeat(num)
    |   while num > 0
    |     yield
    |     num -= 1
    |   end
    | end
   nil
ruby> repeat(3) { print "foo\n" }
foo
foo
foo
   nil

retry ile while gibi çalışan ancak hız açısından pek de pratik olmayan bir tekrarlayıcı tanımlayabiliriz.

ruby> def WHILE(cond)
    |   return if not cond
    |   yield
    |   retry
    | end
   nil
ruby> i=0; WHILE(i<3) { print i; i+=1 }
012   nil

Tekrarlayıcının ne olduğunu anladınız mı? Bir kaç kısıtlama hariç, kendi tekrarlayıcınızı yazabilirsiniz, aslında yeni bir veri tipi tanımladığınız zaman ona uygun bir tekrarlayıcı tanımlamanız da uygun olacaktır. Yukarıdaki örnekler pek de kullanışlı örnekler sayılmazlar. Sınıfları daha iyi anladığımızda daha pratik tekrarlayıcılar hakkında konuşabiliriz.


Kontrol Yapıları
Önceki
İçindekiler
İçindekiler
Nesneye yöncelik düşünme
Sonraki