16 Şubat 2025 Pazar

Ruby Temelleri 3

https://ujk-ujk.blogspot.com/2025/02/ruby-temelleri-3.html
İçindekiler +

    Merhaba, 3. bölümde sınıflarda kalıtım sistemi ile devam ediyoruz.


    Sınıflarda kalıtım

    -- Deyim yapısı

    class AltSınıf < ÜstSınıf
     
    end



    -- Alt sınıflar

    Kalıtım ile bir sınıftan ilave ya da değişmiş özellikleri olan bir alt sınıf üretilir. 

    class Hayvan
      def merhaba_de
        'Meep!'
      end
     
      def ye
        'Yumm!'
      end
    end
     
    class Köpek < Hayvan
      def merhaba_de
        'Hav!'
      end
    end
     
    köpük = Köpek.new
    köpük.merhaba_de   # 'Hav!'
    köpük.ye           # 'Yumm!'


    Bu örnekte Köpek sınıfı Hayvan sınıfından kalıtımla üretilmiş bir alt sınıftır. 

    Köpek sınıfı merhaba_de ve ye metodlarını Hayvan sınıfından kalıtımla alır. 

    Köpek sınıfı tanımında merhaba_de metodu üzerine yazılarak Hayvan sınıfına göre davranışı değiştirilmiştir.



    -- Çoklu kalıtım

    Ruby birkaç sınıftan alt sınıf oluşturmayı yapan çoklu kalıtımı desteklemez (bir sınıfın tek ebeveyni olur). Ancak modüller kullanarak ve bunları sınıflarda içererek karmaşık sınıflar oluşturulabilir. Bir örneğini burada yazmıştım.



    -- Kalıtmla ne geçer

    -- -- Metodlar kalıtımla geçer

    class A
      def boo; p 'boo' end
    end

    class B < A; end

    b = B.new
    b.boo # => 'boo'



    -- -- Sınıf metodları da kalıtımla geçer

    class A
      def self.boo; p 'boo' end
    end

    class B < A; end

    B.boo # => 'boo'



    -- -- Sabitler kalıtımla geçer

    class A
      WOO = 1
    end

    class B < A; end

    p B::WOO # => 1


    Ama dikkat edin üzerlerine yazılabilir.

    class A
      WOO = 1
    end

    class B < A
      WOO += 1
    end

    p A::WOO # => 1
    p B::WOO # => 2



    -- -- Oluşum değişkenleri kalıtımla geçer

    class A
      attr_accessor :ho
      def initialize
        @ho = 'haha'
      end
    end

    class B < A; end

    b = B.new
    p b.ho # => 'haha'


    Şu noktaya dikkat edin, eğer oluşum değişkenine ilk değeri veren metodun üstüne yazarsanız, ve değer de vermezseniz, oluşum değişkeni nil değeri olur. Yukarıdaki koda devam edelim.

    class C < A
      def initialize; end
     end

    c = C.new
    p c.ho    # => nil



    -- -- Sınıf oluşum değişkenleri kalıtımla geçmez

    Zaten sınıf oluşum değişkeninin yapılma amacı da bu olabilir, alt sınıflarına (çocuklarına) geçmez.

    class A
      @foo = 'foo'
      class << self
          attr_accessor :foo
      end
    end

    class B < A; end
    class C < A; end

    p B.foo # => nil

    # accessor sınıf metodu olduğu için
    # kalıtımla geçer
    B.foo = 'fob' # mümkün
    C.foo = 'foc' # mümkün

    p A.foo # => foo
    p B.foo # => fob
    p C.foo # => foc



    -- -- Sınıf değişkenleri gerçekte kalıtımla geçmez

    Sınıf değişkenleri ana sınıf ve tüm yavru sınıflarda tek bir değişkenmiş gibi paylaşılır. 

    class A
      @@foo = 0
      def initialize
          @@foo  += 1
          p @@foo
      end
    end

    class B < A;end
    class C < B;end

    a = A.new # => 1
    b = B.new # => 2
    c = C.new # => 3
    z = A.new # => 4



    -- Mixin'ler

    Mixin çoklu kalıtıma benzer etki elde etmek için en doğru yoldur. Modüller içindeki metodları sınıflarımıza aynı kalıtımla üst sınıf metodlarını aldığımız gibi alabiliriz. Bu metodlar oluşum ya da sınıf metodu olarak kullanılabilir. Aşağıdaki örnek bunu tasvir ediyor.

    module ÖrnekModül

      def self.included(base)
        base.extend SınıfMetodları
      end

      module SınıfMetodları

        def sınıf_metodu
          puts "Bu bir sınıf metodu"
        end

      end

      def oluşum_metodu
        puts "Bu bir oluşum metodu"
      end

    end

    class ÖrnekSınıf
      include ÖrnekModül
    end

    nesne1 = ÖrnekSınıf.new

    nesne1.oluşum_metodu

    # => "Bu bir oluşum metodu"

    nesne1.class.sınıf_metodu
    # ya da
    ÖrnekSınıf.sınıf_metodu
    # => "Bu bir sınıf metodu"



    -- Mevcut sınıfları kalıtım kullanacak şekilde düzenlemek

    Diyelim Kedi ve Köpek adlarında iki sınıfımız olsun.

    class Kedi
      def ye
        yaşamaz unless yiyecek_var?
        self.yiyecek_miktarı -= 1
        self.acıkmış = false
      end
      def ses
        puts "Miyav"
      end
    end

    class Dog
      def ye
        yaşamaz unless yiyecek_var?
        self.yiyecek_miktarı -= 1
        self.acıkmış = false
      end
      def ses
        puts "Hav"
      end
    end


    Bu iki sınıfta ye metodu aynı davranışı gösteriyor. Eğer aynı metodu kullanan bir sürü sınıfımız olsa durum daha da vahim olur. Bunu kalıtımla çözebiliriz.

    class Hayvan
      def ye
        yaşamaz unless yiyecek_var?
        self.yiyecek_miktarı -= 1
        self.acıkmış = false
      end
    end

    class Kedi
      def ses
        puts "Miyav"
      end
    end

    class Dog
      def ses
        puts "Hav"
      end
    end


    Ortak metodumuzu içeren bir Hayvan sınıfı ürettik sonra da Kedi ve Köpek sınıflarını buradan kalıtımla üretince ikisinde de metodumuz kullanılabilir. 




    Akış Kontrolleri

    -- if elsif else ve end

    Ruby bütün programlama dillerinde olan if...else dallanma yapılarını if elsif else end ifadeleri ile gerçekleştirir. 

    # yazı tura atmak simülasyonu
    sonuç = [:yazı, :tura].sample

    if sonuç == :yazı
      puts '"yazı" diyenler kazandı'
    else
      puts '"tura" diyenler kazandı'
    end


    Ruby'de if yapıları işlem yapan ifadelerdir, bloktan dönen değer kullanılabilir. 

    durum = if yaş < 18
        :ufaklık
      else
        :yetişkin
      end


    Dönen değer bloktan çıkmadan önce son elde edilen değerdir.

    durum = if yaş < 18
        :boo
        :ufaklık
      else
        :foo
        :yetişkin
      end


    Burada yaş değeri ne olursa olsun ya :ufaklık ya :yetişkin değeri durum değişkenine atanır. :boo ve :foo değerleri bloktan çıkmadan önce son işlenen satırlar olmadığı için, hiç bir zaman durum değişkenine atanmaz.



    -- -- Ternary operatör

    Ruby ayrıca Ternary operatörü de destekler.

    bir_ifade ? eğer_true : eğer_false


    Ternary operatör yapısında bir_ifade ile belirtilen ifadenin sonucu true çıkarsa soru işaretinden sonra gelen işlem, false çıkarsa iki nokta sonrasındaki işlem gerçekleştirilir. Bu doğrultuda yukarıdaki if...else bloğu yerine.

    durum = yaş < 18 ? :ufaklık : :yetişkin

    şeklinde tek satır yazılabilir.


    İlave olarak elsif ifadesi ile extra karşılaştırmalar da yapabiiriz.

    etiket = if size == :s
        'small'
      elsif size == :m
        'medium'
      elsif size == :l
        'large'
      else
        'bilinmeyen size'
      end


    if koşulu gerçekleşmezse ilk elsif (elseif değil dikkat edelim) koşuluna bakılır, o da gerçekleşmezse ikinci elsif koşuluna bakılır, hiç bir koşul gerçekleşmezse else ile verilen değer geri döner.

    Eğer else ifadesini koymadıysak ve hiç bir koşul gerçekleşmezse nil değer dönecektir. Bu string enterpolasyonunda çok işe yarar, nil.to_s bize boş bir string verir.

    >> @users = ["a","b"]
    >> "kullanıcı#{'lar' if @users.size != 1}"
    => "kullanıcılar"

    >> @users = ["a"]
    >> "kullanıcı#{'lar' if @users.size != 1}"
    => "kullanıcı"

    gibi.



    -- case ifadesi

    Ruby switch yapıları için case deyimini kullanır. 

    Ruby dökümanına göre, case ifadeleri isteğe bağlı bir koşul (bu koşul case deyimine argüman gibi verilir), ve bir ya da daha çok when maddelerinden oluşur. Sırayla giderken hangi when maddesi koşulu karşılarsa (ya da koşul verilmemişse boolean doğru sonuç veren bir işlem olursa), ona ait kod bölümü çalıştırılır. case yapısından dönen değer, koşulu karşılayan when kısmından dönen değer olur ya da koşul karşılanmazsa nil döner. 

    Bir case yapısı else bölümüyle bitebilir. Her when satırı virgülle ayrılmış bir'den çok aday değere sahip olabilir.

    case x
    when 1,2,3
      puts "değer 1, 2, veya 3"
    when 10
      puts "değer 10"
    else
      puts "Başka bir sayı"
    end

    Bunun kısaltılmış yazımı.

    case x
    when 1,2,3 then puts "değer 1, 2, veya 3"
    when 10 then puts "değer 10"
    else puts "Başka bir sayı"
    end

    Aslında daha az şey yazmadık , ama az satır kullandık, pek de kısaltılmış değil. 

    Bir de case'e koşul vermeden deneme yapalım.

    case
    when a < b
        puts "'a' sayısı küçük"
    when a > b
        puts "'b' sayısı küçük"
    else
        puts "sayılar eşit"
    end


    Tamamen farklı çalışan iki farklı yöntem gibi duruyor. Her biri başka amaçla kullanılabilir. 

    case'e verilen değer karşılaştırılırken == değil === kullanıldığı için birçok ilginç kullanım imkanları oluşur. 

    Bir case yapısı Range nesnelerle kullanılabilir.

    case 17
    when 16..25
      puts "genç"
    end


    Regexp ile kullanılabilir.

    case "google"
    when /oo/
      puts "kelimede 'oo' var"
    end


    Proc veya lambda ile de kullanılabilir.

    case 44
    when -> (n) { n.even? or n < 0 }
      puts "çift veya sıfırdan küçük"
    end


    Sınıflar'la da kullanılabilir.

    case x
    when Integer
      puts "Bu bir tamsayı"
    when String
      puts "Bu bir yazı"
    end


    === operatörü kullanarak kendi eşleşmelerinizi üretebilirsiniz.

    class Boş
      def self.===(nesne)
        !nesne or "" == nesne
      end
    end
     
    case ""
    when Boş
      puts "isim boş"
    else
      puts "isim boş değil"
    end


    Bir case yapısı argüman girmeden de çelışabilir.

    case
    when ENV['A'] == 'Y'
      puts 'A'
    when ENV['B'] == 'Y'
      puts 'B'
    else
      puts 'A da değil B de'
    end


    case yapısının dönen değeri vardır, bu sayede metodlara argüman olarak verilebilir veya bir değişkene atanabilir.

    açıklama = case 17
      when 16..25
        "genç"
      end
    puts açıklama # => genç



    -- Doğru ve yanlış değerler

    Ruby'de sadece iki değer yanlış kabul edilir.

    • nil
    • boolean false

    Tüm diğer değerler doğru kabul edilir, içinde şunlar da var:

    • 0 - tamsayı ya da diğer tiplerde.
    • "" - Boş string
    • "\n" - harf içermeyen stringler
    • [ ] - Boş array
    • { } - Boş hash

    Örneğin aşağıdaki kodu alalım.

    def doğruluğu_kontrol(değişken_adı, değişken)
      doğruluk = değişken ? "doğru" : "yanlış"
      puts "#{değişken_adı} değeri #{doğruluk}"
    end

    doğruluğu_kontrol("false", false)
    doğruluğu_kontrol("nil", nil)
    doğruluğu_kontrol("0", 0)
    doğruluğu_kontrol("boş string", "")
    doğruluğu_kontrol("\\n", "\n")
    doğruluğu_kontrol("boş array", [])
    doğruluğu_kontrol("boş hash", {})

    çıktımız şöyle olur:

    false değeri yanlış
    nil değeri yanlış
    0 değeri doğru
    boş string değeri doğru
    \n değeri doğru
    boş array değeri doğru
    boş hash değeri doğru



    -- Satır içinde if/unless

    Satır içinde veya takipçi if veya unless kullanmanın genel şablonu.

    puts "x, 5'ten küçüktür" if x < 5


    Buna koşullu değiştirici denir, ve koruma önlemi kodları yazarken ve kod kısaltırken çok işe yarar.

    def save_to_file(data, filename)
      raise "dosya ismi verilmedi" if filename.empty?
      return false unless data.valid?

      File.write(filename, data)
    end


    Bu değiştiricilere else bölümü ekleyemeyiz. Bir de koşullu değiştiricileri esas işi yapan kod bloklarında kullanmak önerilmez, karmaşık kodlarda if..elsif..else..end blokları önerilir.



    -- while ve until döngüleri

    Bir while döngüsü verilen koşul true olduğu sürece blok içinde verilen kodları tekrar tekrar çalıştırır.

    i = 0
    while i < 5
      puts "İterasyon ##{i}"
      i +=1
    end


    while satırında koşul karşılanırsa blok içindeki kod çalıştırılır, eğer koşul doğru değilse blok kodu çalıştırılmadan end sonrasına geçilir. Blok kodu çalışıp end satırına geldiyse tekrar while satırına gönderilir, böylece koşul karşılanana kadar aynı kod döner durur. 

    until döngüsü ise tam tersine verilen koşul false olduğunda blok kodunu çalıştırır. 

    i = 0
    until i == 5
      puts "İterasyon ##{i}"
      i +=1
    end


    Yalnız böyle tek bir değeri bekleyen kodlarda dikkat etmek lazım, i değeri 6'dan falan başlatılırsa sonsuza kadar kod döner. 



    -- Veya eşitlemesi (koşullu atama)

    Ruby veya eşitlemesi operatörü , bir değişkene eğer değeri sadece nil veya false ise başka bir değer atamak için kullanılır. 

    ||= # operatörün yazımı böyle


    Sanki += operatörü falan gibi düşünsek bunun da eşitliğin solu ile sağını veya işlemine tabi tutup soluna yazacağını düşünebiliriz.

    x ||= y      # yerine
    x = x || y  # mi acaba?


    Aslında öyle değil daha çok 

    x || (x = y)

    şeklinde gösterilebilir. x değeri nil ya da false değil ise zaten or işleminin sol tarafı true olacağı için, sağ tarafa bakılmaz bile, ve x aynı kalır. Eğer x değeri false ya da nil ise or işleminin sol tarafı false vereceğinden sağ tarafına geçilir, orası da x değişkenine y değerini atar.

    Bu veya eşitlemesi bir şeylerin değerinin olmadığı yerlerde diğer bir değer kullanmak için çok kullanılır. Diyelim kullanıcıya mail atacaksınız ama o kullanıcıda tanımlı bir email yok, yerine kendinize mail atabilirsiniz.

    if user_email.nil?
      user_email = "error@uygulamam.com"
    end

    bu kodu tek satırda bitirmek mümkün.

    user_email ||= "error@uygulamam.com"

    aynı işi yapar. 

    false değerinin geçerli bir değer olduğu durumlarda bu operatörü kullanmak sorun oluşturabilir.

    çalıştı = false
    çalıştı ||= true
    #=> true olur, değişir

    çalıştı = false
    çalıştı = true if çalıştı.nil?
    #=> false kalır, değişmez



    -- Ruby flip flop operatörü

    Bu ilginç bir operatör önce kaldırılacağı duyuruldu, sonra tekrar geri geldi vs. 

    Flip flop operatörü . . iki koşul arasında yerleştirilir. 

    (1..5).select do |e|
      e if (e == 2) .. (e == 4)
    end
    # => [2, 3, 4]


    Koşul ilk karşılaştırma, iterasyonda true değer verene kadar false sonuç verir ve işlem yapılmaz. Sonra true sonuç verir ve ikinci koşul true değer verene kadar öyle kalır, sonra tekrar false olur. 

    Bu örnek nelerin seçildiğini gösteriyor.

    [1, 2, 2, 3, 4, 4, 5].select do |e|
      e if (e == 2) .. (e == 4)
    end
    # => [2, 2, 3, 4]


    Flip flop operatörü sadece if'lerde  (unless da dahil) ve ternary operatörlerde çalışır. Diğer yerlerde görüntüden de anlaşılacağı gibi Range nesnesi gibi görülür. 

    (1..5).select do |e|
      (e == 2) .. (e == 4)
    end
    # => ArgumentError: bad value for range


    İterasyon içinde true ve false sonuçlarına defalarca ulaşabilir. 

    ((1..5).to_a * 2).select do |e|
      e if (e == 2) .. (e == 4)
    end
    # => [2, 3, 4, 2, 3, 4]



    -- throw ve catch

    Diğer programlama dillerinden farklı olarak Ruby'de throw ve catch sıradışı durumları işlemek için kullanılan deyimler değildir. 

    Ruby throw ve catch deyimleri biraz diğer dillerdeki label kullanımına benzer. Akışı değiştirmek için kullanılır ama sıradışı durumlarda hata algılamakla alakalı değildir.

    catch(:out) do
      catch(:nested) do
        puts "nested"
        puts "before"
        throw :out
        puts "will not be executed"
      end
    end
    puts "after"
    # prints "nested", "before", "after"


    İç içe bloklar var. throw deyimi parametresinde verilen catch bloğunu bulur ve onun dışına çıkar. İç içe bloklardan çıkmak için break kullanıp iki bloktan da çıkmak için koşullar oluşturmak yerine throw kullanmak faydalı olacaktır.

    a = [[1,2],[3,4],[5,6]]
    a.each do |r|
      r.each do |c|
        p c
        if c == 3
          p "3 buldum"
          break
        end  
      end
      p r
    end

    burada sadece ilk iç döngüden çıkılacağı için sonraki r değerleri de değerlendirilecektir. 

    1
    2
    [1, 2]
    3
    "3 buldum"
    [3, 4]
    5
    6
    [5, 6]

    Ama throw kullanarak iki döngüden de çıkabiliriz.

    catch(:buldum) do
      a.each do |r|
        r.each do |c|
          p c
          if c == 3
            p "3 buldum"
            throw :buldum
          end  
        end
        p r
      end
    end

    ve çıktısı

    1
    2
    [1, 2]
    3
    "3 buldum"

    Bu da ilginç bir sıçrama komutu olarak hafızamızda yerini alsın.



    -- unless deyimi

    Eğer birşeylerin olmadığı koşullarda iş yapmak istersek genelde if !(bir_koşul) şeklinde değilini kullanırız. Ruby kod okunurluğu bakımından daha iyi duran bir alternatif olarak unless deyimini kullanır.

    Yapı aynı if deyimi yapısı gibidir, ve satır içi koşullarda da kullanılır. unless yapısında elsif gibi ikinci bir karşılaştırma yapma imkanı yoktur, fakat else satırı olabilir.

    # içinde yoksa bildirir
    unless 'hellow'.include?('all')
      puts 'içinde yok'
    end

    (2..5).each do |x|
      unless x < 4
        p x
      else
        p "bomba"
      end
    end

    a = 4
    p "wow" unless a > 4




    -- Ternary operatör

    Ruby'de de ternary operatörü ( ? : ) kullanımı vardır. Bir koşul sorgulanır, doğruysa bir işlem , yanlışsa başka bir işlem yapılır.

    koşul ? doğruysa_değer : yanlışsa_değer

    koşul = true
    koşul ? "doğru" : "yanlış"
    #=> "doğru"

    koşul = false
    koşul ? "doğru" : "yanlış"
    #=> "yanlış"


    Prensip olarak if a then b else c end yazmaya benzer. Örnek

    puts (if 1 then 2 else 3 end) # => 2

    puts 1 ? 2 : 3                # => 2

    x = if 1 then 2 else 3 end
    puts x                        # => 2




    -- break, next ve redo ile döngülerde dallanma

    Bir Ruby kod bloğunun akışı break, next ve redo deyimleri kullanarak değiştirilebilir. 


    -- -- break 

    break komutu ile hemen içinde bulunulan kod bloğu sonlandırılır. Komut sonrasında olan satırlar işlenmez, döngü varsa döngü de biter, yani end satırı arkasına atlanır diyebiliriz. 

    eylemler = %w(koşu zıplama yüzme çıkış çiftetelli)
    index = 0

    while index < eylemler.length
      eylem = eylemler[index]

      break if eylem == "çıkış"

      index += 1
      puts "Şu anda yaptığım eylem : #{eylem}"
    end

    # Şu anda yaptığım eylem : koşu
    # Şu anda yaptığım eylem : zıplama
    # Şu anda yaptığım eylem : yüzme



    -- -- next

    next deyimi bir iterasyon içinde kullanıldığında hemen bloğun başına döner ve sonraki iterasyona geçer. Bu da sanki end satırına atlıyormuş gibi düşünülebilir. 

    eylemler = %w(koşu zıplama yüzme dinlenmek çiftetelli)
    index = 0

    while index < eylemler.length
      eylem = eylemler[index]
      index += 1

      next if eylem == "dinlenmek"

      puts "Şu anda yaptığım eylem : #{eylem}"
    end

    # Şu anda yaptığım eylem : koşu
    # Şu anda yaptığım eylem : zıplama
    # Şu anda yaptığım eylem : yüzme
    # Şu anda yaptığım eylem : çiftetelli

    Döngü olmayan blokta next kullanırsak hata verir.

    a = 5
    if a < 7
      puts "yediden küçük"
      next if a  >= 6
      puts "altıdan da küçük"
    end
    # Invalid next (SyntaxError)



    -- -- redo

    redo ile blok başına dönülüp aynı iterasyon tekrar yapılır. 

    eylemler = %w(koşu zıplama yüzme uyumak çiftetelli)
    index = 0
    tekrar_sayısı = 0

    while index < eylemler.length
      eylem = eylemler[index]
      puts "Şu anda yaptığım eylem : #{eylem}"

      if eylem == "uyumak"
        tekrar_sayısı += 1
        redo if tekrar_sayısı < 3
      end

      index += 1
    end

    # Şu anda yaptığım eylem : koşu
    # Şu anda yaptığım eylem : zıplama
    # Şu anda yaptığım eylem : yüzme
    # Şu anda yaptığım eylem : uyumak
    # Şu anda yaptığım eylem : uyumak
    # Şu anda yaptığım eylem : uyumak
    # Şu anda yaptığım eylem : çiftetelli

    Dikkatli olun , bu döngü kendi kontrolümüzde ve index sayacını biz kendimiz arttırıyoruz. Bu örnekte index += 1satırının redo sonrasında konması sayesinde aynı iterasyon tekrarlanmıştır. 

    while index < eylemler.length
      eylem = eylemler[index]
      puts "Şu anda yaptığım eylem : #{eylem}"

      index += 1
      if eylem == "uyumak"
        tekrar_sayısı += 1
        puts "redooo"
        redo if tekrar_sayısı < 3
      end
    end


    Bu kodun davranışı bambaşka olur, dikkat edelim.



    -- -- Enumerable iterasyonları

    Döngüler dışında bu dallanmalar each ve map gibi enumerable iterasyonlarında da kullanılır. 

    [1, 2, 3].each do |item|
      next if item.even?
      puts "Item: #{item}"
    end

    # Item: 1
    # Item: 3



    -- -- Blok dönen değerleri ne olur?

    Hem break hem de next deyimlerine bir değer eklenerek dönen değer olarak kullanılabilir. 

    çift_değer = for değer in [1, 2, 3, 4]
      break değer if değer.even?
    end

    puts "İlk çift sayı : #{çift_değer}"
    #=> İlk çift sayı : 2


    Kitaptan bir örnek verelim.

    squareroots = data.collect do |x|
      if (x < 0)
        0
      else
        Math.sqrt(x)
      end
    end

    yerine

    squareroots = data.collect do |x|
      next 0 if x < 0
      Math.sqrt(x)
    end

    yazılabilir. Ama bir de bu var.

    squareroots = data.collect { |x| (x < 0) ? 0 : Math.sqrt(x) }

    Ternary sanki daha iyi bir ifade. next sonrasında değer girmek Ruby'nin meşhur İngilizce cümle gibi okunurluğu özelliğine pek uymamış sanki.



    -- return ve next - bloktan yerel olmayan dönüş

    Şu yarıda kesilen kod parçacığına bakalım. 

    def foo
      bar = [1, 2, 3, 4].map do |x|
        return 0 if x.even?
        x
      end
      puts 'baz'
      bar
    end
    foo # => 0


    İlk bakışta sanki iterasyon sonucunda işlenmiş değerler dönecekmiş gibi geliyor.  [1, 0, 3, 0] gibi bir değer beklenirken, return aslında metoddan geri dönüyor ve değeri sıfır oluyor. Dikkat ederseniz 'baz' yazısı hiç yazılmaz, çünkü kod oraya asla ulaşamadan metod biter. 

    Ama next burada işimizi görür çünkü blok seviyesinde değer döner ve beklenen sonuç alınır.

    def foo
      bar = [1, 2, 3, 4].map do |x|
        next 0 if x.even?
        x
      end
      puts 'baz'
      bar
    end
    p foo
    # baz
    # [1, 0, 3, 0]

    return satırı konmadığında metodlardan dönen değer en son hesaplanan değerdir. Bu örnekte de end satırı öncesine yazılan bar değişkeni değeri metoddan dönen değerdir.



    -- Akışı lojik işlemlerle yönetmek

    Her ne kadar mantığa aykırı gibi gelse de mantıksal işlemleri prosesleri yönetmek amacıyla kullanabilirsiniz. Örneğin.

    File.exist?(dosya_adı) or STDERR.puts "#{dosya_adı} mevcut değil!"


    or işlemi iki tarafından birinin true değer vermesi durumunda true değer döner. Ruby bunu yapmak için ilk önce deyimin sol tarafına bakar ve oranın true sonuç verip vermediğine bakar. Deyimin solunda File.exist?(dosya_adı) yazıyor, ve eğer ismi verilen dosya mevcutsa bu işlem true dönecektir. Eğer sol taraf true değer dönerse Ruby sağ tarafa bakmaz bile ve çünkü or işleminin gereği olan en az bir tarafın true olması koşulu gerçekleşmiş olur. Ama eğer dosya mevcut değilse deyimin sol tarafı false olacağından , sağ tarafındaki işlemin sonucuna bakılır. E bakmak içinde o işlemi çalıştırması gerekir, ve böylece ekrana hata mesajı yazılmış olur. Sağ tarafın sonucunun true olup olmaması bizi ilgilendirmiyor, biz or deyimini sadece bir işlem başarısız olursa diğerini yapalım amacıyla kullandık. 

    or deyiminin dallanma amaçlı bir yaygın kullanımı da şöyledir.

    bardak = bardak or 'dolu' # Optimist


    bardak değişkenine henüz bir değer girilmemişse "dolu" değerini verir. 

    Yukarıdaki ifade genelde Ruby'ciler tarafından,

    bardak ||= 'dolu' # Pesimist

    şeklinde kullanılır, onu da söyleyelim. 

    or gibi and deyimi de dallanma amaçlı kullanılabilir. 

    File.exist?(dosya_adı) and puts "#{dosya_adı} bulundu!"


    Burada da eğer bir şey gerçekleşirse ikinci şeyi yapmak için and deyimi kullanılıyor. and deyimi her iki tarafının da sonucu true olursa true çıkış verir. Bunu yaparken de ilk önce sol tarafın sonucunu bulur, yani örnekte dosya_adı ile verilen dosya mevcut mu, test eder. Dosya yoksa zaten sol taraf false döndüğü için sağ tarafa bakılmaz bile işlem sonucu false olur. Ama dosya mevcutsa, sol taraf true değer döndüğü için sağ taraftaki işlem de yapılır ve ekrana dosyanın bulunduğu yazılır. Tabi ki yine and işlemi sonucuyla ilgilenilmez. 

    or işlemi and işleminden daha düşük önceliklidir. Aynı şekil || işlemi de && işleminden düşük önceliklidir, ve sembol şekillleri yazı şekilllerinden daha önceliklidir. Bütün bunları göz önünde bulundurarak karışık teknikler geliştirebiliriz.

    a = 1 and b = 2
    #=> a==1
    #=> b==2
    # cümle yazar gibi kod

    a = 1 && b = 2; puts a, b
    #=> a==2
    #=> b==2
    # önce && işlemi yapılarak '1 && b = 2'


    Bu kullanımlarla programı inceleyen başka yazılımcıların kafasını bulandırabiliriz, ancak Ruby Style Guide şöyle der:

    and ve or kelimelerini mümkünse kullanmayın , okunurluğu arttırırken ince hatalar yapmanıza sebep olabilir. Boolean işlemler için her zaman || ve && kullanmaya özen gösterin. Akış kontrolü için if ve unless kullanın, || ve && de kullanabilirsiniz ancak okunurluğu düşük olur.



    -- begin , end blokları

    begin bloğu birçok satırdan oluşan bir kısım kodu bir arada toplamak için kullanılır. 

    begin
      a = 7
      b = 6
      a * b
    end


    begin bloğu son edinilen değeri geri döner.  Aşağıdaki örnekte blok 3 değeri döner.

    a = begin
      1
      2
      3
    end
    a #=> 3


    begin bloğu ||= operatörü kullanarak yapılan değer atamalarında birkaç satır kodun sonucunu hesaplamak için çok kullanışlıdır. 

    çevre ||=
      begin
        yarı_çap = 7
        t =2 * Math::PI
        t * yarı_çap
      end


    begin blokları  rescue, ensure, while, if, unless gibi bloklarla beraber kullanılarak program akışını kontrole yardımcı olurlar. 

    begin blokları {...} ve do...end kod blokları gibi değildir. Metodlara gönderilemezler. 


    Bu bölümü de burada bitirelim , sonraki bölümde String değerler ile devam edeceğiz inşallah.

    Şimdilik kalın sağlıcakla..




    Hiç yorum yok:

    Yorum Gönder