- Programlama
- Kaynak kod
- Derleme
- Nesne kodu
- Yorumlama
- Çalışma zamanı
- Dinamik programlama dilleri
- Yüksek/alçak seviye diller
- Derleme/Yorumlama
- Derlenen dil
- Yorumlanan dil
- Günümüz trendleri
- Değişken
- İsimlendirme
- İfadeler
- Aritmetik operatörler
- Sayısal tür
- Fonksiyonlar
- Fonksiyon veya Metot
- Nesne Nokta Metot notasyonu
- İlkel veri türleri
- Dizgi
- Karakter
- ASCII Tablo
- Temel bazı dizgi metotları
- Akış denetimi
- Örnek: Kuadratik denklemde çözümü
- Aritmetik Karşılaştırma İşleçleri
- Mantıksal İşleçler
- nil
- Doğruluk/Yanlışlık
- Metot
- Üçlü operatörü
- Kapsam
- İsimlendirilmiş argümanlar
- Öntanımlı argümanlar
- Döngü
- Örnek: Sayı tahmini
- Yeni
- Diziler
- Söz dizimi
- Değer atama
- Temel metotlar
- Negatif indisler
- Dizi sonuna yeni eleman eklemek
- Dizgiden diziye, Diziden dizgiye
- Dizide dolaşmak
- each
- each_with_index
- Bloklar
- Blok kapsamı
- Sayılabilirler modülü
- Seçme (Süzme): select
- Eşleme: map
- Koleksiyon predikatörleri
- Sıralama: sort
- Karşılaştırma işlemi
- <=>: "Spaceship" işleci
- Ünlemli metotlar
- Standart kitaplık
- Sözlük
- Sembol
- Neden "sembol"?
- Sözlüklerde sıra
- Struct
- Modüller
- require
- require_relative
- Düzenli ifade
- gsub
- Nesneler
- Örnek: Düzlemde bir noktanın temsili
- Sınıf
- Nesne Yönelimli Programlama
Programlama
puts 'Merhaba Dünya'
MİB
Bellek
Giriş/Çıkış
Programı belleğe yükle (İşletim Sistemi)
Denetimi programa ver (İşletim Sistemi)
Bellekte sırayla çalışan buyruklar
Sınırlı sayıda buyruklar → Buyruk kümesi (instruction set)
Buyruğu veya işlem sonucunu tutan kayıt alanları → Kaydediciler (registers)
Aritmetik ve Mantıksal işlemleri yerine getiren birim → ALB (ALU)
Kaydediciler: sadece 1 tane → Akümülatör (Birikeç)
Buyruk kümesi: 14 buyruk
İki sayıyı topla
start load this
add result
store result
load that
add result
store result
load result
print
stop
this 3
that 5
result 0
Kaynak kod
Problemin çözümünü ilgili programlama dilinin sözcük ve kurallarıyla anlatan tarif
MİB'nin anladığı tek dil: makine dili
- Programın çalıştırılması: Kaynak kodla yapılan tarifin MİB'nin dilindeki buyruklara dönüştürülmesi
Tarifin hayata geçirilmesi ("programın çalıştırılması")
Önce kaynak kodun tamamını makine diline çevir → Derleme (compile)
Kaynak kodu (tarifi) bir programa girdi olarak vererek tarifteki her cümlenin gereğinin MİB'ne bu program tarafından yaptırılmasını sağla → Yorumlama (interprete)
Kaynak kod bir tarifin hayata geçmesi için tek başına yeterli değil
Sadece makine dilinde yazılan bir tarif doğrudan yeterli (ki onda bile bir tür işlemeye ihtiyaç var, bk. örnekte yapılan bellek ilklendirmeleri)
Bir derleyiciye veya bir yorumlayıcıya ihtiyaç var
Derleme
(Aşırı basitleştirme içerir)
Kaynak kodu hedef MİB'in buyruklarından oluşan makine diline çevir
Bu işlem program çalıştırılmadan önce bir seferliğine yapılır
Derlenmiş biçimdeki program çalıştırılır
Bu modelde program işletim sistemi tarafından doğrudan yüklenerek çalıştırılıyor
#include <stdio.h>
static int this = 3;
static int that = 5;
static int result = 0;
int main()
{
result = this + that;
printf("%d\n", result);
return 0;
}
Nesne kodu
Object code
Derleme sonucunda elde edilen imajı (ör. çalıştırılabilir kipte bir ikili program dosyası) anlatır
Kaynak kodun devamında yer alan bir terim
Terimde geçen nesneyi "Nesne Yönelimli"deki (Object Oriented) nesne ile karıştırmayın
Yorumlama
(Aşırı basitleştirme içerir; derlemeye göre daha da aşırı)
"Yorumlayıcı" programı belleğe yükle
Yorumlayıcı kaynak kodu okur; artık denetim yorumlayıcı programda
Yorumlayıcı, kaynak koddaki anlamlı çalıştırma cümlelerini (ör. satırlar) sırayla yorumlar
Yorumlama? Cümleyi anlamlandır ve gereğini MİB'ne (onun anladığı buyruklarla) yaptır
Bu modelde program işletim sistemi tarafından yüklenenen bir yorumlayıcının aracılığıyla çalıştırılıyor
this = 3
that = 5
result = this + that
puts result
Çalışma zamanı
Önemli bir terim: "çalışma zamanı" → runtime
Programın çalıştırılması süresince geçen zaman dilimini anlatıyor
Derlenen programlarda, derlenmiş program imajının belleğe yüklenip MİB tarafından çalıştırılmaya başlandığı andan, sonlandığı ana kadar geçen süre
Yorumlanan programlarda, kaynak kodun yorumlayıcı tarafından çalıştırılmaya başlandığı andan, sonlandığı ana kadar geçen süre
Dinamik programlama dilleri
Kaynak kod üzerinde çalışma zamanı dışında yapılan başka işlemler de var
Bu süreçler de farklı şekilde adlandırılabiliyor, ör. derleme zamanı (compile time)
Yorumlanan bir program dilinde kararlar çalışma zamanında dinamik olarak alındığından bu dillere "dinamik program dilleri" de deniliyor
"Dinamik" teriminin karşı tarafındaki terim: "Statik"
Bu nedenle kaynak kod üzerinde çalışma zamanı dışında gerçekleşen süreçler genel olarak "statik" terimiyle vasıflandırılıyor
Örnek: Statik kod çözümlemesi
Yüksek/alçak seviye diller
Bilinmesinde yarar olan bir terim çifti
Bir programlama dilinde sunulan soyutlamalarla ifade kabiliyeti ne kadar yüksek ise dil de o kadar "yüksek seviye" (high-level) bir dil oluyor
Karşısındaki terim "alçak seviye" (low-level); soyutlamalar daha az, donanıma daha yakın (ve bir o kadar da denetim olanağı)
Yüksek/alçak diyerek dilin kalitesine ilişkin bir sıfat oluşturmuyoruz, bu teknik bir tartışma
Bunlar göreceli terimler, mutlak anlamda kullanmayın
Örnek: Go, Ruby'ye göre alçak-seviye bir dildir, ama C'ye göre yüksek-seviyelidir
Yorumlanan (dinamik) diller derlenen dillere göre hemen hemen daima yüksek-seviyeli
Derleme/Yorumlama
"Hesaplama" (computing) süreçlerini anlamak için yararlı
Günümüzde artık çok anlamlı terimler değil (bk. JIT, bytecode, garbage collector)
Pek çok gerçeklemede "yorumlama" sürecinde bir tür derleme yapılıyor (çalışma zamanında)
Derleme bazen doğrudan MİB'i hedeflemiyor, sanal bir MİB hedefleniyor (ör. Java sanal makinesi)
Bu terimler programlama dilinin gerçeklemesiyle ilişkili; programlama diline iliştirilecek mutlak bir özelliği anlatmıyor
Bir programlama dili, en azından kuramsal olarak, hem derlenen hem yorumlanan biçimde gerçeklenebilir
Fakat dil (ortaya çıkışında belirlenmiş) doğası itibarıyla bir tür gerçeklemeyi daha etkin kılar veya bir tür gerçeklemeyi teknik olarak çok zorlaştırır
"Derlenen/yorumlanan dil" yerine "Kaynak kodun derlenerek/yorumlanarak çalıştırılması öngörülen dil"
Ör. Ruby, Python, Javascript yorumlanarak çalıştırılması öngörülen diller
Ör. C, Go, Rust derlenerek çalıştırılması öngörülen diller
Derlenen dil
Avantajlar
Çalışma zamanında yorumlama olmadığından (veya minimize edildiğinden) çok daha hızlı
Bellek kullanımı daha az
Sorunlar program çalışmadan önce (derleme aşamasında) yakalanabilir
Lojistiği daha kolay; hedef platform için derlenmiş programın kurulumu yeterli, ayrıca bir yorumlayıcı kurmanıza gerek yok
Dezavantajlar
Yazılması daha maliyetli (derleyiciyi mutlu etmek zorundasınız, tip bildirimleri gibi daha ayrıntılı tarifler gerekiyor)
Çalışma zamanı üzerinde denetiminiz olmadığından "dinamik" işler çeviremezsiniz
(C gibi en azından bir kısım dilde) Çalışma zamanında güvenlik açıkları
Yorumlanan dil
Avantajlar
Geliştirme süresi daha kısa (arada zeki bir yorumlayıcı var, daha az lafla çok iş)
Çalışma zamanı denetlenebildiğinden "dinamik" işler çevrilebilir
Çalışma zamanı denetlenebildiğinden basit güvenlik açıkları yaşanmaz
Daha "taşınabilir" (portable); yazdığınız kodun ilgili platformda çalışması için yorumlayıcının o platformda kurulu olması yeterli (fakat bk. lojistik)
Dezavantajlar
Daha yavaş
Daha fazla bellek tüketimi
Çalışma zamanında yaşanan sürpriz hatalar (derlenebilseydi çalıştırmadan önce yakalanabilirdi)
Artan lojistik yük (yorumlayıcı kurulumu gerekiyor)
Yorumlanması öngörülen bir dilde programın çalışma süresi ve bellek tüketimini artırmak pahasına, programın geliştirme süresini azaltıyoruz
Birim zamanda daha fazla iş
Daha çabuk hayata geçen fikirler
("Fakat" ile başlayacak eleştirilere açık bir önerme)
Önerme: Dinamik (yorumlanan) bir dilde geliştirici konforu hedeflenir
- Sistem kaynaklarını (MİB, bellek vs) daha konforsuz bir durumda tutmak pahasına
Günümüz trendleri
Ayrım yine korunmakla birlikte her iki türün en iyi özellikleri dillere eklenebiliyor
Yorumlanan dillerde tip bildirimleri
Derlenen dillerde çalışma zamanını denetleyen eklemeler (ör. çöp toplayıcı)
Teknik olarak geçerli, fakat pratikte hatalı kod parçalarını geliştirme aşamasında yakalayan zengin statik çözümlemeler ("lint"leme)
Değişken
İsimlendirilmiş bellek hücresi
Bellek hücresinde (bir tür) veri var
Veriye anlamlı bir isimle erişiyoruz
kur = 8.96
dolar = 100.0
tl = kur * dolar
oran = 18.0 / 100
fiyat = 100.0
kdv = fiyat * oran
İsimlendirme
Söz dizimi (sentaks) kuralları
İlk karakter İngilizce alfabedeki küçük/büyük harflerden biri veya alt tire (
_
) olmalıVarsa devam eden karakterlerde ilkine ilave olarak rakamlar kullanılabilir (ama ilk karakter rakam olamaz)
Sadece değişkenler değil, metot adları, sabitler, sınıf/modül adları da (Ruby'de bunlar da birer sabit isim) isimlendirmenin kapsamında
Bu isimlere genel olarak "tanımlayıcı" (identifier) deniliyor
İsimlendirme söz dizimi kuralları → Tanımlayıcı söz dizimi kuralları
Önerme: Uygun isimlendirme kod okunurluğunu çok artırır
Her program bir öykü veya (uzunluğuna göre) bir roman
İsimler bu öykünün kahramanları
Anlamlı isimler öykünün okunmasını kolaylaştırıyor
Türkçe karakterler?
ç | ğ | ı | ö | ş | ü |
Ç | Ğ | İ | Ö | Ş | Ü |
ı
veİ
'ye dikkat! (i
veI
Türkçe'ye özgü değil)- Değişken adlarında Türkçe karakter çoğu durumda kullanabiliriz, ama kullanmamalıyız
Programlama evrensel bir etkinlik
Programlama dillerinin anahtar kelimeleri de İngilizce
İsimlendirmeleri enternasyonal yapmakta yarar var; özellikle her dilden geliştiricinin katkı sunabileceği açık kaynak projelerde
exchange_rate = 8.96
usd = 100.0
tl = exchange_rate * usd
tax_rate = 18.0 / 100
price = 100.0
tax = price * tax_rate
İfadeler
Değerlendirmeye (evaluation) konu ögeler
Değerlendirme? Hesaplama, değer verme
Örnek:
exchange_rate * usd
Bu bir aritmetik ifade
Örnek:
usd = exchange_rate * usd
Bu (aritmetik ifade içeren) bir "atama" (assignment) ifadesi
Her ifade bir değer döner (değerlendirme sonrası)
Ruby'de ilkel değerlerin bizzat kendisi de ifade
Örnek:
100.0
usd = 100.0
atama ifadesinde önce sağ taraf değerlendirilir, dönen değer (100.0
) sol taraftaki değişkene atanır
Önerme: Ruby'de her şey bir ifadedir
IRB'de girilen bir satır
enter
tuşu ile yorumlayıcıya gönderilirIRB, satırı bir bütün halde ifade olarak yorumlar
İfadenin döndüğü değer
#=>
ile belirtilir
Aritmetik operatörler
Operatör → İşleç
Sayısal türde değerleri operatörlerle düzenleyerek dönüş değeri yine sayısal türde olan aritmetik ifadeler kurabiliyoruz
Sayısal tür? Tam sayı, Gerçel sayı, Rasyonel sayı
Aritmetik operatörler beklediğiniz gibi:
+
,-
,*
,/
Ayrıca iki operatör:
%
modülüs ve**
üs alma operatörleri
Sayısal tür
Tam sayı ve gerçel sayılar
Gerçel sayılarla kurulan ifadelere dikkat!
18.0 / 100
yerine18 / 100
yazılırsa?
Ruby'de Rasyonel sayıların gösterimi için özel bir söz dizimi kullanılıyor
tax_rate = 18/100r
Daha okunur
Bunu nasıl kullanacağız? Göründüğü gibi, ör.
18/100r * 100.0
Tür dönüşümleri yapılabilir
Değer nesneleri üzerinde çalıştırılacak iki metot:
to_i
veto_f
Bir değeri tamsayıya çevirmek için
to_i
, ör.18.9.to_i #=> 18
Bir değeri gerçel sayıya çevirmek için
to_f
, ör.18/100r.to_f #=> 0.18
Değer bu dönüşümü desteklemeli
Fonksiyonlar
value = Math.sin(0.5236) # 0.5236 ~ Pi/6 ~ 30 derece
Matematikte aşina olduğumuz bir trigonometrik fonksiyon:
sin
<fonksiyon>(girdi listesi) → çıktı
Fonksiyonlara giriş değerlerini argümanlar yoluyla iletiyoruz, örnekte
30
derecesin
fonksiyonuna iletiliyorFonksiyon (isminin yansıttığı) hesaplamayı yapıp bir değer dönüyor, örnekte
0.5
Ruby'de bir fonksiyona geçirilen argümanlar etrafında parantez kullanmanız her zaman gerekmiyor
Matematiksel fonksiyonlardan bir parça farklı olarak programlamada yazacağınız fonksiyonlar:
Hiç argüman istemeyebilir
Birden fazla argüman isteyebilir
Bir değer dönmeyebilir
Dönecekse sadece tek bir değer döner (bazı dillerde, ör. Go, birden fazla değer dönülebilir)
Fonksiyon veya Metot
Ruby gibi Nesne Yönelimli dillerde fonksiyon yerine metot adlandırması tercih ediliyor
Bu bir isimlendirme inceliği (bazı nedenleri var, gelecekte daha ayrıntılı değineceğiz)
Bundan sonra fonksiyon değil metot diyeceğiz
Metotlarla ilk karşılaşmamız:
puts 'Merhaba Dünya'
puts
bir metot (yani fonksiyon)Metotlara genel olarak bir nesne üzerinde
.
operatörüyle erişiyoruzFakat bu örnekte metot bir nesne üzerinden değil doğrudan çağrılıyor
Bu konuya gelecekte değineceğiz
Nesne Nokta Metot notasyonu
Notasyona dikkat edin! 18.to_f #=> 18.0
Noktanın solunda bir değer:
18
, sağında ise bir metot:to_f
bulunuyorNoktanın solundaki "değer" aslında bir "nesne" (object)
Nesnelere
.
operatörü yoluyla bir mesaj iletiyoruzMesaj → Metot
Nesne mesajın gereğini yerine getiriyor (ilgili metot çağrılıyor)
Önerme: Ruby'de hemen her şey bir nesne
- Nesneleri
.<metot>
söz dizimiyle uyarıyoruz
İlkel veri türleri
Sayısal türler ilkel (primitive) veri türlerinin en yaygın örneği
Pek çok programlama dilinde bir diğer önemli veri türü: "dizgi" (string)
Dizgi
message = 'Merhaba Dünya'
puts message
Örnekteki
'Merhaba Dünya'
değeri bir dizgi (string)Çift tırnak veya tek tırnak kullanabiliriz
who = 'Dünya'
message = "Merhaba #{who}"
puts message
Çift tırnakta Ruby dizgi değerini özel olarak yorumlar → "Dizgi Enterpolasyonu" (String Interpolation)
#{}
arasına istediğiniz karmaşıklıkta bir Ruby kodu yazabilirsinizYorumlayıcı
#{}
arasındaki kodu bir ifade olarak değerlendirir ve dönüş değerini yerine koyarBu örnekte tek tırnak kullanılsaydı
message
dizgisi olduğu gibi (literal) yorumlanacaktı
Dizgiler programlama dillerinde çok temel bir veri türü
- Her bir tespih tanesi bir "karakter" (character, char) olan bir tespih gibi
Karakter
Dizgilerin yapıtaşları; kabaca harfler, rakamlar ve noktalama işaretleri
Bunlara ilave kontrol karakterleri var: boşluk, satır sonu, sekme gibi
Karakterler belirli sayıda bitlik bir bilgiyle kodlanıyor
En bilineni 7 bitlik ASCII: American Standard Code for Information Interchange
Türkçe gibi dile özgü karakterler ASCII tabloda yok
Bunun yerine günümüzde UTF-8 gibi daha evrensel kodlama standartları kullanılıyor
Yine de ASCII tabloya hakim olmalısınız (örneğin UTF-8 ASCII'nin bir tür üst sürümü)
ASCII Tablo
Dec Char Dec Char Dec Char Dec Char
--------- --------- --------- ----------
0 NUL (null) 32 SPACE 64 @ 96 `
1 SOH (start of heading) 33 ! 65 A 97 a
2 STX (start of text) 34 " 66 B 98 b
3 ETX (end of text) 35 # 67 C 99 c
4 EOT (end of transmission) 36 $ 68 D 100 d
5 ENQ (enquiry) 37 % 69 E 101 e
6 ACK (acknowledge) 38 & 70 F 102 f
7 BEL (bell) 39 ' 71 G 103 g
8 BS (backspace) 40 ( 72 H 104 h
9 TAB (horizontal tab) 41 ) 73 I 105 i
10 LF (NL line feed, new line) 42 * 74 J 106 j
11 VT (vertical tab) 43 + 75 K 107 k
12 FF (NP form feed, new page) 44 , 76 L 108 l
13 CR (carriage return) 45 - 77 M 109 m
14 SO (shift out) 46 . 78 N 110 n
15 SI (shift in) 47 / 79 O 111 o
16 DLE (data link escape) 48 0 80 P 112 p
17 DC1 (device control 1) 49 1 81 Q 113 q
18 DC2 (device control 2) 50 2 82 R 114 r
19 DC3 (device control 3) 51 3 83 S 115 s
20 DC4 (device control 4) 52 4 84 T 116 t
21 NAK (negative acknowledge) 53 5 85 U 117 u
22 SYN (synchronous idle) 54 6 86 V 118 v
23 ETB (end of trans. block) 55 7 87 W 119 w
24 CAN (cancel) 56 8 88 X 120 x
25 EM (end of medium) 57 9 89 Y 121 y
26 SUB (substitute) 58 : 90 Z 122 z
27 ESC (escape) 59 ; 91 [ 123 {
28 FS (file separator) 60 < 92 \ 124 |
29 GS (group separator) 61 = 93 ] 125 }
30 RS (record separator) 62 > 94 ^ 126 ~
31 US (unit separator) 63 ? 95 _ 127 DEL
Önerme: Ruby'de karakterler özel bir veri türü değildir
- Ama örneğin C gibi bazı programlama dillerinde çoğunlukla
char
adında özel bir veri türüdür (Ruby'den farklı olarak C programlama dilinde dizgi veri türü yoktur)
Ruby'de bir karakterin ASCII tablodaki onluk tabanda kodunu öğren: .ord
'a'.ord
' '.ord
"\n".ord
"\t".ord
Onlu tabanda verilen bir kodu karakteri içeren dizgiye çevir: .chr
97.chr
Özel karakterler
"\n"
→ Satır sonu"\t"
→ Sekme- Bunlar en yaygınları, bunların dışında ters bölü karakteriyle nitelendirilen başka kodlar da var
Beyaz boşluk (whitespace)
Kabaca; boşluk, satır sonu ve sekme karakterlerine deniliyor (ama başkaları da var)
Dizgi içinde kullanılmadığında, kaynak kod ayrıştırılırken bu karakterler göz ardı edilir veya kodun söz dizimsel olarak farklı parçalarını birbirinden ayırır
Temel bazı dizgi metotları
string = gets
puts string.size # string.length
puts string.empty?
puts string.chomp
puts string.chop
puts string.upcase
puts string.downcase
puts string.capitalize
puts string.tr '_', ' '
puts string.delete '_'
puts string.strip
puts string.start_with? '2021'
puts string.end_with? '2021'
puts string.delete_prefix '2021'
puts string.delete_suffix '.rb'
String birleştirme ("concatenate")
string = ''
string << 'Cezmi'
string << ' '
string << 'Seha'
puts string
# frozen_string_literal: true
?
Bu bir pragma
Kaynak koddaki tüm dizgi literallerini öntanımlı olarak "değiştirilemez" yapıyor
Bu sayede aynı dizgi literali için bellek ayırmak gerekmiyor
Yorumlayıcıya verdiğiniz açık sözün denetlenmesi sağlanıyor (hata yakalama)
city = 'Samsun'.freeze
city << '55' # hata
yerine
# frozen_string_literal: true
city = 'Samsun'
city << '55' # hata
# frozen_string_literal: true
yapılırsa birleştirmeler nasıl?
- IRB'de sorunu görmeyebilirsiniz (görmek için
RUBYOPT="--enable-frozen-string-literal" irb
)
string = String.new '' # string = '' yerine
(Dikkat! String.new
'i argümansız çalıştırırsanız karakter kodlaması ASCII oluyor)
Akış denetimi
Kod akışını farklı kod yollarına bölen koşul deyimleri
Kod bloklarının etkinleştirilmesi belirli koşulların sağlanmasına bağlanıyor
Örnek: Kuadratik denklemde çözümü
Katsayıları verilen kuadratik (İkinci derece) bir denklemde çözüm var mı?
Diskriminant pozitif olmalı (alan bilgisi)
a, b, c = 1.0, 0.0, 1.0
delta = b ** 2 - 4 * a * c
if delta >= 0.0
puts 'Çözüm var'
end
if
,end
birer anahtar kelimeKoşul ifadesi:
delta >= 0.0
aritmetik karşılaştırma içeren bir mantık (lojik) ifadeAritmetik karşılaştırma operatörü
>=
"büyük veya eşit"Gerçel sayı karşılaştırmalarını böyle yapmayın, sorunu görebiliyor musunuz?
İlk satırda paralel atama yapılıyor (kötüye kullanmayın)
Gövdesi tek satır olan if
deyimlerini tek satırda yazabiliyoruz
a, b, c = 1.0, 0.0, 1.0
delta = b ** 2 - 4 * a * c
puts 'Çözüm var' if delta >= 0.0
a, b, c = 1.0, 0.0, 1.0
delta = b ** 2 - 4 * a * c
puts 'Çözüm var' unless delta < 0.0
Yeni anahtar kelime:
unless
Negatif lojik için kullanılıyor
Özellikle
!
değillemeleri içeren basit ifadelerde yararlıOkunurluğu (yerine göre) bir parça arttırıyor
Örnek: Katsayıları verilen kuadratik denklemin gerçel kökleri neler?
a, b, c = 1.0, 0.0, 1.0
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
- Yeni anahtar kelime:
else
Aritmetik Karşılaştırma İşleçleri
İşleç | Açıklama |
---|---|
> | Büyüktür |
>= | Büyük eşittir |
== | Eşittir |
< | Küçüktür |
<= | Küçük eşittir |
Örnek: Verilen 3 sayı geçerli bir üçgenin kenar uzunlukları mı?
Üçgen kuralı (alan bilgisi): Sayılardan herhangi ikisinin toplamı üçüncüden daima büyüktür
a, b, c = 3, 4, 5
if a + b > c && a + c > b && b + c > a
puts "Geçerli üçgen"
else
puts "Geçerli üçgen değil"
end
Koşulda mantıksal (lojik) bir ifade, önermeler
&&
"ve" mantık operatörüyle bağlanmışÖnermelerin her biri aritmetik karşılaştırma,
>
"büyüktür"
Mantıksal İşleçler
İşleç | Açıklama |
---|---|
&& | VE (AND) |
|| | VEYA (OR) |
! | DEĞİL (NOT) |
Örnek: Kullanıcıdan bir tam sayı iste
print 'Lütfen bir sayı girin: '
string = gets.chomp
if string == ''
puts 'Hiç bir şey girmediniz.'
elsif (number = Integer(string, exception: false))
puts "Girdiğiniz sayı #{number}"
else
puts "Geçersiz sayı girdiniz: #{string}"
end
Yeni anahtar kelime:
elsif
, çoklu koşul deyimleriInteger(string, exception: false)
hatalı dönüşümdenil
değeri dönüyorKoşul ifadesi içinde atama yapabilirsiniz (kötüye kullanmayın), yeter ki parantezlerle niyetinizi açık hale getirin
Boş dizgi denetimi daha deyimsel nasıl yapılabilir?
Kullanıcı bir veya daha fazla sayıda beyaz boşluk girmişse?
nil
Düpedüz yokluğu veya geçerli bir değerin yokluğunu anlatan "sözde değer"
Mantıksal bağlamda
false
ile benzer sonuçlar üretiyorYani bu bir "falsy" değer
Diğer dillerde de kısmen benzer değerler var; ör. C, C#, Java'da
null
Doğruluk/Yanlışlık
Basit iki kural
Ruby'de değeri
false
venil
olan her ifade yanlıştırYanlış olmayan her şey doğrudur
number = Integer('geçersiz', exception: false) #=> nil
if number
puts 'Doğru'
else
puts 'Yanlış'
end
Bazen nil
değerini açıkça denetlemeniz gerekebilir
number = Integer('geçersiz', exception: false) #=> nil
if number.nil?
puts 'Evet: nil'
end
Yeri gelmişken... "Ruby'de her şey bir ifade" demiştik, örneği inceleyelim
flag =
if x == 0
false
else
true
end
- Herşeyin ifade olmadığı pek çok dilde böyle bir kod yazamazsınız (
if
pek çok dilde bir ifade değil bir deyimdir)
flag =
if x == 0
false
else
true
end
Bu kodu basitçe şöyle yazabilirdiniz
flag = x == 0
Metot
İsmiyle çağrılarak çalıştırılabilir (bir veya çoğunlukla birden fazla satırlık) kod parçası
Farklı girdilerle tekrar tekrar yapılan hesaplamalar için her seferinde aynı kodu yazmanız gerekmiyor
Hesaplama girdileri çağırma zamanında verilen parametrelerle değiştirilebilir
Örnek: Katsayıları verilen kuadratik (İkinci derece) bir denklemin gerçel köklerini bul
def calculate_roots(a, b, c)
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
end
a, b, c = 1.0, 0.0, 1.0
calculate_roots(a, b, c)
Yeni anahtar kelime:
def
a
,b
vec
metot argümanlarıÇağırma zamanında metoda bu argümanlarla değerleri geçiriyoruz
Metot argümanlarıyla çağırma zamanında kullanılan değişkenlerin aynı isimde olması gerekmiyor
a2, a1, a0 = 1.0, 0.0, 1.0 calculate_roots(a2, a1, a0)
Değerleri hiç bir değişken kullanmadan da geçirebiliriz
calculate_roots(1.0, 0.0, 1.0)
Örnek: Verilen 3 sayı geçerli bir üçgenin kenar uzunlukları mı?
def validate_triangle(a, b, c)
if a + b > c && a + c > b && b + c > a
puts "Geçerli üçgen"
else
puts "Geçerli üçgen değil"
end
end
validate_triangle(3, 4, 5)
Metotlar çoğunlukla bir hesap yaptıktan sonra bize bir sonuç döner
Her iki örnekte de bir sonuç dönmedik
Son örnekte aşama aşama giderek gösterelim
def validate_triangle(a, b, c)
if a + b > c && a + c > b && b + c > a
return true
else
return false
end
end
if validate_triangle(3, 4, 5)
puts "Geçerli üçgen"
else
puts "Geçerli üçgen değil"
end
Yeni anahtar kelime:
return
Kullanıldığı noktada metotu sonlandırarak verilen değeri çağıran tarafa dönüyor
Her metot tek bir iş yapmalı
- İlk örnekte bu kural nasıl ihlal edilmiş?
Ruby zaten
true
/false
hesabını yapıyor, biz ayrıca neden hesap ediyoruz?Ruby'de metottan çıkarken etkin olan son satır aynı zamanda dönüş değeridir
Çoğu zaman
return
ile açık dönüş yapmamız gerekmezRuby'de
return
deyimini "erken çıkış"lar için kullanın
def validate_triangle(a, b, c)
a + b > c && a + c > b && b + c > a
end
if validate_triangle(3, 4, 5)
puts "Geçerli üçgen"
else
puts "Geçerli üçgen değil"
end
Örnek: Kullanıcıdan bir tam sayı iste
def getnum
print 'Lütfen bir sayı girin: '
string = gets.chomp
if string.empty?
puts 'Hiç bir şey girmediniz.'
elsif (number = Integer(string, exception: false))
puts "Girdiğiniz sayı #{number}"
else
puts "Geçersiz sayı girdiniz: #{string}"
end
number
end
İsimlendirmeler çok önemli
Ruby'de metot adlarının sonunda
?
ve!
karakterlerini kullanabilirsiniztrue
veyafalse
değer dönen metotlara "predicate method" diyoruz?
sonlandırma karakteri bir metotun "predicate" olduğunu nitelendirmekte kullanılan bir konvansiyonBu sadece bir konvansiyon, metot adının sonunda
?
karakteri olunca sihirli bir işlem gerçekleşmiyorİsimlendirmeleri çok daha anlamlı yapıyor
Örnek: Katsayıları verilen kuadratik (İkinci derece) bir denklemde çözüm var mı?
Diskriminant pozitif olmalı (alan bilgisi)
def has_solution?(a, b, c)
(b ** 2 - 4 * a * c) >= 0.0
end
if has_solution?(1.0, 0.0, 1.0)
puts "Çözüm var"
else
puts "Çözüm yok"
end
def triangle?(a, b, c)
a + b > c && a + c > b && b + c > a
end
if triangle?(3, 4, 5)
puts "Geçerli üçgen"
else
puts "Geçerli üçgen değil"
end
Üçlü operatörü
Ternary operatörü
def has_solution?(a, b, c)
(b ** 2 - 4 * a * c) >= 0.0
end
puts "Çözüm #{has_solution?(1.0, 0.0, 1.0) ? 'var' : 'yok'}"
def triangle?(a, b, c)
a + b > c && a + c > b && b + c > a
end
puts "Geçerli üçgen#{triangle?(3, 4, 5) ? '' : ' değil'}"
Kapsam
a, b, c = 1.0, 0.0, 1.0
def calculate_roots(a, b, c)
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
end
calculate_roots(a, b, c)
puts delta #=> ?
Metotlar dışarıya kapalı bir kutu gibi davranır
Metot gövdesi bir kapsam ("scope") belirler: yerel kapsam ("local scope")
Yerel kapsamdaki bir değişken dışarı sızmaz (ör.
delta
)Benzer şekilde metot dışındaki hiç bir değer argümanlar yoluyla verilmedikçe içeri sızmaz
Metodun dış dünyayla yegane kontak noktaları: giriş argümanları ve dönüş değeri
İsimlendirilmiş argümanlar
def calculate_roots(a:, b:, c:)
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
end
calculate_roots(a: 1.0, b: 0.0, c: 1.0)
Veriliş sırasıyla anlamlandırılan argümanlar: "pozisyonel argümanlar"
Argümanları veriliş sırasıyla değil de isimleriyle belirtsek?
Özellikle birden fazla sayıda argüman geçirmemiz gerektiğinde yararlı
Neyin ne olduğunu çağırma zamanında karıştırmamış oluyoruz
Öntanımlı argümanlar
def calculate_roots(a: 0.0, b: 0.0, c: 0.0)
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
end
calculate_roots(a: 1.0, c: 1.0)
calculate_roots
Öntanımlı argümanlar "pozisyonel argümanlar" için de geçerli
def calculate_roots(a = 0.0, b = 0.0, c = 0.0)
delta = b ** 2 - 4 * a * c
if delta >= 0.0
delta_sqrt = Math.sqrt(delta)
p, q = (-b - delta_sqrt) / 2 * a, (-b + delta_sqrt) / 2 * a
puts "Kökler: (#{p}, #{q})"
else
puts 'Çözüm yok'
end
end
calculate_roots
calculate_roots(1.0, 0.0, 1.0)
İlk çağrıda işe yaradı
Fakat ikincide işe yaramıyor
Zorunlu olarak tüm argümanları girmek zorunda kaldık, neden?
İkinci derece denklem örneğinde bir sorun daha var
"Bir metot tek bir iş yapmalı" kuralı ihlal edilmiş
Bunu düzeltmek şu aşamada zor
Ruby'de metotlar sadece tek bir değer dönebilir
Birden fazla değeri tek bir değer halinde dönmek gerekiyor
Bunun yolu? Diziler
Döngü
Bilgisayarın en temel kabiliyeti: bir işlemi tekrar tekrar yapabilmek
Örnek: Kullanıcıdan geçerli bir tamsayı al
01 def getnum
02 print 'Lütfen bir sayı girin [ENTER sonlandırır]: '
03
04 while !(string = gets.chomp).empty?
05 number = Integer(string, exception: false)
06 if number
07 return number
08 end
09
10 print "Geçersiz sayı: '#{string}'. Lütfen tekrar girin: "
11 end
12
13 nil
14 end
Yeni anahtar kelime:
while
Çoğu durumda "... oldukça/olmadıkça" veya "... olduğu sürece/olmadığı sürece" gibi okuyabilirsiniz
Sözde kod
Girdi al, bu boş bir dizgi olmadığı sürece
dizgiyi tamsayıya çevir
eğer dönüşüm geçerli ise tamsayıyı dön
hata görüntüle
def getnum
print 'Lütfen bir sayı girin [ENTER sonlandırır]: '
until (string = gets.chomp).empty?
number = Integer(string, exception: false)
return number if number
print "Geçersiz sayı: '#{string}'. Lütfen tekrar girin: "
end
nil
end
Yeni anahtar kelime:
until
if
/unless
ilişkisine benzer şekildewhile
/until
Olumsuz lojik için kullanılıyor
Basit ifadeler kullanıldığı sürece okunurluğu bir parça arttırıyor
Örnek: Sayı tahmini
Verilen bir aralık içinde belirlenmiş bir tamsayıyı tahmin et
İyileştirmeler
- Kod tekrarını nasıl önleriz?
- Döngü üzerinde tam denetim nasıl kurarız?
- Kullanıcıya ipucu verebilir miyiz? "Büyük/küçük" gibi
- Maksimum deneme sayısını sınırlayabilir miyiz?
- Sayı aralığını değişken yapabilir miyiz?
- UX: Her tahminde deneme sayısını da kullanıcıya bildirebilir miyiz?
- Sayı aralığı ve maksimum deneme sayısını program çalıştırılırken girebilir miyiz?
Yeni
- Yeni anahtar kelime:
loop
: açık uçlu döngüler kurmaya yarıyor - Yeni anahtar kelime:
break
: döngü sonlandırmaya yarıyor
break
anahtar kelimesiwhile
,until
ve gelecekte göreceğiniz tüm döngülerde, döngüyü kırmak için kullanılırloop
'a özgü değil
Sabitler: büyük harf ile başlayan (ve çoğunlukla hepsi büyük harften oluşan) tanımlayıcılar
ARGV
: komut satırı argümanlarını tutan (sabit isimli bir) dizi||=
öntanımlı değer atama özdeyişiSTDIN
: standart girdiya karşı düşen sabitSTDIN.gets
: daima standart girdiden (ör. klavye) okuma yapan özdeyiş
Diziler
Birbiriyle ilişkili (bir küme oluşturan) değerleri barındıran veri türü
days = ['pazartesi', 'salı', 'çarşamba', 'perşembe', 'cuma', 'cumartesi', 'pazar']
Kümedeki her değere tek bir tanımlayıcı üzerinden erişiyoruz, nasıl?
İndis kullanarak, ilk değerin indisi
0
, her seferinde 1 artıyor
days[0] #=> 'pazartesi'
days[1] #=> 'salı'
days[6] #=> 'pazar'
days[7] #=> nil
days[100] #=> nil
Pek çok programlama dilinde saymaya 0
'dan başlanır, 1
'den değil
- Bu durumda son elemanın indisi ne oluyor?
<dizi uzunluğu> - 1
Söz dizimi
Birden fazla satıra yayabiliriz
days = [ 'pazartesi', 'salı', 'çarşamba', 'perşembe', 'cuma', 'cumartesi', 'pazar', ]
Son elemandan sonra da
,
kullanmaya izin var (ama topluluk stilinde hoş bakılmıyor)Özel olarak dizgilerden oluşan dizileri ilklendirmek için
%w[]
kullanabilirizdays = %w[pazartesi salı çarşamba perşembe cuma cumartesi pazar]
Değer atama
Basitçe ilgili indisteki elemana değer atamamız yeterli
days[0] = 'monday'
Temel metotlar
Dizi uzunluğu:
days.size
veyadays.length
(eşdeğer)İlk eleman:
days.first
Son eleman:
days.last
Negatif indisler
Diziye sondan erişmek için kullanılıyor
days[-1] #=> 'pazar'
days[-2] #=> 'cumartesi'
days[-7] #=> 'pazartesi'
days[-days.size] #=> 'pazartesi'
days[-8] #=> nil
days[-100] #=> nil
Dizi sonuna yeni eleman eklemek
Çok sık yaptığımız bir işlem
a = []
a << 3
a << 5
a << 9
a #=> [3, 5, 9]
Dizinin başına (veya seçilen bir indisten önce/sonraya) elema eklemek? Düşündüğünüz kadar sık ihtiyaç duyulacak bir işlem değil
a = []
a.unshift 3
a.unshift 5
a.unshift 9
a #=> [9, 5, 3]
Dizgiden diziye, Diziden dizgiye
split
ve join
: bu metot çiftine özel bir yer ayırıyoruz
split
: bir dizgiden (tekil, skalar) bir dizi (çoğul, vektör) üretiyorjoin
: bir diziden (çoğul, vektör) bir dizgi (tekil, skalar) üretiyor
name = 'Ahmet Yılmaz'
names = name.split #=> ['Ahmet', 'Yılmaz']
dashed = names.join('-') #=> 'Ahmet-Yılmaz'
Dizide dolaşmak
En sık yapılan işlem, bir tür döngü
Şu ana kadar öğrendiğiniz döngü deyimleriyle yapılabilir
Ama bunu yapmayın, Ruby'de dizilerde dolaşmak için çok daha güçlü yöntemler var
each
Ruby'de Enumerable
modülünde tanımlı olan ve dizi türündeki tüm nesnelerin cevap verdiği bir metot
days.each do |day|
puts day
end
Ruby'de dizilerde dolaşmak için daima each
ve daha sonra gösterilecek Enumerable
metotlarını kullanın
while
,until
,loop
(ve anlatmaya gerek görmediğimizfor
) gibi döngü deyimlerini kullanmayınBu deyimlere çoğunlukla dizi içermeyen düzensiz döngülerde ihtiyaç duyacaksınız
Bu uyarı özellikle diğer programlama dillerinden bilgi transfer edeceklere önemli
each_with_index
Dolaşırken indis bilgisine ihtiyacımız varsa?
days.each_with_index do |day, i|
puts "#{i}: #{day}"
end
Bloklar
Metotlara geçirilen eylemler
Daha teknik bir anlatımla isimsiz (anonim) işlevler
Ruby'nin çok önemli bir özelliği
Blokları anlayabilmemiz için hikayede biraz geriye gitmek zorundayız
Ruby'de hemen her şey akıllı bir "nesne"
Uygun metotlarla uyararak nesnelerin istediğiniz davranışı göstermesini sağlayabilirsiniz
Örneğin dizgiler birer nesne
irb(main):001:0> 'This is a test'.length => 14 irb(main):002:0> 'This is a test'.upcase => THIS IS A TEST
Sayılar da öyle
irb(main):003:0> 3.times { puts 'Test' } Test Test Test => 3
Son örneğe yoğunlaşalım
3.times { puts 'Test' }
3
bir tamsayı nesnesi,times
bu nesnenin bir metotuÖyle ki bu metota hangi eylemi tekrarlayacağını bildirebiliyorsunuz
Nasıl? Eylemi gerçekleyen bir kod bloğuyla
{ puts 'Test' }
Kod bloklarını
{...}
yerinedo ... end
ile de yazabiliriz3.times do puts 'Test' end
Stil olarak tek satırlık bloklarda kıvrık parantezler, birden fazla satıra yayılan kod blokları için
do ... end
tercih ediyoruz
Metafor
İngiltere kraliçesi Türkiye'ye resmi ziyaret yapacak; Kraliçenin ülkede bulunduğu sürede yemekleri nasıl olacak?
1: İngiliz protokolü Dışişleri protokolüne kraliçenin ülkedeyken yiyeceği yemeklerin listesini veya tarifini iletebilir
2: Kraliçe yemekleri yapacak özel ahçısını bizzat yanında getirebilir
İlki klasik yöntem, bir metota (ör.
yemek_hazırla
) veri girilmesi (yemek listesi veya tarifler)İkincisinde ise metota bir eylem veriliyor, ahçının eylemleri
Bir işleve eylemde ihtiyaç duyacağı bilgileri argümanlar üzerinden geçirebiliriz.
puts 'Test'
puts
: Neyi görüntüleyeyim?Çağıran: "Test" dizgisini
Aynı diyaloğu times
için kurgulayalım.
times
: kaç defa ne yapacağım?
Ama bu soru hatalı.
times
metodu uyarılırken kaç defa bilgisini zaten alıyor, nasıl?3.times ...
Diyalog
times
ile değil3
tamsayı nesnesi arasında gerçekleşmeli
Diyalog:
3
: Ne istiyorsun?Çağıran: Sen defa (yani
3
defa) bir şey yapmanı.3
: Tamam, ben defa ne yapacağım?Çağıran:
puts 'Test'
('Test' dizgisini görüntüle).
Sonuçlar:
Nesnelere sadece veri değil eylem de bildirilebiliyor
Bu sayede çeşitlenebilir davranışlar elde edebiliyoruz
Tekrarlama eylemiyle (
times
), tekrarlanacak eylemi (puts "Test"
) ayırıyoruztimes
bir metot,puts 'Test'
ise bu metota geçirilen bir blok
Bloklar metodumsu (veya fonksiyonumsu) şeyler
Blok argümanları (varsa)
|...|
karakterleri arasında (metotlardaki parantezler)Blok dönüş değeri metotlardaki gibi etkin olan son satır
Dikkat! Bloğun dönüş değerini bloğu alan metotun dönüş değeriyle karıştırmayın
Nihai "dönüş değeri"ni bloğun verildiği metot belirliyor
3.times { 'Merhaba' } #=> 3 döner
- Bloklarda da erken çıkış için (
return
yerine)break
veyanext
tercih ediyoruz
%w[samsun istanbul izmir adana].each do |city|
next if city.include? 'a'
puts city
end
%w[samsun istanbul izmir adana].each do |city|
break unless city.include? 'a'
puts city
end
Blok kapsamı
"Bloklar metodumsu (veya fonksiyonumsu) şeyler" demiştik
Blok içinde tanımlanan bir değişken bloğa özgüdür, blok dışına çıkamaz
%w[samsun istanbul izmir adana].each do |city| next if city.include? 'a' puts city end puts city
Bu kod neden hata veriyor?
Düzeltmek için ne yapılabilir?
Bazen bir blokta üretilen bir değeri dış kapsama taşımak isteyebiliriz
cities_with_a = [] %w[samsun istanbul izmir adana].each do |city| cities_with_a << city if city.include? 'a' end puts cities_with_a
Sonuç: dış kapsama taşınacak değeri tutacak değişkeni bloktan önce tanımlayın (
nil
gibi bir değerle de olsa)Fakat bunu yapmanın hemen hemen daima daha iyi bir yolu vardır (örnek için
select
veyacollect
)
Sayılabilirler modülü
Enumerable
modülü
Koleksiyonlar üzerinde yapılabilecek pek çok işlemi barındıran bir "katıştırma" (mixin) modülü
Koleksiyon? Genel olarak Dizi ve daha sonra görülecek olan Sözlük veri yapıları
Koleksiyon? Daha teknik bir anlatımla
each
metodunu sağlayan tüm nesnelerİşlemler? Sıralama, seçme, eşleme, arama
En temel metot:
each
, koleksiyonlarda dolaşmak içinFakat "sonuç üreten" dolaşmalarda
each
yerine kullanmanızın daha uygun olacağı metotlar varEnumerable
modülü bu metotları sağlıyorYeter ki nesne
each
metotunu gerçeklesin (bu metotuEnumerable
sunmuyor, sadece yararlanıyor)
Seçme (Süzme): select
N
boyutlu bir dizide belirli bir koşulu sağlayan ögeleri seçiyoruzKoşul bir "blok"la ifade ediliyor: "blok"
true
değer üretmişse ise seç, aksi halde bırakSeçilen ögelerle
M
boyutlu yeni bir dizi oluşturuyoruz; öyle kiM <= N
[1, 2, 3, 4, 5].select { |num| num.even? } #=> [2, 4]
%w[foo bar].select { |str| str == 'foo' } #=> ['foo']
select
metotunun diğer adları:filter
,find_all
Seçme işlemi yeni bir dizi üretir
Yeni dizinin boyutu özgün diziden büyük olamaz, genellikle daha küçüktür
Diziler üzerinde seçme yaparken each
değil select
tercih edin
Enumerable
modülünü de barındıran Standart kitaplığı iyi tanıyınHer iş için en uygun çözümü kullanın
Aksi halde kendi "kötü" çözümünüzü geliştirme tehlikesi söz konusu
İlişkili metot: reject
[1, 2, 3, 4, 5].reject { |num| num.even? } #=> [1, 3, 5]
İlişkili metot: find
[1, 2, 3, 4, 5].find { |num| num.even? } #=> 2
Bu metotun daima tek bir değer döndüğüne dikkat edin ("find" yapamamışsa
nil
)Amacınız koşul sağlandığında devam etmeden ilgili ögeyi dönmek ise daima
find
kullanınDiğer adı:
detect
Eşleme: map
N
boyutlu bir dizideki her ögeyi, çoğunlukla o ögeyi bir işlemden geçirerek, yeni bir ögeyle eşleştiriyoruzİşlem bir "blok"la ifade ediliyor: "blok"un (girdi olarak verilen ögeyle) ürettiği değerle eşleme yap
Eşleşen ögelerle yine aynı boyutta (
N
) yeni bir dizi oluşturuyoruz
[1, 2, 3, 4, 5].map { |num| num ** 2 } #=> [1, 4, 9, 16, 25]
%w[foo bar].map { |str| str.upcase } #=> ['FOO', 'BAR']
map
metotunun diğer adı:collect
Eşleme işlemi yeni bir dizi üretir
Yeni dizinin boyutu özgün diziye daima eşittir (daha büyük veya küçük olamaz)
Diziler üzerinde eşleme yaparken each
değil map
tercih edin
Enumerable
modülünü de barındıran Standart kitaplığı iyi tanıyınHer iş için en uygun çözümü kullanın
Aksi halde kendi "kötü" çözümünüzü geliştirme tehlikesi söz konusu
Koleksiyon predikatörleri
all?
, any?
, one?
, none?
%w[ant bear cat].all? { |word| word.length >= 3 } #=> true
%w[ant bear cat].any? { |word| word.length >= 3 } #=> true
%w{ant bear cat}.one? { |word| word.length == 4 } #=> true
%w{ant bear cat}.none? { |word| word.length == 5 } #=> true
Diziler üzerinde mantıksal sonuç üreten bir değerlendirme yaparken each
veya select
değil bu metotları tercih edin
Enumerable
modülünü de barındıran Standart kitaplığı iyi tanıyınHer iş için en uygun çözümü kullanın
Aksi halde kendi "kötü" çözümünüzü geliştirme tehlikesi söz konusu
İyi
ok = original.all? { |word| word.length >= 3 }
if ok
...
end
Kötü
original = %w[ant bear cat]
longest_than_two = original.select { |word| word.length >= 3 }
if longest_than_two.length == original.length
...
end
Çirkin
original = %w[ant bear cat]
ok = true
original.each do |word|
if word.length < 3
ok = false
break
end
end
if ok
...
end
Sıralama: sort
N
boyutlu bir diziyi "belirli bir kurala" göre sıralıyoruzKural bir "blok"la ifade ediliyor: verilen öge çiftini karşılaştırarak "küçük, eşit, büyük" sonucu üret
Sıralanan yine aynu boyutta (
N
) yeni bir dizi oluşturuyoruz
[1, 2, 3, 4, 5].sort { |a, b| b <=> a } #=> [5, 4, 3, 2, 1]
%w[foo bar].sort #=> ['bar', 'foo']
Sıralama kuralı zorunlu değil: verilmemesi halinde öntanımlı kurallar uygulanıyor
Öntanımlı kuralllar? Dizgilerin alfabetik sıralaması, sayıların büyüklüğüne göre sıralanması
Sıralama işlemi yeni bir dizi üretir
Yeni dizinin boyutu özgün diziye daima eşittir (daha büyük veya küçük olamaz)
Diziler üzerinde sıralama yaparken each
değil sort
tercih edin
Enumerable
modülünü de barındıran Standart kitaplığı iyi tanıyınHer iş için en uygun çözümü kullanın
Aksi halde kendi "kötü" çözümünüzü geliştirme tehlikesi söz konusu
Karşılaştırma işlemi
Sıralama yaparken her seferinde iki ögeyi karşılaştırıyoruz: ör.
a
veb
İşlem başarımını eniyileştirmek için öge çiftlerinin nasıl seçileceği önemli bir "algoritmik problem"
Buna "sıralama algoritması" diyoruz
Literatürde "quick, merge, heap, shell, bubble" sort gibi iyi bilinen bazı algoritmalar var
Standart kitaplıktaki metotları kullanarak sıralama yaparken "sıralama algoritması"yla ilgilenmiyoruz
Bu bir gerçekleme detayı: Ruby sizin için en uygun (optimum) veya ona yakın algoritmayı seçiyor
Bizim ilgilendiğimiz kraşılaştırma işlemi; ki çoğu durumda onu bile vermemiz gerekmiyor
Karşılaştırma işlemi 3 ihtimallı bir bilgiyi üretmeli: "küçüktür", "eşittir", "büyüktür"
"Küçüktür": negatif bir sayı, çoğunlukla
-1
"Eşittir":
0
"Büyüktür": pozitif bir sayı, çoğunlukla
1
[1, 2, 3, 4, 5].sort do |a, b|
if b < a
-1 # herhangi bir negatif değer de olabilir
elsif b > a
1 # herhangi bir pozitif değer de olabilir
else
0
end
end #=> [5, 4, 3, 2, 1]
Örnekte dizideki öge çiftinin a, b
sırasıyla verildiğine fakat karşılaştırmanın b, a
sırasıyla yapıldığına dikkat edin: "Ters sıralama"
<=>
: "Spaceship" işleci
Örnekteki karşılaştırmayı her seferinde yapmanız gerekmiyor
<
,>
ve==
karşılaştırmaları ilkel nesnelerin zaten cevap verdiği metotlar (bu bilgi nesne de zaten var)O halde örnekteki karşılaştırmaları
<=>
isimli özel bir metotta toplayabiliriz
(Dikkatli bakılırsa cepheden bir tür "uzay gemisi" görülebilir)
def <=>(other)
if self > other
1 # herhangi bir negatif değer de olabilir
elsif self < other
1 # herhangi bir pozitif değer de olabilir
else
0
end
end
Böyle bir
<=>
metotuInteger
,String
gibi ilkel veri türü nesnelerine ekleyebilirizBu yapıldığında
sort
metotuna karşılaştırma işlemi vermek bile gerekmezNeden? Ruby "türdeş" dizide dolaşırken ilgili türün
<=>
metotunu uyarır
sort
metotunu genellikle hiç bir blok tanımlamadan kullanıyoruz
Öntanımlı davranışın var olmadığı veya uygun olmadığı durumlarda bir blok lullanıyoruz
Fakat çoğu durumda ilgili nesneye ait sınıfta özel bir
<=>
metotu yazmak daha doğru
Bir diziyi ters sıralamak için her seferinde önceki örneklerdeki gibi mi kod yazacağız?
[1, 2, 3, 4, 5].sort.reverse #=> [5, 4, 3, 2, 1]
Önce sırala, sonra tersle
Dikkat! Sadece
reverse
diziyi sıralamadan tersler
Bir dizgi dizisini dizgi uzunluklarına göre sıralamak istersek?
İlişkili metot: sort_by
%w[apple pear fig].sort_by { |word| word.length } #=> ['fig', 'pear', 'apple']
Karşılaştırma işlemini ögelerin kendisiyle değil bir özelliği üzerinden yapıyoruz
"Blok" nesnenin kendisinden bu özelliğe ait değeri üretiyor
Öyle ki karşılaştırma bu değerlere ait öntanımlı
<=>
işleciyle gerçekleşiyor
Ünlemli metotlar
Verilen koleksiyonla eş boyutta bir koleksiyon üreten metotları ele alalım: map
, sort
, reverse
Bu metotlar her seferinde yeni bir dizi üretiyor
Örneğin bellekte büyük yer kaplayan 100,000 ögeli bir diziyi sıraladığımızda bellekte bir o kadar yer işgal eden yeni bir dizi üretiyoruz
Sıralanmamış özgün diziye sonraki aşamalarda çoğunlukla ihtiyacımız yok
Bellekten tasarruf için yerinde sıralasak? Yani sıralanmış dizi eskisine ayrılan bellek alanına kayıtlansa?
> a = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
> b = a.reverse #=> [5, 4, 3, 2, 1]
> a #=> [1, 2, 3, 4, 5]
yerine
> a = [1, 2, 3, 4, 5]
> a.reverse! #=> [5, 4, 3, 2, 1]
> a #=> [5, 4, 3, 2, 1]
Tek yapmamız gereken metotu sonunda !
olan çeşidiyle değiştirmek
> a = [1, 2, 3, 4, 5]
> a.reverse!
> a #=> [5, 4, 3, 2, 1]
> a.sort!
> a #=> [1, 2, 3, 4, 5]
> a.map! { |i| i ** i }
> a #=> [1, 4, 9, 16, 25]
Ters sıralamada daima ünlemli metotları kullanın
İyi
a.sort!.reverse!
Kötü
a.sort.reverse
# veya
a.sort.reverse!
Ruby'de sonunda
?
karakteri bulunan metotları görmüştük?
"doğru mu, değil mi?"yi çağrıştırıyorduSondaki
!
ise genel olarak "dikkatli ol, bu metotun bir yan etkisi var" mesajını iletiyorÖrneklerdeki yan etki? Özgün dizinin artık yok olması, değişime uğraması
Bu yan etki bazen istemediğiniz bir şey olabilir
Örneğin: "Sıralanmamış diziye de ihtiyacım var; isteğe göre farklı kurallarla birden fazla sıralama yapmam gerekiyor"
Bu bir konvansiyon
Her metotun ünlemli bir karşılığı yok, sadece anlamlı durumlarda var
Sona
!
koyduğunuzda sihirli bir şey olmuyor, bu sadece bir konvansiyonBu konvansiyon, (daima zor olan) isimlendirmelerde kullanacağınız bir enstrüman
Mevcut bir ismi "recycle" ederek yeniden kullanabiliyorsunuz, üstelik daha anlamlı bir seçenek olarak
Ünlemli metotu, eğer bu anlamlıysa, siz gerçekleyeceksiniz
Bunu yaparken "dikkat, yan etkisi var" semantiğine sadık kalın
Standart kitaplık
Standart kitaplık bir hazine
Kıymetli pek çok mücevher (metot) var
Bunları tanıyın (aksi halde ne olacağını biliyorsunuz)
Diğer "sayılabilirler" metotları
max
,min
,max_by
,min_by
,sum
uniq
,zip
,tally
Diğer dizi metotları
compact
,flatten
sample
,rotate
permutation
,combination
product
,transpose
intersection
,union
,difference
Sözlük
Fikir
- Dizi elemanlarına tamsayı indisleriyle erişiyoruz
- Erişmek istediğimiz bilgiyi bulmakta kullandığımız tek anahtar bir tamsayı
- Anahtar olarak daha anlamlı bir şey kullanamaz mıyız?
- Örneğin "dizgi"leri anahtar olarak kullansak?
Gündelik hayattan alınma örnekler
- Arayacağımız kişiye telefon numarası yerine ismiyle erişmek
- Bir web sitesine IP numarası yerine anlamlı bir isimle erişmek
Bilgisayar bilimlerinde yapılan en önemli keşiflerden birisi: Sözlük veri yapısı
- Neden sözlük? Bir sözlükte kelimeleri anahtar kılarak tanımlara erişiyoruz
- Anahtar ("key") → Değer ("value")
Söz dizimi basit, dizilerle karşılaştırın
turkish_to_english = {
'iyi' => 'good',
'kötü' => 'bad',
'çirkin' => 'ugly'
}
turkish_to_english['iyi'] #=> 'good'
turkish_to_english['güzel'] = 'beautiful'
- Sözlük (dictionary) yaygın kullanılan bir karşılık, fakat bununla sınırlı değil
- Bu veri yapısı, kullandığınız programlama diline göre, farklı adlarla karşınıza çıkabilir
Terminoloji
"Dictionary": Sözlük (ör. Python)
"Associative Array": İlişkili Dizi (ör. Bash)
"Map" veya "Mapping": Eşlem (veya Eşleme) (ör. Go, Java)
"Hash table" veya "Hash": Çırpma tablosu (?) (ör. Ruby, C#)
(sadece) "Table": Tablo (ör. Lua)
Ruby'de "Hash" adlandırması kullanılıyor
Neden "Hash"? Buna daha sonra değineceğiz
Türkçe'ye uygunluk açısından anlatımda "sözlük" adlandırmasını tercih edeceğiz
İsimler farklı olsa bile kavram aynı
Anahtar/Değer çiftlerinden oluşan bir koleksiyon
Öyle ki "değer"lere (genel olarak) dizgi türünde "anahtar"larla erişiyoruz
Anahtarların salt dizgi (string) olması gerekmiyor
Belirli bir koşulu sağlayan bir tür, bir nesne de anahtar olabilir
Ama bu bir parça ileri bir konu
Değerler için hiç bir kısıtlama yok, herhangi bir tür olabilir
Temel hareketler: İlkle
rehber = {
'annem' => '05051234567',
'babam' => '05331234567',
'kanka' => '05337654321'
}
Temel hareketler: oku/yaz
puts rehber['babam'] #=> '05331234567'
rehber['abim'] = '05333216547'
rehber['kanka'] = '05303216547'
Temel hareketler: Metotlar
p rehber.size
p rehber.keys
p rehber.values
p rehber.key? 'abim' #=> true
p rehber['olmayan'] #=> nil
Dikkat!
rehber['olan'] = nil
p rehber['olan'] #=> nil
p rehber.key? 'olan' # true
Temel hareketler: Dolaş
rehber.each do |isim, numara|
puts "#{isim}: #{numara}"
end
Temel hareketler: Dolaşırken değiştir
rehber.each_key do |isim|
rehber[isim] += '9'
end
each
,keys
vevalues
en temel metotlarkeys
vevalues
metotlarının birer dizi ürettiğine dikkat edinBu sayede bir sözlüğün anahtarları veya değerleri arasında dolaşabiliyoruz
Küçük bir optimizasyon
- Anahtarlar arasında dolaşmak için,
keys.each
yerine.each_key
- Değerler arasında dolaşmak için,
values.each
yerine.each_value
- Bu metotlar "biraz" daha hızlı sonuç veriyor
Sözlük veri yapısı neye yarar?
Çok, pek çok şeye yarar
Sözlük kullanmadan ciddi bir program yazmak çok zor
Böylesine önemli bir veri yapısı
Örnekleyeceğiz
Örnek: Tekilleştirme
meyveler = %w(
elma armut Elma kiraz şeftali karpuz karpuz kavun şeftali ARMUT
)
pazar= {}
meyveler.each do |meyve|
pazar[meyve.downcase] = true
end
p pazar.keys
Örnek: Kelime sıklığı
metin = <<MSG
Nush ile uslanmayanı etmeli tekdir; tekdir ile uslanmayanın hakkı kötektir.
MSG
frekans = {}
metin.split.each do |kelime|
kelime.delete! '.,;'
kelime.downcase!
frekans[kelime] = 0 unless frekans[kelime]
frekans[kelime] += 1
end
p frekans
Örnek: İl adlarını ve plaka numaralarını taşıyan plaka
isimli bir sözlük
plaka = {
# ...
'samsun' => 55,
# ...
}
Sözlük veri yapısının "arama" işleminde bir diziye göre çok daha çabuk sonuç verdiğini görebiliyor muyuz?
Örnek: Verilen bir şehrin listedeki varlığı?
iller_array = [
'samsun',
'ankara',
'istanbul',
'izmir'
]
iller_hash = {
'samsun' => true,
'ankara' => true,
'istanbul' => true,
'izmir' => true
}
Örnek: Anagram kelime grupları
# İpucu: Bir kelimeyi harf dizisine dönüştürmek için kelime.split('')
# kullanabilirsiniz. Anagram kelimeyi nasıl tespit edersiniz?
kelimeler = %w[
demo none tied evil
dome mode live fowl
veil wolf diet vile
edit tide flow neon
]
# Bulunması gereken dizi (her anagram kelime grubu bir alt dizi)
anagramlar = [
['demo', 'dome', 'mode'],
# ...
]
Örnek: Basit veritabanı (evet, bir sözlük basit bir veritabanıdır)
ogrenciler = {
'aekinci' => {
'isim' => 'Ahmet Ekinci',
'yas' => 23,
'cinsiyet' => 'erkek',
'aldigi dersler' => ['Matematik','Tarih','Fizik']
},
'yhas' => {
'isim' => 'Yusuf Has',
'yas' => 26,
'cinsiyet' => 'erkek',
'aldigi dersler' => ['Coğrafya','Türkçe','Matematik']
}
}
Enumerable modülünde dizilerden aşina olduğunuz pek çok metot sözlükler için de geçerli
select
,map
,all?
,any?
Tek fark: dizide bu metotlar tek argüman alırken sözlükte bir argüman çifti (sırasıyla anahtar ve değer) alıyor
Bunu kavradığınızda kalanı kolay
Sözlüklerde bir işlem var ki çok önemli: merge
İki sözlüğe (genelleştirecek olursak, birden fazla sözlüğü) birleştirerek tek sözlük yapmak
Sözlüklerde o sözlüğe özgü anahtarlar sonuç olarak üretilen birleşik sözlükte de bulunur
Peki, ortak anahtarlar? Her iki sözlükte de aynı anahtar farklı değerlerle yer alıyorsa?
Örnek: İki sözlüğü tek bir sözlükte birleştir
redhouse = {
'iyi' => 'good',
'kötü' => 'bad',
'çirkin' => 'ugly',
'güzel' => 'beautiful'
}
fono = {
'iyi' => 'angel',
'kötü' => 'evil',
'çirkin' => 'ugly',
'akıllı' => 'smart'
}
Sembol
Ruby'ye pek özgü bir veri türü
"Dizgimsi" bir şey: değiştirilemez ("Immutable") bir tür "dizgi"
Ruby öğrenenlerin anlamakta zorlanabileceği bir veri türü
Ruby'de sözlük anahtarları başta olmak üzere pek çok yerde kullanılıyor
'sunday'.class #=> String
:sunday.class #=> Symbol
'sunday'.to_sym #=> :sunday
:sunday.to_s #=> "sunday"
Sözdizimi
Dizginin başına, şayet boşluk içermiyorsa,
:
ekliyoruzDizgide boşluk varsa tırnak içine alıyoruz
(Tercihen) ASCII olmayan karakterler varsa onu da bir önceki kurala uygun yazıyoruz
:sembol
:'bu bir sembol'
:türkçe
:'türkçe'
Sözlük anahtarlarında yaygın kullanıyoruz
Dizgi anahtarlarla
redhouse = { 'good' => 'iyi', 'bad' => 'kötü', 'ugly' => 'çirkin', 'beautiful' => 'güzel' } redhouse['bad'] #=> "kötü"
Sembol anahtarlarla
redhouse = {
:good => 'iyi',
:bad => 'kötü',
:ugly => 'çirkin',
:beautiful => 'güzel'
}
redhouse[:bad] #=> "kötü"
Ruby 1.9 sürümünde gelen bir kısayolla :key => value
yerine key: value
yazabilirsiniz ve (yaygın stil olarak) yazmalısınız
redhouse = {
good: 'iyi',
bad: 'kötü',
ugly: 'çirkin',
beautiful: 'güzel'
}
redhouse[:bad] #=> "kötü"
Neden "sembol"?
İki temel nedeni var (öncelik sırasıyla):
Okunurluk: Sembol kullanımı yoluyla "niyet daha iyi ifade ediliyor"
Başarım: (Sözlük erişimlerinde) başarım daha yüksek
1 Okunurluk
If the contents (the sequence of characters) of the object are important, use a string. If the identity of the object is important, use a symbol.
— Jim Weirich (Ruby Cookbook)
Mealen: Dizgimsi bir ögenin içeriğiyle değil de temsil ettiği kimlikle ilgileniyorsanız sembol, aksi halde dizgi kullanın.
Aşağıdaki sembol örneklerinde ilgili kelimeler bir şeyleri temsil ediyor, kelime içeriğiyla harf harf ilgilenmiyoruz
Haftanın günleri:
:sunday
,:monday
, ...Durum:
:enabled
,:disabled
Nitelik:
:color
,:age
,:gender
,:weight
,:fullname
İsim:
:HTTP
,:SSH
person = {
fullname: 'Cezmi Seha Sahir',
age: 54
}
:fullname
sembolüperson
nesnesindeki bir niteliği temsil ediyor, bu nedenle'fullname'
demiyoruzÖte yandan
'Cezmi Seha Sahir
' dizgisi bir veri, içeriğiyle ilgileniyoruz
Sembollerin "Okunurluk" rolünün en azından bir kısmı diğer programlama dillerinde çok kaba bir benzerlik olarak "Enum" veri türüne karşı düşer
Ruby'de "Enum" veri türü yoktur
Diğer dillerden bilgi transferi yaparken bu argümanı göz önünde bulundurarak sembol kullanımını bir seçenek olarak değerlendirin
2 Başarım
require 'benchmark'
count = 10000000
symbol = :symbol
string_mutable = "string"
string_frozen = "string".freeze
Benchmark.bm do |bm|
bm.report('Symbol:') do
count.times { symbol.hash }
end
bm.report('String Mutable:') do
count.times { string_mutable.hash }
end
bm.report('String Frozen:') do
count.times { string_frozen.hash }
end
end
user system total real
Symbol: 3.263617 0.000000 3.263617 ( 3.263620)
String Mutable: 3.998714 0.000000 3.998714 ( 3.998727)
String Frozen: 3.990479 0.000000 3.990479 ( 3.990506)
Örnekteki başarım ölçümüne göre
hash
hesaplarında semboller değişmez ve değişebilir dizgilere göre%25
daha hızlıDuruma göre bu başarım çok daha fazla olabilir
Neden?
Semboller aslında bir tür tamsayı
Ruby yorumlayıcısı kodu yüklerken tüm sembolleri tek seferliğine bir tür tamsayıya dönüştürüyor
"Hash" fonksiyonu zaten bir tamsayı hesaplar
Bir tamsayının "hash" değerini hesaplamak bir dizginin "hash" değerini hesaplamaya göre daha hızlıdır
Çünkü yapılması gereken işlem dizgiye göre daha basittir
Dizgide tüm içerik (her bir karakter) dikkate alınarak işlem yapılıyor
Hash fonksiyonu nerede kullanılıyor? Sözlük erişimlerinde
Bir sözlükte anahtar üzerinden değere erişirken önce daima anahtara bir "hash" fonksiyonu uygulanır
Anahtar bir sembol ise bu işlemin süresi de daha kısa oluyor
Ama unutmayın:
Öncelikli amacımız okunurluğu arttırmak
Semboller kodu okuyan kişiye "burada bir şeyi temsil ediyorum" niyetini iletiyor
Ruby'nin son sürümlerinde başarım açısından iki yöntem arasında devasa bir farklılık yok
Bu nedenle başarımdan ziyade okunurluk amacını daha kıymetli buluyoruz
Sembollerin kullanımı sadece sözlük anahtarıyla sınırlı değil
Bir değişken veya metot adı, özetle "identifier" kabul eden her işlemde ilgili tanımlayıcıyı sembolle temsil ediyoruz
%w[foo bar].map(&:upcase) #=> ['FOO', 'BAR']
- Bakın bu örnekte eşleme yapılırken kullanılacak süzgecin (bir metot) bilgisi
map
metoduna bir sembolle iletiliyor::upcase
(baştaki&
ayrı bir bilgilenmenin konusu)
Örnek: Varsayılan sayfa ayarları
DEFAULTS = {
paper: :A4,
layout: :portrait,
numbering: true,
cover: true
}.freeze
Verilen ayarlarla çıktı alan printout
adında bir metod yazalım, öyle ki bir ayar verilmediğinde varsayılan değer esas alınsın
printout 'foo.pdf', layout: :landscape
Örnek: Basit öğrenci kaydı 1
Ahmet Fuat 1234567
Ayşe Begüm Toprak 3456712
Fatma Çetin 2346567
Muzaffer Talha Emir Gündüz 5452135
liste.txt
isimli bir dosyada bulunan öğrenci listesini ayrıştırarak basit bir öğrenci veritabanı (sözlük) oluşturalım
Sözlüklerde sıra
Pek çok programlama dilinde sözlük veri yapısı sıralı değildir
Yani anahtar veya değerleri bir dizi halinde almak istediğinizde sözlüğe ekleme sırasıyla gelmez
Neden? Başarım
İlgili programlama dilinin sözlük veri yapısının gerçeklemesindeki başarım sırasız halde daha yüksektir
Sözlükleri sırasız olan programlama dillerinde sıralı sözlükler için farklı düzenlemeler sunulur
Ayrı bir "sıralı sözlük" veri yapısı veya sırayı tutan ilave bir anahtar dizisi
Önerme: Ruby'de sözlükler sıralıdır
- Anahtar ve değerleri ekleme sırasıyla alırsınız
Struct
Point = Struct.new :x, :y
p = Point.new 3, 5
pp p
Point
?Struct
tarafından üretilen bir sınıfStruct
sizin adınıza basit bir nesne (nesnenin planı olan sınıfı) oluşturuyor
Ruby'de veri kapları (data container) kurmanın etkili bir yolu
Diğer dillerde de benzer veri türleri var, ör. C'de
struct
Sadece veri tutan, davranışın ön planda olmadığı basit nesneler oluşturmak için yararlı
Bu yönüyle sözlükler ve tam teşekküllü nesneler arasında bir yerde konumlanıyor
Ne zaman Struct
?
Veri bileşenleri (nitelikler) sabit sayıda ve önceden biliniyor
Davranış ön planda değil: nesne metotları yok veya çok az sayıda
Sözlük alternatifi bir parça ilkel duruyor
Struct
başarımı yüksek bir gerçeklemeye sahip, olağan Ruby nesnelerine göre erişimler daha hızlıNeden? C ile gerçeklenmiş bir nesne gibi bakılabilir
Nitelikleri isimlendirilmiş argümanlarla verebilirsiniz
Point = Struct.new :x, :y, keyword_init: true
p = Point.new x: 3, y: 5
Nitelik sayısı arttığında veya okunurluğu iyileştirmek için tercih ediyoruz
Bu yöntemi kullandığınızda diğer yöntem (pozisyonel nitelikler) kullanılamaz, buna dikkat edin
Struct
ile oluşturduğunuz nesnelerde davranış metotları tercihen hiç olmamalıHesap edilen nitelikleri ("computed attributes") hariç tutuyoruz, bunlar davranış değil
Person = Struct.new :name, :surname do
def fullname
"#{name.capitalize} #{surname.capitalize}"
end
end
person = Person.new 'ahmet', 'fuat'
person.fullname #=> "Ahmet Fuat"
- Bu örnekte
fullname
sık hesaplanacak bir nitelikse bunu nasıl hızlandırabiliriz?
Davranış varsa ("eser miktarda" olanlar belki hariç) "tam teşekküllü" bir nesne tercih edin
Point = Struct.new :x, :y do
def distance(other)
Math.sqrt((other.x - x)**2 + (other.y - y)**2)
end
end
p = Point.new 3, 5
q = Point.new 6, 9
p p.distance(q)
Point
nesnesi karmaşıklaşma eğiliminde, bugündistance
yarın?
Örnek: Basit öğrenci kaydı 2
Öğrenci temsilinde sözlük (Hash
) yerine Struct
kullanalım
Modüller
Ruby'nin modüler programlama için ("sınıf"larla birlikte) sunduğu imkan
module A
def self.meth
...
end
end
A.meth
İki önemli amaca hizmet ediyor:
İsim uzayını düzenliyor
"Katıştırma" ("Mixins") tekniğiyle gerçeklemelerin paylaşılmasını sağlıyor
İsim uzayı
sin
metoduMath
modülünde tanımlı, ör.Math.sin(0)
Bu sayede:
Kod okunurluğu artıyor: modül ismi metod ismiyle sınırlı anlamı pekiştiriyor
İsim çakışmaları önleniyor:
sin
isimli bir metod yazdığımızı varsayın?
sin
: "sinus", "sin" (günah)?
def sin(context)
warn.puts "Hatalı bir eylem gerçekleşti: #{context}"
end
...
x = Math.sin(teta)
if x > 0.5
sin("hatalı aralıkta değer üretildi: #{x}")
end
sin
: "sinus", "sin" (günah)?
module Log
...
def self.sin(context)
warn.puts "Hatalı bir eylem gerçekleşti: #{context}"
end
end
x = Math.sin(teta)
if x > 0.5
Log.sin("hatalı aralıkta değer üretildi: #{x}")
end
Log
modül isminin anlamı pekiştirdiğine dikkat edin
require
log.rb
dosyası
module Log
...
def self.sin(context)
warn.puts "Hatalı bir eylem gerçekleşti: #{context}"
end
end
Müşteri kodu (log.rb
modül yollarında tanımlı)
require 'log'
x = Math.sin(teta)
if x > 0.5
Log.sin("hatalı aralıkta değer üretildi: #{x}")
end
- Bu haliyle hata verir, neden?
Ruby modülleri
$LOAD_PATH
değişkeninin gösterdiği dizinlerde aranırBulunduğunuz dizin öntanımlı olarak
$LOAD_PATH
'te kayıtlı değildirBulunduğunuz dizindeki
log.rb
modülü yüklenemezrequire 'log'
satırının çalışması için bulunduğunuz dizini yükleme yoluna ekleyin$LOAD_PATH.unshift __dir__
require_relative
Dosya yolu göreceli modülleri $LOAD_PATH
değişkenine dokunmadan yükleyebiliriz
require_relative 'log'
x = Math.sin(teta)
if x > 0.5
Log.sin("hatalı aralıkta değer üretildi: #{x}")
end
Düzenli ifade
Regular expression
Metin işleme için geliştirilmiş bir tür DSL (Alana özgü dil)
Bir metinde (bir dizgi) aradığınız bir alt metni tanımlayan bir "desen" ("pattern")
Bu sayede bul (find), bul/değiştir (find/replace) işlemleri yapabiliyoruz
Hemen hemen her programlama dilinde destekleniyor
Anlatıma örnekleyerek devam edelim:
- https://rubular.com/
- https://regexr.com/
Örnekler:
Dizgi (en azından ilk aşama doğrulama olarak) geçerli olabilecek bir e-posta mı?
Dizgide (ör. bir dosya adı) boşluk ve Türkçe karakter var mı?
Telefon numarası geçerli mi?
gsub
"Global Substitute"
String
sınıfında tanımlı bir nesne metotuSıklıkla kullandığımız bir metot
"hello".gsub(/[aeiou]/, '*') #=> "h*ll*"
"hello".gsub(/([aeiou])/, '<\1>') #=> "h<e>ll<o>"
"hello".gsub(/./) {|s| s.ord.to_s + ' '} #=> "104 101 108 108 111 "
"hello".gsub(/(?<foo>[aeiou])/, '{\k<foo>}') #=> "h{e}ll{o}"
'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*') #=> "h3ll*"
Örnek: Bir dizgideki Türkçe karakterleri yaklaşık ASCII eşdeğerleriyle değiştir
asciify('Şule İsmet Yılmaz') #=> Sule Ismet Yilmaz
Örnek: CSV formatında bir öğrenci listesinden tanımlayıcı listesi üret
(Öğrenci Otomasyonundan indirilen) Girdi:
#,ID,Öğrenci No,Ad,Soyad,OMU Mail
1,11111111,77777777,MERVE,EREN,77777777@stu.omu.edu.tr
2,11111112,88888888,Qossay G. M.,ABULAYMOUN,88888888@stu.omu.edu.tr
1,11111113,999999,MEHMET AKİF,CEBECİ,99999999@stu.omu.edu.tr
Çıktı:
MERVE-EREN-77777777
QOSSAY-G-M-ABULAYMOUN-88888888
MEHMET-AKIF-CEBECI-99999999
Nesneler
"Ruby'de her şey birer nesne"
Nesne (object)? Nitelik ("attribute") ve davranışları/eylemleri ("method") olan "şey"
Nitelik ve davranışlara nasıl erişiyoruz?
nesne.metot
veyanesne.nitelik
(Ruby'de
.
gösteriminde sağdaki kelime teknik olarak daima bir metot; ama şimdilik bunu göz ardı edelim)Örneğin
'foo'.upcase
'de'foo'
bir dizgi nesnesi,upcase
ise bu nesne üzerinde etki gösteren bir eylem
Nesneyi anlamak nispeten kolay, ama... Ruby'de bir nesneyi yöneten kod nerede?
Beklentimiz "nesneyi yöneten" bu kodda nitelik ve metotları bulmak
Örneğin
'foo'.upcase
'deupcase
metotu nerede olabilir?(Evet, Ruby standart kitaplığında ama orada nerede?)
Dizgi nesnesi derken bir "tip"den ("type") bahsediyoruz, "dizgi" tipi
Aynı tipte çok sayıda nesne var olabilir
'foo'
,'bar'
ve'baz'
dizgi nesnelerini düşününTüm bu nesneler "dizgi" olma temelinde ortak ama içerikleri farklı
"Dizgi olma temelinde ortaklık"? Örneğin tüm dizgi nesneleri
upcase
metotuna cevap veriyor
Demek oluyor ki aradığımız "nesneyi yöneten kod" aslında bu "ortaklığı" yönetiyor
Bir yerlerde öyle bir kod var ki tüm dizgi nesnelerinde ortak olan davranışı kodluyor
İşte buna "class" diyoruz
Nesneleri ortak özelliklerine göre "sınıf"ladığımızı düşünün, ne cümleler kurabiliriz?
"Tam sayı sınıfında nesneler", "Dizgi sınıfında nesneler"
Bu anlatıma göre: "sınıf" ("class") ve "tip" ("type") terimlerinin neredeyse eş anlamlı
Evet, eş anlamlı; sadece farklı mahallelerin konuşma biçimleri
Mahalle? "Nesne Yönelimi Programlama" mahallesi
Bu konuya döneceğiz
Örnek: Düzlemde bir noktanın temsili
Kartezyen koordinatların kullanıldığı bir düzlemde bir
p
noktasını ele alalımp
noktasının nitelikleri?x
vey
; bu niteliklerep.x
,p.y
olarak erişiyoruzp
bir "Nokta" tipinde bir nesne; bu tip başka pek çok nokta nesnesi olabilirBir resmi düşünün, nokta nesnelerinden oluşan bir başka "şey" ("nesne")
Sorular
"Bir yerlerde öyle bir kod var ki tüm nokta nesnelerinde ortak olan davranışı kodluyor"
Bu yer neresi? Öyle ki
x
,y
niteliklerini bulalımBir nokta nesnesini nasıl oluşturacağız?
class Nokta
def initialize(x_degeri, y_degeri)
@x_niteligi = x_degeri
@y_niteligi = y_degeri
end
def x
@x_niteligi
end
def y
@y_niteligi
end
end
p = Nokta.new 3, 5
p.x #=> 3
p.y #=> 5
Cevaplar
"Bir yerlerde öyle bir kod var ki tüm nokta nesnelerinde ortak olan davranışı kodluyor"
Ruby'de bu yer bir
class
(module
benzerliğine dikkat edin)Nitelikler özel bir sözdizimiyle gösteriliyor (ör.
@x_niteligi
)Bir nokta nesnesi
Nokta
sınıfından "üretilen" bir şey:new
metoduylaBu şekilde istediğiniz sayıda (farklı veya aynı koordinatlara sahip) nokta nesnesi üretebilirsiniz
Sınıf
"Nesneyi yöneten kod"
(Veya) Aynı "tip"te ("sınıfta") nesneler arasındaki "ortaklığı" yönetiyor
"Sınıf"a bir binanın mimari çizimi gibi bakabilirsiniz
Elimizde bir "tip" binanın mimari planı var, örneğin TOKİ konutu
Bu plandan yararlanarak pek çok şehirde TOKİ konutları inşa edebiliriz
Tek plan (sınıf) → Çok sayıda konut (nesneler)
Tüm konutlar benzer özellikte (biçim, boyut vs), çünkü aynı "sınıf"da
Ama inşa edildiği yerlerin farklı olması yönüyle her bir konut ("nesne") eşsiz, kendine özgü
Plandan yararlanarak konutun inşa edilmesi? "construction"
İşte bu
new
çağrısına karşı geliyor
# Mimari plan
class TOKI
def initialize(kent)
@kent = kent
end
def metrekare
100
end
end
# İnşaat
samsun_toki = TOKI.new :samsun
'foo'.upcase
'de upcase
nerede? Standart kitaplıkta bulunan String
adında bir sınıfta
(Hayali bir kod yazalım; gerçek biraz farklı)
class String
def initialize(dizgi)
@dizgi = dizgi
end
def upcase
@dizgi.chars.map(&:ord).map { |ord| ord >= 'a'.ord ? ord - 32 : ord }.map(&:chr).join # Sağlığa zararlı kod
end
end
Ama dizgi oluştururken new
kullanmadık?
kelime = 'foo'
kelime.upcase #=> "FOO"
Evet, çünkü bu sık yapılan bir işlem ("dizgi literalleri"); bir kısayol konulmuş
kelime = String.new 'foo'
kelime.upcase #=> "FOO"
Nesne Yönelimli Programlama
Object Oriented Programming
Problemdeki unsurları (ve çözüm olarak sunulan unsurları) "nesneler" halinde görmeye eğilimli programlama tarzı
Bir programlama metodolojisi
Karmaşıklıkla mücadelede kullanılan bir düşünme enstrümanı
Nesne Yönelimli Programlama dersini bu konuya ayırdık
Range
(-1..-5).to_a #=> []
(-5..-1).to_a #=> [-5, -4, -3, -2, -1]
('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a #=> ["a", "b", "c", "d"]
Set
require "set"
Set.new([1, 2]) #=> #<Set: {1, 2}>
Set.new([1, 2, 1]) #=> #<Set: {1, 2}>
Set.new([1, 'c', :s]) #=> #<Set: {1, "c", :s}>
Set.new(1..5) #=> #<Set: {1, 2, 3, 4, 5}>
Set.new([1, 2, 3]) { |x| x * x } #=> #<Set: {1, 4, 9}>
require "set"
s1 = Set[1, 2] #=> #<Set: {1, 2}>
s2 = [1, 2].to_set #=> #<Set: {1, 2}>
s1 == s2 #=> true
s1.add("foo") #=> #<Set: {1, 2, "foo"}>
s1.merge([2, 6]) #=> #<Set: {1, 2, "foo", 6}>
s1.subset?(s2) #=> false
s2.subset?(s1) #=> true
case
/when
if name == "Clark Kent"
hero_name = "Superman"
elsif name == "Peter Parker"
hero_name = "Spiderman"
elsif name == "Bruce Wayne"
hero_name = "Batman"
else
hero_name = "unknown"
end
case name
when "Clark Kent"
hero_name = "Supername"
when "Peter Parker"
hero_name = "Spiderman"
when "Bruce Wayne"
hero_name = "Batman"
else
hero_name = "unknown"
end
hero = case name
when "Clark Kent" then "Supername"
when "Peter Parker" then "Spiderman"
when "Bruce Wayne" then "Batman"
else "unknown"
end
ARGV
, $PROGRAM_NAME
puts $PROGRAM_NAME
ARGV[0].to_i.downto(ARGV[1].to_i) { |i| puts i }