Ruby'de +, -, *, ==, < gibi operatörlerin çoğu aslında metottur: 5 + 3 yazdığınızda 5 nesnesinin + metodu 3 argümanıyla çağrılır. Bu yüzden bir operatörün nasıl davranacağını sol taraftaki nesnenin sınıfı belirler ve kendi sınıflarınızda operatörleri tanımlayabilirsiniz. &&, ||, and, or ve not gibi mantıksal operatörler ise metot değildir ve yeniden tanımlanamaz.

Bu yazıda aritmetik, karşılaştırma, mantıksal ve diğer operatörleri; and ile && arasındaki öncelik farkını, &. güvenli gezinme operatörünü ve Ruby 4.0 ile satır başında yazılabilen mantıksal operatörleri resmî belgeler temelinde anlatıyoruz. Diğer temel konular için Ruby öğrenme rehberi sayfasına bakın.

Operatörler metottur

Operatörler metottur

Operators belgesi, hangi operatörlerin kullanılabileceğinin onu uygulayan sınıfa bağlı olduğunu söyler. Aynı * işareti bir metinde tekrar, bir sayıda çarpma anlamına gelir; 3 * "ab" ise hata verir, çünkü Integer#* bir metni sayıya çeviremez:

ruby
p 5 + 3          # çıktı: 8
p 5.+(3)         # çıktı: 8
p "ab" * 3       # çıktı: "ababab"
p [1, 2] + [3]   # çıktı: [1, 2, 3]

begin
  3 * "ab"
rescue TypeError => e
  puts e.message # çıktı: String can't be coerced into Integer
end

Kendi sınıfınızda def +(diger) yazarak toplama operatörünü tanımlayabilirsiniz. Aşağıdaki örnek, kuruş cinsinden tutulan para değerlerini toplar:

ruby
class Para
  attr_reader :kurus

  def initialize(kurus)
    @kurus = kurus
  end

  def +(diger)
    Para.new(kurus + diger.kurus)
  end

  def to_s
    format("%.2f TL", kurus / 100.0)
  end
end

toplam = Para.new(1250) + Para.new(399)
puts toplam      # çıktı: 16.49 TL

Karşılaştırma operatörlerinin tamamını tek tek yazmak yerine yalnızca <=> tanımlayıp Comparable modülünü eklemek daha pratiktir; bu yöntem Comparable ve Enumerable yazısında anlatılıyor.

Aritmetik operatörler

Integer#/ iki tam sayıyı böldüğünde sonucu tam sayıya yuvarlar. Belgelerdeki örneklerde görüldüğü gibi yuvarlama negatif sonsuza doğrudur: -7 / 2 sonucu -3 değil -4 olur. Mod operatörü % de bu kurala uyar ve sonuç bölenin işaretini alır. Ondalıklı sonuç için fdiv kullanabilir veya işlenenlerden birini Float yapabilirsiniz. ** üs alır; negatif tam sayı üs Rational sonuç verir.

ruby
p 7 / 2          # çıktı: 3
p(-7 / 2)        # çıktı: -4
p 7 % 3          # çıktı: 1
p(-7 % 3)        # çıktı: 2
p 7.fdiv(2)      # çıktı: 3.5
p 7.divmod(2)    # çıktı: [3, 1]
p 2 ** 10        # çıktı: 1024
p 2 ** -1        # çıktı: (1/2)

Karşılaştırma ve eşitlik

Ruby'de eşitlik için birden fazla metot vardır ve her birinin görevi farklıdır. == değer eşitliğini sınar ve sınıflar tarafından anlamlı biçimde yeniden tanımlanır. equal? iki ifadenin aynı nesne olup olmadığını sınar ve yeniden tanımlanmamalıdır. eql? hash anahtarlarının eşitliği için kullanılır; sayılarda tür dönüşümü yapmadığı için 1.eql?(1.0) yanlıştır. <=> (uzay gemisi operatörü) küçükse -1, eşitse 0, büyükse 1, karşılaştırılamıyorsa nil döndürür. === ise case ifadelerinde kullanılan eşleşme operatörüdür.

ruby
p 1 == 1.0              # çıktı: true
p 1.eql?(1.0)           # çıktı: false

a = "ruby"
b = a.dup
p a == b                # çıktı: true
p a.equal?(b)           # çıktı: false

p 1 <=> 2               # çıktı: -1
p "b" <=> "a"           # çıktı: 1
p 1 <=> "a"             # çıktı: nil

p Integer === 5         # çıktı: true
p((1..10) === 7)        # çıktı: true
p(/ab/ === "xaby")      # çıktı: true

=== sonuçlarının sol taraftaki nesneye göre değiştiğine dikkat edin: bir sınıf için "bu nesne benim örneğim mi", bir aralık için "bu değer aralıkta mı", bir düzenli ifade için "bu metin desene uyuyor mu" anlamına gelir. case ifadesinde nasıl kullanıldığını Ruby if, unless ve case yazısında görebilirsiniz.

Mantıksal operatörler ve kısa devre

Mantıksal operatörler ve kısa devre: and ile && arasındaki fark, Ruby 4.0: satır başında mantıksal operatör

&& ve and, ifadeleri soldan sağa değerlendirir ve ilk yanlış değerde durur; || ve or ise ilk doğru değerde durur. Belgelerin vurguladığı nokta şudur: sonuç her zaman true veya false değildir, son değerlendirilen ifadenin değeridir. Bu yüzden || varsayılan değer vermek için sık kullanılır. ! ise değerin tersini döndürür ve sonucu her zaman true veya false olur.

ruby
p(true && 9 && "metin")      # çıktı: "metin"
p(1 && nil && "metin")       # çıktı: nil
p(false || nil || "yedek")   # çıktı: "yedek"
p(!nil)                      # çıktı: true
p(!!"dolu")                  # çıktı: true

and ile && arasındaki fark

and ve && aynı işi yapar, ama öncelikleri çok farklıdır. Precedence belgesindeki tabloda && atama operatörünün (=) üstünde, and ve or ise altındadır. Sonuç olarak x = true and false satırı önce x = true atamasını yapar, ardından sonucu false ile birleştirip atar. Aynı satır && ile yazıldığında önce mantıksal işlem yapılır:

ruby
x = true and false
p x      # çıktı: true

y = true && false
p y      # çıktı: false

def kaydet
  puts "kaydedildi"
  true
end

kaydet or puts "kaydedilemedi"   # çıktı: kaydedildi

Bu yüzden değer hesaplayan ifadelerde && ve || kullanmak daha güvenlidir. and ve or düşük öncelikleri sayesinde son satırdaki gibi "bunu yap, olmazsa şunu yap" biçimindeki akış kontrolünde okunaklı durur.

Ruby 4.0: satır başında mantıksal operatör

Ruby 4.0 NEWS dosyasına göre ||, &&, and ve or artık bir satırın başında yazılabilir ve önceki satırın devamı sayılır; bu, satır başında nokta ile yazılan metot zincirlerine benzer. Uzun koşulları satırlara bölerken operatörü satır başına almak artık mümkün:

ruby
yas = 25
uye = true

if yas >= 18
   && uye
  puts "giriş izni var"   # çıktı: giriş izni var
end

Güvenli gezinme operatörü: &.

Calling Methods belgesinde tanımlanan &. operatörü, alıcı nil ise metot çağrısını atlar ve nil döndürür; bu durumda metodun argümanları da değerlendirilmez. Normal nokta ile nil üzerinde metot çağırmak NoMethodError verir. &. yalnızca hemen sonraki çağrıyı atlar; uzun bir zincirde her adıma ayrıca yazılması gerekir.

ruby
kullanici = nil
p kullanici&.upcase      # çıktı: nil
p "deniz"&.upcase        # çıktı: "DENIZ"

begin
  kullanici.upcase
rescue NoMethodError => e
  puts e.message         # çıktı: undefined method 'upcase' for nil
end

eslesme = "Python harika".match(/(ruby) (\w+)/i)
p eslesme&.captures&.join("-")   # çıktı: nil

Bit, kaydırma ve diğer operatörler

Tam sayılarda &, |, ^ ve ~ bit düzeyinde işlem yapar, << ve >> bitleri kaydırır. Dizi ve metinlerde ise << sona ekleme anlamına gelir ve nesneyi döndürdüğü için zincirlenebilir. =~ bir metni düzenli ifadeyle eşleştirir ve eşleşme konumunu döndürür; !~ eşleşme yoksa true döndürür.

ruby
p 1 << 4            # çıktı: 16
p 0b1100 & 0b1010   # çıktı: 8
p 0b1100 | 0b1010   # çıktı: 14
p 6 ^ 3             # çıktı: 5

liste = [1]
liste << 2 << 3
p liste             # çıktı: [1, 2, 3]

p "ruby" =~ /b/     # çıktı: 2
p "abc" !~ /z/      # çıktı: true

Ruby 4.0'da dizi açma (splat) operatörüyle ilgili de küçük bir değişiklik var: NEWS dosyasına göre *nil artık nil.to_a metodunu çağırmıyor; bu, **nil ifadesinin nil.to_hash çağırmamasıyla tutarlı hale getirildi.

Öncelik tablosu

Öncelik tablosu: ! ~ tekli +, **, tekli -, * / %

Bir ifadede birden fazla operatör varsa yüksek öncelikli olan önce çalışır. Aşağıdaki tablo Precedence belgesindeki sıranın özetidir; üstteki satırlar önce değerlendirilir. Emin olmadığınız her yerde parantez kullanmak, hem hatayı önler hem de kodu okuyanın işini kolaylaştırır.

Öncelik (yüksekten düşüğe)Operatörler
1! ~ tekli +
2**
3tekli -
4* / %
5+ -
6<< >>
7&
8| ^
9> >= < <=
10<=> == === != =~ !~
11&&
12||
13.. ...
14? : (üçlü operatör)
15modifier rescue
16= += -= ve diğer atamalar
17defined?
18not
19or and
20modifier if, unless, while, until

Tablodaki ilginç bir ayrıntı: **, tekli eksiden daha yüksek önceliklidir. Bu yüzden -2 ** 2 ifadesi -(2 ** 2) olarak hesaplanır. defined? ise çok düşük öncelikli olduğu için parantezsiz kullanıldığında beklenmedik sonuç verebilir; Miscellaneous Syntax belgesi bu yüzden parantez kullanmayı önerir.

ruby
p(-2 ** 2)          # çıktı: -4
p((-2) ** 2)        # çıktı: 4
p(2 + 3 * 4)        # çıktı: 14
p((2 + 3) * 4)      # çıktı: 20

p(defined? @olmayan && @olmayan.zero?)    # çıktı: "expression"
p(defined?(@olmayan) && @olmayan.zero?)   # çıktı: nil

Sık yapılan hatalar

  • Atamada and/or kullanmak: x = a and b yalnızca a değerini atar. Değer hesaplarken && ve || kullanın.
  • Tam sayı bölmesinin yuvarlama yönünü bilmemek: -7 / 2 sonucu -4'tür.
  • == ile equal? metodunu karıştırmak: içerik eşitliği için ==, aynı nesne olup olmadığı için equal? kullanılır.
  • &. operatörünü zincirin yalnızca başına yazmak: her adım nil dönebiliyorsa her adımda &. gerekir.
  • Sırayı değiştirince sonucun aynı kalacağını sanmak: "ab" * 3 çalışır, 3 * "ab" hata verir; davranışı sol taraf belirler.

Sık Sorulan Sorular

Ruby'de ++ operatörü var mı?

Hayır. Ruby'nin operatör ve öncelik belgelerinde ++ veya -- yer almaz. Bir değeri artırmak için kısaltılmış atama kullanılır: sayac += 1. Kısaltılmış atamaların listesi değişkenler yazısında.

Hangi operatörleri kendi sınıfımda tanımlayabilirim?

Metot olarak uygulanan operatörleri tanımlayabilirsiniz: aritmetik operatörler, karşılaştırmalar, ==, <=>, [], << ve ! gibi. Belgeler &&, ||, and ve or operatörlerinin metot olmadığını ve yeniden tanımlanamayacağını belirtir.

|| ile varsayılan değer vermek güvenli mi?

deger || "varsayılan" biçimi, deger yalnızca nil veya false olduğunda varsayılana geçer. 0 veya boş metin Ruby'de doğru sayıldığı için korunur. Ancak false değerinin geçerli bir değer olduğu durumlarda bu yöntem false değerini de ezer; o durumda açıkça nil? kontrolü yapın.

&. ile try arasında ne fark var?

&. dilin kendi operatörüdür ve Ruby'nin resmî belgelerinde tanımlanır. try ise Rails ile gelen bir eklentidir ve bu serinin kapsamı dışındadır; saf Ruby kodunda &. kullanılır.

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 değişkenler ve sabitler · Ruby Integer, Float ve Rational · Comparable ve Enumerable