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