İçeriğe atla

Ruby/Veri tipleri

Vikikitap, özgür kütüphane

←Temel teknikler | Metod tanımlamak→

Ruby Veri Tipleri

[düzenle]

Önceki bölümde bahsettiğimiz gibi Ruby'de her şey bir nesnedir. Ruby 8 ana veri tipi ve 3 tane de Numeric üst sınıfından devşirme veri tipine sahiptir. Her şeyin bir sınıfı var. İnanmıyor musunuz? Aşağıdaki kodu deneyin:

h = {"hash?" => "yep, it\'s a hash!", "the answer to everything" => 42, :linux => "fun for coders."}
puts "Stringy string McString!".class
puts 1.class
puts 1.class.superclass
puts 1.class.superclass.superclass
puts 4.3.class
puts 4.3.class.superclass
puts nil.class
puts h.class
puts :symbol.class
puts [].class
puts (1..8).class

çıktısı

String
Fixnum
Integer
Numeric
Float
Numeric
NilClass
Hash
Symbol
Array
Range

Gördünüz mü? Her şey bir nesne. Her nesnenin class metodu o nesnenin sınıfını dönüyor. Hemen her şeyde metod çağırabilirsiniz. Daha önce 3.times metodunda bunu görmüştük (Teknik olarak bir nesnede metod çağırdığınızda o nesneye bir mesaj gönderiyorsunuz, ama buna daha sonra değineceğiz)
Bu aşırı nesne yönelimlilik aslında çok eğlenceli, istediğiniz sınıf yapısına değişkenler veya metodlar ekleyip program çalışırken yapısını değiştirebilirsiniz. Ama biz burada veri tiplerini inceleyeceğiz, yani oynanmamış hali.


Sabitler

[düzenle]

Önce sabitler (constant) ile başlıyoruz , çünkü o daha basit. Sabitler hakkında hatırlamanız geren 2 şey var:

  1. Sabit isimleri büyük harf ile başlar. Constants start with capital letters. Sabit bir sabittir, ama sabit bir sabit değildir.
  2. Sabitlerin değerini değiştirebilirsiniz (diğer hemen bütün programlama dillerinde bunu yapamazsınız), ama Ruby size bir uyarı mesjı verir (biliyorum saçma ama bunu yapabilirsiniz)
# types.rb
Sabit = 4
puts Sabit
Sabit = 5
puts Sabit
$ ruby types.rb
4
types.rb:4: warning: already initialized constant 
Sabit
types.rb:2: warning: previous definition of Sabit 
was here
5

Uyarıyor ama işlemi yapıyor. Tebrikler! artık Ruby sabitler hakkında uzman sayılırsınız.


Semboller

[düzenle]

Bu sayfada verdiğimiz ilk kod örneğine dikkat ettiniz mi? "İki nokta üstüste karakteri ile başlayan bu zımbırtı da ne?". Ruyb'nin her şeyi nesne olarak görmesinin bir kötü bedeli var, bir sürü nesne kodun çalışmasını yavaşlatıyor. Ne zaman bir metin yazsanız Ruby yeni bir String nesnesi oluşturuyor. İki tane metin aynı bile olsa Ruby onlara yeni nesne muamelesi yapacağı için ayrı nesneler üretilir. Kodunuzda örneğin "çok yaşa padişahım" metnini kullansanız, sonra başka bir yerde bir daha kullansanız Ruby bunların aynı şey olduğunu düşünmez (aslında düşünmek istemez). Örnek bir irb oturumunda gerçekleri görelim:

$ irb --simple-prompt
>> "çok yaşa padişahım".object_id
=> 43300
>> "çok yaşa padişahım".object_id
=> 46400

Dikkat ettiyseniz aynı iki string değer için Ruby farklı nesne ID değerleri döndü, yani ikisi farklı nesne.

Bu hafıza aç gözlülüğünü halletmek için Ruby'de "semboller" kullanılabilir. Symbol dahili lojikte ve karşılaştırmalarda kullanmak için tasarlanmış hafif nesnelerdir. Eğer kullanıcı görmeyecekse string yerine neden semboller kullanılmasın? Kodunuz bunun için size minnettar kalacaktır. Hadi yukarıdaki kodu string yerine sembol kullanarak deneyelim:

>> :sembolüm.object_id
=> 3078748
>> :sembolüm.object_id
=> 3078748

Semboller adları önünde iki nokta üstüste ile ifade edilir: :sembol_ismi. Kelimeleri birleşik yazmak ya da aralarında alt çizgi kullanarak birleştirmek gerekir. Ama çok da isterseniz şöyle yapılabilir:

>> :"çok yaşa padişahım".object_id
=> 3082548
>> :"çok yaşa padişahım".object_id
=> 3082548
>> puts :"çok yaşa padişahım"
çok yaşa padişahım


Hash değerler

[düzenle]

Hash'ler benzetmek gerekirse sözlük gibidir. Bir anahtar (key) ve bir de döküman (reference) değer çiftlerinden oluşur, ve bilgiyi bulmak için içinde arama yaparsınız.

Bunu hayal etmenin en iyi yolu küçük bir deneme yapmak olacak:

hash = { :leia => "Alderaan prensesi", :han => "Tam bir asi", :luke => "Jedi'a dönüşen bir çiftçi"}
puts hash[:leia]
puts hash[:han]
puts hash[:luke]

bu kodun çıktısı:

Alderaan prensesi
Tam bir asi
Jedi'a dönüşen bir çiftçi

Ayrıca bu kodu şöyle de yazabiliriz:

hash = { :leia => "Alderaan prensesi", :han => "Tam bir asi", :luke => "Jedi'a dönüşen bir çiftçi"}
hash.each do |key, value|
    puts value
end

Bu kod hash değerin her elemanı için iterasyon yapar, anahtar değeri key ve açıklamayı da value değişkenine koyar. Döngü içinde de sadece value değeri yazdırılıyor. Sonuç çıktı önceki ile aynı olacaktır.

Hash değeri tanımlarken daha ayrıntılı olabilirim; şöyle de tanımlayabilirim:

hash = Hash.[](:leia => "Alderaan prensesi", :han => "Tam bir asi", :luke => "Jedi'a dönüşen bir çiftçi")
hash.each do |key, value|
    puts value
end

Eğer Luke'u başımdan savmak istersem, şöyle bir şey yaparım:

hash.delete(:luke)

Artık Luke hash değerimiz elemanlarında yok.

Ya da diyelim çitçilerle aramda husumet var, şunu yapabilirim:

hash.delete_if { |key, value| value.downcase.match("çiftçi") }

Bu tüm hash elemanları üzerinde silme yapmak amacıyla iterasyon yapar ama sadece kod bloğu true değer dönerse. Blok içinde emin olmak için önce value değerini küçük harfe dönüştürdük (içinde çiftçi olan bir kelime "ÇiFtçi!1!" gibi de yazılsa bulsun bana), sonra değer de "çiftçi" kelimesi var mı? sorguladık. 3 değerden sadece birinde "çiftçi" kelimesi olduğu için iterasyonda o elemanda bloğumuz true değer dönecek ve o eleman (yani :luke anahtarı olan çift) silinecektir. Düzenli ifadeler (regular expressions) kullanarak da eşleşme bakabiliriz ama o tamamıyla ayrı ve geniş bir konu başlığı olacak.

Hash değerimize "Lando" içinde bir açıklama ekleyelim:

hash[:lando] = "Cüretkar ve zarif şehir yöneticisi"

Hash değer içindeki eleman sayısını hash.length ile öğrenebiliriz. Sadece anahtar değerlere hash.keys metodu ile bakabiliriz, bu bize anahtar değerlerden oluşan bir Array dönecektir. Şimdi de ona bakalım...


Array değerler

[düzenle]

Array'ler çok bakımdan Hash'lere benzer, ama key değerleri yazılmaz, sıfırdan başlayan sıra numaraları vardır (bunlara "key" değil "index" denir). Beş elemanlı bir Array'in son elemanını bulmak için array[4] yazılır, ve ilk elemanını bulmak için de array[0] yazılır. Ek olarak Hash için gördüğümüz tüm metodlar Array için de kullanılabilir.

Burada bir Array tanımlamanın iki yöntemi görülüyor:

array1 = ["merhaba", "bu", "bir", "array", "değer!"]
array2 = []
array2 << "Bu"      # index 0
array2 << "da"      # index 1
array2 << "bir"     # index 2
array2 << "array"   # index 3
array2 << "değer!"  # index 4

Tahmin edebileceğiniz gibi << operatörü bir Array'in sonuna yeni bir eleman ekler. Eğer bu iki array tanımlamasını yaptıktan sonra puts array2[4] satırnı girersek, çıktıda değer! yazacaktır. Eğer bu değer! elemanını array2 içinden silmek istersem, sadece Array.pop yazarım, bu metod uygulandığı array'in son elemanını döner ve arkasından elemanı siler.

...
puts array2.pop
print array2 , "\n"
$ ruby types.rb
değer!
["Bu", "da", "bir", "array"]

array2.pop metodu sildiği son elemanın değeini (yani "değer!" kelimesini) dönüyor, ve array2 artık daha kısa boyutlu.

Eğer bunu yapmaya devam edersem array2'de hiç bir eleman kalmayacaktır. Eleman kalmamasını Array.empty? metodu ile öğrenebilirim. Örneğin aşağıdaki kısa kod bir array içindeki tüm elemanları diğerine ilave eder:

...
array1 << array2.pop until array2.empty?
print array1, "\n"
$ ruby types.rb
["merhaba", "bu", "bir", "array", "değer!", "değer!", "array", "bir", "da", "Bu"]


Biraz ters oldu ama neyse. Brada beni gerçekten çok etkileyen bir şey var: Array'ler birbirinden çıkarılabilir ya da toplanabilir. Ortalıkta bulunan her dil için kefil olamam ama eminim Java, C++, C# ve Perl bilenler aşağıdaki kodu denediğimi görünce bana deliymişim gibi bakarlar:

array3 = array1 - array2
print array3, "\n"
array4 = array1 + array2
print array4, "\n"
$ ruby types.rb
["merhaba", "bu"]
["merhaba", "bu", "bir", "array", "değer!", "Bu", "da", "bir", "array", "değer!"]

Bu kod çalıştırıldıktan sonra tüm şunlar doğrudur:

  • array3 içinde array1'in array2 elemanlarından biriyle aynı olamayan tüm elemanları vardır.
  • array1'in tüm elemanlarından array2'nin elemanlarının çıkartılmış hali şimdi array3 içindedir.
  • array4 hem array1 hem de array2 elemanlarının toplamını içinde barındırır, dikkat ederseniz aynı elemanlar yine de yazılmıştır.


array1 değişkeni elemanları içinde herhangi bir değeri Array.include? metodu ile arayabilirsiniz:

array1.include?("Burada bu var mı?")


Eğer tüm Array'i tek bir String'e dönüştürmek isterseniz şöyle yapabilirsiniz:

string = array2.join(" ")

Eğer array2 örnekte verdiğimiz elemanlara sahipse string şöyle olacaktır:

Bu da bir array değer!

Bu Array.join metodunu argüman girmeden de çağırabiliriz:

string = array2.join

string'in değeri şimdi:

Budabirarraydeğer!


String değerler

[düzenle]

Eğer henüz okumadıysanız stringler ve alternatif string yazımları sayfalarını okumanızı tavsiye ederim. Bu bölümde String hakkında gelişmiş bilgiler olacak ve bu iki sayfada anlatılanları bildiğinizi kabul ediyorum.

Ruby'de Stringler söz konusu olunca harika bazı metodlar mevcuttur. Örneğin onları çarpmaya tabi tutabilirsiniz:

"Danger, Will Robinson!" * 5

şunu üretir:

Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!

String'ler ayrıca karşılaştırılabilir:

"a" < "b"

şunu üretir:

true


Yukarıdaki kod aslında iki karakterin ASCII kod değerlerini karşılaştırır. Ruby versiyon 1.9 öncesinde bir karakterin ASCII kodunu öğrenmek için:

puts ?A

yapısı kullanılırdı. Ancak Ruby versiyon 1.9 ve sonrasında bu artık çalışmıyor. Yerine String.ord metodu kullanılır:

puts "A".ord

Her iki komut aynı çıktıyı verir, A karakterinin ASCII kodu olan

65

sayısını. Başka karakterler için sadece A harfi yerine kodunu bulmak istediğiniz harfi yazın.

Tersine dönüştürme yapmak için (65'ten A'ya) Integer.chr metodunu kullanın:

puts 65.chr

çıktısı:

A


Concatenation (yazıları birleştirme) birçok diğer dile benzer yapılır: birleştirmek istediğiniz iki String değer arasına + operatörü koyarak iki string birbiri arkasına eklenir:

"Hi, this is " + "a concatenated string!"

şunu üretir:

Hi, this is a concatenated string!


Bu belalı String'leri birleştirme operatörü olmadan da işlemek için enterpolasyon da kullanabilirsiniz. Aşağıdaki kodun çalışması sonucunda String1, String2 ve String3 eşit içeriğe sahip olacaktır:

thing1 = "Red fish, "
thing2 = "blue fish."
string1 = thing1 + thing2 + " And so on and so forth."
string2 = "#{thing1 + thing2} And so on and so forth."
string3 = "#{thing1}#{thing2} And so on and so forth."


Eğer bir String nesnenin harfleri üzerinden iterasyon yapmank isterseniz String.scan metodunu kullanırsınız:

thing = "Red fish"
thing.scan(/./) {|letter| puts letter}

Bu kod thing değişkeni içindeki değerin her harfini tek tek ekrana basar (puts otomatik olarak alt satıra geçer):

R
e
d
 
f
i
s
h

Peki parametredeki şu gıcık "/./" şeyi nedir? Evet arkadaşım, buna regular expression (kurallı ifade) denir. Güçlü küçük yardımsever şeylerdir, fakat bu sayfada ayrıntısına girmeyeceğiz. Şimdilik tüm bilmeniz gereken /./ bir "regex" dir ve herhangi "tek bir" karakter ifade eder. Eğer /../ ifadesi kullanmış olsaydık Ruby bu sefer her iki yanyan karakter üzerinden iterasyon yapacaktı, ve tek sayıda karakter olunca sonuncuyu pas geçecekti!

Kurallı ifadelerin başka bir kullanım yeri =~ operatörüdür. Bir String değerin içinde bir kurallı ifade ile eşleşme olup olmadığı eşleşme operatörü =~ kontrol edilir:

puts "Yeah, there's a number in this one." if "C3-P0, human-cyborg relations" =~ /[0-9]/

çıktısı

Yeah, there's a number in this one.

String.match metodu aşağı yukarı aynı çalışır, ama parametre olarak String değer alır. İşte bir örneği:

puts "Yep, they mentioned Jabba in this one." if "Jabba the Hutt".match("Jabba")


Tamam, bu kadar kurallı ifadeler konuşmak yeter. AŞağıdaki iki örnekte kurallı ifadeler de kullanılailir, ama biz standart eski String'leri kullanacağız. Diyelim Doğruluk Bakanlığı'nda çalışıyorsunuz ve bir String içinde bir kelimeyi başkasıyla değiştirmek istiyorsunuz. Şöyle bir şey deneyebilirsiniz:

string1 = "2 + 2 = 4"
string2 = string1.sub("4", "5")

Şimdi string2 içeriğinde 2 + 2 = 5 var. Fakat ya bu String bir'den fazla doğrultulması gereken yalan içeriyorsa? String.sub metodu sadece kelimeyi ilk bulduğu yeri değiştirir! Tahminim String üzerinde String.match metodu kullanarak ve bir while döngüsü kullanarak halledersiniz. Ama bunu yapmanın çok daha etkili bir yolu var:

winston = %q{   Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!}
winston.gsub("Down with", "Long live")

Big Brother çok gurulanacak! String.gsub metodu "global substitute" fonksiyonudur. "Down with" yazısının her oluşumu şimdi "Long live" ile değiştirilecek ve winston sadece Big Brother'a aşkını ilan edecek, onu küçümsemesini değil.

Bu mutlu haberden sonra haydi Integers ve Float'lara geçelim. If you want to learn more about methods in the Eğer String sınıfının metodları hakkında daha çok bilgi istiyorsanız, sayfanın sonundaki referanslar tablosuna bakınız.


Sayı değerler (Integer ve Float)

[düzenle]

Eğer tüm sayı operatörlerini biliyorsanız bu paragrafı pas geçebilirsiniz. Bilmeyenler için işte hızlı bir kurs. + iki sayıyı toplar. - çıkartma yapar. / bölme yapar. * çarpma yapar. % iki sayının tamsayı bölünmesinden kalanı verir.

Okey, tamsayılar (integer) desimal noktası olmayan sayılardır. Kayan noktalı sayılar (float) noktalı sayılardır. 10 / 3 sonucu 3'tür çünkü iki tamsayının bölümü sonucu da tamsayı olmalıdır. Tamsayıların noktası olmadığı için sadece 3 elde edersiniz. Eğer 10.0 / 3 denerseniz 3.33333... elde edersiniz. Eğer herhangi biri float ise sonuç da float olur. Kapiş?

Pekala, eğlenceli parçaya gelelim. Ruby'de her şey bir nesnedir, bir daha söyleyeyim. Bunun anlamı herşeyin en az bir metodu vardır. Tamsayı ve kayan noktalı sayıların da bir istisnası yok. Öncelikle bazı tamsayı metodları gösterelim.

Burada saygıdeğer times metodumuz var. Bir şeyi defalarca yapmak için kullanılır. Örnekler:

puts "Şimdi 99'a kadar sayacağım..."
100.times {|sayı| puts sayı}
5.times {puts "Bil bakalım ne oldu?"}
puts "İşim bitti!"

Bu kod 0'dan 99'a kadar sayıları yazacak, 5 defa "Bil bakalım ne oldu?" yazacakve sonra "İşim bitti!" diyecek. Bu for döngüsünün basitleştirilmiş bir şekli. for döngüsüne göre saniyenin yüzde biri kadar yavaştır, bunu Ruby ile NASA'ya kod yazarken hatırlarsınız. ;-)


Pekala, hemen hemen bitti, 6 metodumuz daha kaldı. İşte bunların üçü:

# Önce saymasını öğrendik...
1.upto(10) {|number| puts "#{number} Ruby döngüler, ha-ha-ha!"}

# Sonra  NASA'da bir mola verdik...
puts "Geri sayım başladı..."
10.downto(1) {|x| puts x}
puts "Ateşle!!!"

# Sonuçta aldığımız notlar...
5.step(50, 5) {|x| puts x}


Pekala, Ruby kolay anlaşılabilir bir dildir. upto metodu çağrıldığı sayıdan parametrede verilen sayıya kadar yukarı doğru sayar. downto metodu aynısını aşağı doğru sayarak yapar. Son olarak step metodu çağrıldığı sayıdan ilk parametrede verilen sayıya kadar sayar, sayarken de değişim miktarı ikinci parametrede verilen değerdir. Yani 5.step(25, 5) {|x| puts x} kodu 5'er 5'er arttırarak 25'e kadar sayıları yazar.


Diğer 3 metoda sıra geldi:

string1 = 451.to_s
string2 = 98.6.to_s
int = 4.5.to_i
float = 5.to_f

to_s metodu float ve integer sayıları string'e çevirir. to_i metodu float sayıyı integer'a dönüştürür. to_f metodu integer sayıyı float'a dönüştürür. Şimdi hepsini kaptınız. Tüm Ruby veri tipleri "in a nutshell". Şimdi sırada size söz verdiğim string metodları var.


İlave String Metodları

[düzenle]
# Outputs 1585761545
"Mary J".hash

# Outputs "concatenate"
"concat" + "enate"

# Outputs "Washington"
"washington".capitalize

# Outputs "uppercase"
"UPPERCASE".downcase

# Outputs "LOWERCASE"
"lowercase".upcase

# Outputs "Henry VII"
"Henry VIII".chop

# Outputs "rorriM"
"Mirror".reverse

# Outputs 810
"All Fears".sum

# Outputs cRaZyWaTeRs
"CrAzYwAtErS".swapcase

# Outputs "Nexu" (next sayı karşılığı düşünüp bir arttırır - t sonrası u gelir.)
"Next".next

# Outputs nxt == "Neyn" (next metodunu daha iyi anlamanız için)
nxt = "Next"
20.times {nxt = nxt.next}