Merhaba, Ruby dilinin temel tekniklerini incelediğimiz yazımıza devam ediyoruz.
String Değerler
-- Deyim yapıları
-- Tek tırnaklı ve çift tırnaklı string'ler arası farklar
Ana fark , çift tırnaklı string'ler enterpolasyonu ve tüm escape karakterleri kabul ederler. Örneğin enterpolasyon içinde Ruby kodu çalıştırabilirler.
Çift tırnak ile tanımlanan string'ler \n veya \t gibi escape sequence de kabul eder (ayrıca bakınız backslash notasyonu).
Tek tırnaklı ifadeler her ne kadar escape karakterleri kabul etmese de değer içindeki tek tırnağı ifade etmek için ( \' ) ve bu ikisi string içinde böyle denk gelirse, ters slash'ı ifade için de ( \\ ) kullanımı mecburen olmak zorunda kalınılmıştır.
gibi.
-- Bir String nesnesi üretmek
Biliyoruz ki "Ruby'de her şey bir nesnedir", ve string bir değer de tabii ki bir nesnedir, o yüzden "string değer tanımlamak" yerine "string nesnesi üretmek" diyoruz. Ruby'de String nesnesi üretmenin değişik yolları vardır. İlk ve en çok kullanılan yöntem, tek tırnak ya da çift tırnak içinde bir string değer ifadesi kullanmak.
İkisinin arasındaki ana farkı az önce söyledik, çift tırnaklılar enterpolasyon ve escape sequence kabul eder.
Bir diğer yöntem de birbirini karşılayan string sınırlayıcıları içinde tanımlamaktır.
gibi. Birbirini karşılayan parantez, büyük -küçük veya köşeli parantezlerde string içinde de birbirini karşılayan aynı karakterlerde Ruby işi hemen anlar.
gibi.
Son olarak da sırasıyla tek tırnağa ve çift tırnağa eşlenik olan %q ve %Q deyimleri.
%q veya %Q yapıları özellikle string'ler içinde karışık olarak tek tırnak veya çift tırnakların bol olduğu yerlerde habire escape etmeye uğraşmamak için faydalıdır.
gibi. Sınırlayıcı karakter olarak da eşleşen karakterler kullanılabilir. Yukarıda % ile yaptıklarımız aslında %Q ile yapılmış gibi kabul görür.
-- Harf büyüklüğü değiştirme
Bu dört metod orjinal değeri değiştirmez, manipüle edilmiş değerde yeni bir string üretir.
Bu metodların dördünün de orjinali değiştiren metodları aynı ismin sonuna ünlem işareti ekleyerek tanımlıdır - bu demek değil ki her metodun sonuna ünlem konunca orjinal değişecek.
Örneğin,
-- String'i parçalamak
String parçalamak atomu parçalamak kadar zor değil. String#split (metodu böyle diyez ile yazınca o sınıfın oluşum metodu olduğu ifade edilmek istenir, sınıf metodu olsa direk String.split yazılırdı) metodu kullanarak string değer belirtilen ayırıcı ile parçalanmış şekilde bir array'a dönüştürülür.
Ayırıcı değerine istediğimizi girebiliriz.
-- String pozsiyonlama
Ruby string'ler sola yaslanmış, sağa yaslanmış ve ortalanmış olarak şekillendirilebilir.
Sola yaslanmış yazılar için ljust metodu kullanılır. İki parametre alır, oluşturulacak string'in karakter sayısı, ve geri kalan boşlukların doldurulacağı şablon.
Eğer verilen karakter sayısı, string nesnenin karakter sayısından fazlaysa geri kalan karakterler şablona göre doldurulur. Şablon verilmemişse boşluk bırakılır.
Sağa yaslanmış yazı için rjust (right justify) metodu kullanılır. Kullanım ve parametreler olarak ljust ile aynıdır.
Yazıyı ortalamak için center metodu kullanılır.
-- Array'lerde birleştirerek string yapmak
String#split metodu string'i string'lerden oluşan array olarak parçalıyordu. Array#join metodu da tersini yapar, yani string elemanlardan oluşan bir array'i elemanlarını bir birleştirici ile arka arkaya dizerek stringe dönüştürür.
Birleştirici değer opsiyoneldir, verilmezse boş string olarak kabul edilir.
Boş bir array birleştiricinin ne olduğuna bakılmaksızın boş bir string verir.
-- String#start_with? metodu
Bir stringin verilen şablonla başladığını test etmek için start_with? metodu kullanılır.
Şablonun başladığı index değerinin sıfır olmasını kontrol ederek de başlangıcında şablonun olduğunu test edebiliriz.
-- String enterpolasyonu
Enterpolasyon ile string değer içine hesaplanmış başka değerler enjekte edilir. Çift tırnaklı, %Q kullanan ya da % sınırlayıcılar içinde ifade edilen string'ler enterpolasyon kabul eder. Enterpolasyon ifade etmek için #{ruby_ifadesi} yapısı kullanılır.
Süslü parantez içinde verilen Ruby kodu çalıştırılıp, dönen değer o noktaya enjekte edilir. Stringleri birleştirmek için + operatörü de kullanabiliriz, enterpolasyon da kullanabiliriz.
Enterpolayon kodu daha anlaşılabilir yapıyor, birleştirme şeklinde gidersek boşlukları falan kaçırmak gibi hatalar çok oluyor.
-- String end_with?
Bir string'in verilen şablonla bittiğini test için end_with? metodu kullanılır.
-- Formatlanmış string
Bir array içinde verilen değerleri bir string içinde format ifadeleri ile gösterilen yerlere enjekte etmek mümkündür.
String içinde formatı %s (string) olarak verilen yerlere sırası ile array içinde verilen değerler enjekte edilir.
Format belirleyiciler:
- %s - string değer
- %d - tamsayı değer
- %04d - tamsayı soluna 4 rakamı tamamlayana kadar sıfır eklenir.
- %4d - tamsayıyı 4 karakterlik yere solunda boşluk bırakarak yazar
- %-4d - tamsayıyı 4 karakterlik yere sağında boşluk bırakarak yazar
- %f - kayan noktalı sayı
- %0.2f - Noktadan sonra sadecce 2 basamak gösterilen noktalı sayı
- %x - sayıyı hex'e dönüştürerek yazar
- %b - değeri binary'ye dönüştürerek yazar
- %08b - soluna 8 rakama tamamlayana kadar sıfır ekler
- %o - sayıyı octal değere dçnüştürerek yazar
Daha da ayrıntı isteyen Ruby Doc sayfasına göz atabilir.
-- String içinde parça değiştirmek
Bir string içinde istediğimiz karakterleri değiştirmek amacıyla tr metodunu kullanabiliriz.
Sadece ilk bulunan eşleşmeyi değiştirmek için sub metodu kullanılır.
Dikkat ederseniz sub metodu tr metodundan biraz farklı davranıyor. Bulunan değeri verilen yeni değerle değiştirirken karakter sayılarına bakmıyor, aynen verildiği şekilde değiştiriyor.
Bu davranışı tüm eşleşmelerde uygulamak için gsub metodu kullanılır.
Bir parçayı silmek için ikinci argümanda boş string verebilirsiniz.
Tüm bu metodlarda regex de kullanılabilir.
Bu metodlar yeni bir string kopyası oluşturur, mevcut string'i değiştirmek için tr!, sub! ve gsub! metodları kullanılır.
-- String içindeki veriyi anlamak
Ruby'de bir string değer, karakterleri ifade eden bir byte dizisi ve kodlama adından (UTF-8, US-ASCII gibi) oluşur.
Ruby string'leri genelde yazıları saklamak için kullanılır. Standart kodlama UTF-8'dir.
Gördüğümüz üzere standart ASCII kod tablosunda bulunmayan ü harfi iki byte yer işgal ediyor. Diğer Türkçe karakterlere de bir bakalım.
Eski telefonlarda mesaj atarken Türkçe karakter kullanınca max harf sayısı hemen doluyor ve fazladan mesaj parası ödüyorduk. Bu yüzden İngilizce harfler kullanırdık, o saçmalık geldi aklıma..
Sayısal değerleri bir string içine ASCII-8BIT kodlama olarak bytelar şeklinde saklayabilirsiniz.
Integer'a dönüştürülmüş 4 byte (32 bit) veri olarak string içine paketlenmiş bir sayı.
Bir string içinde byte değerler oluşturduğumuzda geçerli bir encoding vermiyorsa o string'de işlem yapmak hata verir.
-- Çok satırlı string
Çok satırlı string olulturmanın en kolay yolu çift tırnak içinde çok satırlı olarak yazmaktır.
Böyle uzun yazıları yazarken en büyük problem içinde de tırnaklar olursa string'i kapatmasın diye ters slash ile escape etmek gerekiyor. Benzer şekilde string içinde her türlü karşılaşılabilecek kısıtlamalardan kurtulmak için here döküman kullanılır.
VSCode editör bile ne renk vereceğini şaşırdı.. % stringleri de çok satırlı string değeri girmek için idealdir.
%q tek tırnak string ifade ettiği için enterpolasyon almasa da iki ters slash arka arkaya gelince bir tanesini yazdı. Enterpolasyon ve escape sequence'lerden korunmak için birkaç ilke var.
- Çift tırnak yerine tek tırnak kullanın, '\n carriage return demektir.'
- Yüzde string'lerinde küçük q tek tırnak demektir. %q[#{bir-değişken-değil}]
- Here döküman etiket adını tek tırnak içine alın.
gibi.
Semboller
-- Sembol ifadeleri
-- String yerine sembol kullanma avantajı
-- -- Aynı değerdeki semboller aynı nesneyi gösterir
String'ler her biri ayrı bir nesnedir, semboller ise aynı olan semboller aynı nesneyi gösterir.
String'leri karşılaştırırken içlerindeki tüm karakterleri karşılaştırmak zorundayız, bu da N karakterli bir string'de N+1 karşılaştıma yapacağımız anlamına gelir.
Ancak :foobar sembolünün her oluşumu aynı nesneyi gösterdiği için eşitliklerini sadece object_id değerlerine bakarak test edebiliriz.
-- -- Bir sembol enümerasyon değeri olarak kullanılabilir
C++ dilinde birbiri ile bağlantılı sabitleri enümerasyon ile ifade ederiz.
gibi. Fakat Ruby dinamik bir dil olduğu için BugStatus tipini tanımlamak zorunda da değiliz, girilen değerin veri doğruluğunu da garantilemek zorunda değiliz. Enümerasyon yerine rahatlıkla sembolleri kullanabiliriz.
-- -- Sembol biricik adı olan bir sabittir
Ruby'de bir string'in değerini değiştirebiliriz.
Ama bir sembolü değiştirmeye kalkarsak hata verir.
-- -- İsimlendirilmiş parametreler kullanılırken semboller anahtar değer olur
İsimlendirilmiş parametreleri olan bir Ruby metodu çağırırken anahtar değerleri (parametre isimlerini) sembol olarak gireriz. Bunu özellikle Rails'de çok kullanırlar.
-- -- Ruby sembolleri hash key değerleri için idealdir
Bir hash tablosunda anahtar değerleri genellikle sembol ifadelerle yazarız.
-- Sembol Tanımlamaları
Tipik tanımlama sembol ismi başına iki nokta üstüste koymaktır.
Bir string değer kullanarak sembol tanımlamanın alternatif yolları var.
Sembolleri ayrıca %q veya %Q gibi sınırlayıcı karakterler arasında %s kullanarak da tanımlayabiliriz.
%s şekli özellikle içinde boşluk bulunan isimlerle sembol tanımlarken faydalıdır.
:/, :[], :^ gibi özel karakterlerle de sembol tanımlayabilirken nümerik değerlerle sembol tanımlanamaz.
Semboller bir string değer ifadesi içine konmadan tek bir ? ya da ! ile bitebilir.
Tüm bu farklı tanımlama şekilleri aynı sembol için aynı nesneyi gösterir.
Ruby 2.0 sonrasında array içindeki değerlerden bir sembol array oluşturmanın kısa yolu vardır.
-- String'den sembole dönüştürme
Bir string verildiğinde.
Bunu sembole çevirmenin değişik yolları vardır.
gibi.
-- Sembol'den String'e dönüştürme
En klasik yolu to_s metodu kullanmaktır.
Diğer bir yöntem Symbol#id2name metodunu kullanmaktır. Aslında aynı işi yapar ama id2name metodu Symbol sınıfınına özel bir metoddur, yani sembolden başka bir değere uygulanmaya kalkarsanız hata verir.
Ruby'de hata ayıklama
Bir exception sıradışı bir durum oluştuğunu bildiren bir nesnedir. Diğer bir deyişle bir şeylerin ters gittiğini gösterir.
Ruby'de sıradışı durumlar error olarak adlandırılır. Sebebi standart Exception sınıfı sıra dışı durum nesnesinin tepe elemanıyken , kullanıcı tanımlı sıradışı durumlar StandardError sınıfı ya da onun alt sınıflarından birine aittir.
-- Kendi sıra dışı durumunuzu tanımlamak
Normalde bunu Exception sınıfından alt sınıf tanımlayarak yapmanız beklenir. Ancak Ruby'de Exception sınıfı standart sistem hataları ya da sanal makine hataları için kullanılır. Bu yüzden onları kullanmak standart bir hatanın karşılığında olması beklenen davranışı etkileyebilir. Kendi sıra dışı durumlarımızı StandardError veya türevlerinden birini kullanarak tanımlarız.
Genelde isimlendirme yaparken sıra dışı durum açıklaması arkasına Error eklemek tavsiye edilir.
gibi.
Ancak isim oldukça açıksa Error eklemeseniz daha kısa olur.
gibi.
-- Sıra dışı durumu işlemek
Bir hatayı yakalamak için begin/rescue blok yapısı kullanılır.
Diyelim kodumuzda hata üretecek bir şey var.
5 / 0 işlemi kodu çalıştırınca ZeroDivisionError hatası (sıfıra bölme hatası) verecektir. Kodumuzu bu sıra dışı durumda hata vermeyecek şekilde ayarlayabiliriz.
Bu kod hata vermez sadece biz öyle davranmasını istediğimiz için ekrana bir mesaj yazar ve çalışmaya devam eder. Bu rescue yapısı C# ve Java'daki catch bloğuna benzer.
Buradaki gibi yalın bir rescue bloğu StandartError sınıfından hataları işlemek içindir.
Default StandartError yerine Exception sınıfı hataları işlemeye çalışmaktan kaçının. Çünkü Exception sınıfında SystemExit , NoMemoryError ve benzeri ciddi sistem hataları işlenir ve onları işlemeye kalkmak çok pahalıya mal olabilir.
Ayrıca işlemek istediğimiz sıra dışı durumu da belirtebiliriz.
Ama bu hata kodumuzdaki sıfıra bölme harici hatalarda işi yine sisteme bırakır, mesela
satırımız olsa hata bizim rescue bloğumuzun odağı dışında kalır ve programımız hata verir.
Ayrıca sıra dışı durum bilgisini bir değişkende saklayabiliriz.
Eğer hata işlemeyi başarıyla halledemediysek orjinal sistem hatasını hala ateşleyebiliriz.
Eğer kurtarmaya çalıştığımız kod içinde de yine aynı hata oluşuyorsa sonsuz bir döngüye girebiliriz. Bunu engellemek için bir hata sayacı yapabiliriz.
Ayrıca hata işleme yapımızı else ve ensure blokları ile destekleyebiliriz.
Bu ensure bloğu C#'taki Finally bloğu gibi hata olsa da olmasa da bloktan çıkmadan önce çalışmasını istediklerimizi içerir.
Eğer bir def, module ya da class bloğu içindeysek ayrıca begin bloğu yapmadan rescue kullanabiliriz.
-- Çoklu hata işlemek
Aynı rescue bloğunda bir'den fazla hatayı işleyebiliriz.
İstersek bir'den fazla rescue bloğumuz da olabilir.
Burada rescue blokları hangisi önce işlem görürse diğerlerine bakılmaz, bu yüzden en sondaki bloğu en üste koyarsanız İlkError ve İkinciError bloklarına asla ulaşamazsınız.
-- Bir hatayı ateşlemek
Özellikle kendi yazdığımız hata sınıfları için hata oluştuğunu da sisteme bildirmek zorundayız. Ayrıca istediğimiz anda sistemde tanımlı olan hataları da ateşleyebiliriz. Bunu Kernel#raise metoduna hata sınıfımızı ve/veya mesajımızı vererek yapabiliriz.
Sadece mesaj girerek ateşlediğimizde RuntimeError sınıfı olarak kabul edilir.
Bir örnek
-- Kendi sıra dışı durumumuza bilgi eklemek
Kendi tanımladığımız sıra dışı durumlara kayıt için vs. bilgiler eklemek yardımcı olabilir.
Kod akışımızda rescue bloğunda bu tekrarlanabilir bilgisini kullanabiliriz.
Ruby Thread
Thread birkaç değişik kodu paralelde çalıştırmak için kullanılır.
Bu kod yeni akışı çalıştırır, ancak bloğun ilk satırında bir beklemeye girdiği için diğer akışlara geçer. Başka akış yoksa ana akışa döner, bekleme işlemi bitene kadar o kodda dolaşır. Kodumuz şöyle olsa.
Bu kod sadece "thr sonrası" yazar ve program biter. Yeni tanımladığımız thr akışı çalışmaya başlar ama o daha sleep 1 satırında süre dolmasını beklerken ana akışa geri dönüldüğü için kod aşağıdan çalışmaya devam eder ve program ana akış bittiği için biter.
Ana akışın yazdığımız yeni akış bitene kadar çalışmasını durdurması için
metodu çağrılır.
çıktısı
Ana akışın beklemesi için yazdığımız akış daha bitmeden join etmemiz gerekir. Zaten yeni akış bittiyse join bir işe yaramaz kod olduğu gibi devam eder. Eğer join yapılmazsa ana akış bitince program biter.
Bu kodun çıktısında "1 saniye sonrası" yazısı yazılamadan ana akış biteceği için thr akışının o satırına gelmeden programdan çıkılır. Yani çıktısı
olur.
-- Paylaşılan değerlere erişmek
Diyelim tüm akışlarda paylaşılan bir değişken kullanmak istiyoruz.
Bu kod çalışırken her akışın counter değişkenine erişmesi başka zamanda olacağı için rastgele bir sonuç oluşur.
gibi. Bu gibi durumlarda birçok akıştan değişkene senkronize erişim için Mutex nesnesi kullanılır.
Mutex nesnesinin synchronize metodu akışı arkasından verilen blok kod işlenene kadar kilitler. Böylece istediğimiz sağlıklı çalışma oluşur. Ortak veriye ulaşırken kod başka akışlara kaymasın diye Mutex nesnesi kullanbiliriz. Bu kodun çıktısı.
-- Bir thread nasıl yok edilir
Ya Thread.kill ya da Thread.terminate metodu kullanılır
-- Bir thread kendi bitmesi
Thread bloğu kodu sona ulaşınca thread bitecektir zaten. Ancak bunu kendiniz yönetmek isterseniz bir örnek verelim.
Kod while döngüsü içinde dönerken dışarıdan counter.stop ile @countinue değeri false yapılarak thread döngüden çıkarılıyor.
Bu bölüm de bu kadar yeter, bir sonraki bölümde metodlarla devam edeceğiz inşallah. Şimdilik kalın sağlıcakla..
Hiç yorum yok:
Yorum Gönder