Jump to page: 1 2
Thread overview
switch()'in aralıklı kullanımı
Sep 02, 2012
Salih Dinçer
Sep 02, 2012
erdem
Sep 02, 2012
Salih Dinçer
Sep 02, 2012
Salih Dinçer
Sep 02, 2012
Salih Dinçer
Sep 02, 2012
Salih Dinçer
Sep 03, 2012
erdem
Sep 20, 2012
Salih Dinçer
September 02, 2012

Hassas durumlarda, switch()'in aralıklı (case X: .. case Y:) kullanımından uzak durun!

Şöyle bir test kodumuz var:

import core.stdc.stdio: printf;

void main() {
 char harf = 64; // Octal 0x100; @ işareti ve devamında A var

 while(harf) {
   harf++;

   //* toggle-code on
   if(harf > 64 && harf < 91) {
     printf("Büyük Harf: %c\n", harf);
   } else if(harf > 96 && harf < 123) {
     printf("Küçük Harf: %c\n", harf);
   }/*/
   switch(harf) {
     case 'A': .. case 'Z':
       printf("Büyük Harf: %c\n", harf);
       break;
     case 'a': .. case 'z':
       printf("Küçük Harf: %c\n", harf);
       break;
     default:
   }//*/
 }
}

İlk durum (toggle-code off) yani baştaki kod öbeğini (snippet) derlediğinizde iki koşul ifadesinden biri şartı sağladığında ekrana Büyük veya Küçük harfleri yazmakta. Aynı işi ikinci durum (toggle-code on) da yapmakta ama diğerinden daha uzun süren bir yöntemle:

Look-up Table

Önce tüm olasılıkları (27*2+1) assembly'deki .rodata bölümüne (segment) dizge olarak yerleştiriyor. Bunu, yanıdaki "Büyük, Küçük, Harf ve %c" ifadelerinden anlıyoruz. Çünkü bu veriler de program işletilirken ekrana yazılmak üzere kullanılıyor. Sonra ana döngüden bağımsız başka bir döngü içinde ve her seferinde bunu tekrar ederek tüm olasılıkları karşılaştırıyor.

Ölme eşeğim ölme...:)

Tabi switch() case'in bu kullanımı bir kolaylık. Sizi fazla düşündürtmeden amacınıza hızlı bir şekilde ulaşmanızı sağlıyor. Hatta, bunun if() gibi ayrı bir komut olmadığını, derleyicinin kafasına göre if() ve koşul verilerinden oluşan bir öbek (snippet) meydana getirdiği sonucunu pekala çıkartabiliriz. O yüzden dikkatli kullanmakta fayda görüyorum. Hatta !!!

Evet, hatta...:)

Örneğin ilk bölümünde if()'leri ayrı ayrı kullanmayın! Eğer birbirleriyle ilintiliyse mutlaka else if() şeklinde devam edin. Çünkü bu ikisi arasında çok küçük (tek satır!) ama büyük olabilecek bir fark var! O da her iki assembly öbeği arasına bir dallanma satırı ('jmp short LC') eklemesi!

Buradaki LC ilk if() ifadesi. Oysa else if() ile ikinci ifadeyi yazsaydık bu satır olmayacaktı. Olmadığında da 1.'yi denetledikten sonra ikinciye bakması (zaten biz bunu istiyoruz!) hem de dolanmadan. Çünkü bahsettiğim ek dallanma satırı olunca ilk ifadeyi kontrol ettikten sonra tekrar kendi içine dönüyor ve oradaki bir kaç satırı işlettikten sonra ikinci ifadenin (L35) başına dallanıyor.
'(Yaş 35, yolun yarısı edermiş, kelaka...:))'

İşte assembly kodu:

'_Dmain:
push EBP
mov EBP,ESP
sub ESP,4
push EBX
push ESI
mov byte ptr -4[EBP],040h
LC:
cmp byte ptr -4[EBP],0
je L57
inc byte ptr -4[EBP]
mov AL,-4[EBP]
cmp AL,040h
jbe L35
cmp AL,05Bh
jae L35
movzx ECX,byte ptr -4[EBP]
push ECX
mov EDX,offset FLAT:.rodata[026h]@SYM32
push EDX
call printf@PC32
add ESP,8
' jmp short LC'
L35:
mov BL,-4[EBP]
cmp BL,060h
jbe LC
cmp BL,07Bh
jae LC
movzx ESI,byte ptr -4[EBP]
push ESI
mov EAX,offset FLAT:.rodata[05Ch]@SYM32
push EAX
call printf@PC32
add ESP,8
jmp short LC
L57:
xor EAX,EAX
pop ESI
pop EBX
leave
ret'

Dip Not: Örnekte özellikle std.stdio; sınfını kullanmadım çünkü assembly kodu iyice karışıyor. Hata bulmak için yapacağınız bu tür denemelerde mümkünse writefln() gibi komutları alias ile gölgeleyin. Yani printf() işlevine yönlendirin....

Başarılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Tam olarak konuyu anlamamış olabilirim. Ama her iki ifade de aynı sürede çalıştırılıyor gibi geldi:
Alıntı:

>

$ time ./test
real 0m0.004s
user 0m0.000s
sys 0m0.000s

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Bu bahsettiklerim süresini ölçemeyeceğimiz müddetçe (cycle time) gerçekleşen bir örnek. Bu şekilde ölçülebileceğini zannetmiyorum...

İnceleme yazısında bahsettiğim olayın farkını bilmekte fayda var. Çünkü switch() case deyimi if() ve look-up table'lardan oluşan bir yapı. Hatta çoğu zaman uzun mantıksal ifadeler (a == 3 || a == 5 || a == 11 || a == 41) ile yaptığımızdan farklı değil. Tek farkı programcı tarafındaki yazım şekli. Şu örnek aslında tek satırla yazılan if()'den farkı yok:

final switch(a) {
 case 3, 5, 11, 41:
   writefln("%d: Bu 6765 sayısının çarpanlarından biridir...", a);
}

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Yazdıklarımı denemeden yazıyorum ve dediğin o ingilizce şeylerin hiçbirini anlamadan tahmin yürüterek ve yine ayrıca assembly bilmeden yazıyorum:

İlk önce eğer gerçekten gerekmiyorsa

harf++;

değil

++harf

kullanmak çok daha mantıklıymış. http://ddili.org/ders/d/aritmetik_islemler.html

sanırım ++a => a+=1; demek
ama a++ => return a; a+=1; demek. Çok küçük hız ve büyük bir anlam farkı var. C++ adı kötü olmuş o zaman :-D

if yerine switch kullanılabiliyorsa Ali Beyin dediğine göre if kullansak bile D switch kullanmışız gibi derlermiş. Çünkü ifte iki değişken karşılaştırılırken switchte tek değişken bir sabitle karşılaştırılıyor. Bu yüzden switch daha hızlı çalıştığından ifler gerçekleşebiliyorsa zaten switche çevrilirmiş.

if else ifte ikinciye bakmaması gayet normal ve iyi bir davranış:

zaten aslında bizim standartımızda yazılışı
if()
{
//birşeyler
}
else
{
if()
{
//birşeyler
}
}
dır.

Eğer else if kullanırsak ilki gerçekleşirse else ife bakılmaz. Ancak sade if kullanırsak ilk if ile hiç bir alakası olmaz. if -> eğer else if-> o olmadıysa ama bu şart olursa(yok eğer) else-> hiç olmazsa anlamında.
Alıntı:

>

Çünkü switch() case deyimi if() ve look-up table'lardan oluşan bir yapı. Hatta çoğu zaman uzun mantıksal ifadeler (a == 3 || a == 5 || a == 11 || a == 41) ile yaptığımızdan farklı değil. Tek farkı programcı tarafındaki yazım şekli. Şu örnek aslında tek satırla yazılan if()'den farkı yok:

Aslında farkı var. Çünkü switch ile tek değişkenin değerine bakacağına ve o değer eşit mi diye bakarken kullanacağın değerin sabit olduğunu derleyiciye garanti ediyorsun. İfte garanti etmiyorsun. İf ile İki üç tane değişkende kullanabilirdin. Karşılaştırdığın değerlerde değişken olabilirdi. İf sadece içindeki mantıksal ifadeyi işletir true ise içindeki kodu işletir değilse işletmez. Ancak switch öyle bir şey yapmaz. Ama derleyici bizim için abilik yapıp if ile kullandığın kodu switch ile kullanabiliyorsan kod hızlansın diye o kodu senin yerine switch'e çeviriyor. Yani derleyici senin için eniyileştirme yaptı.
Yani aslında farkı yok :-) Yani varda yok ta ama yokta var. Yani aslında yumurta tavuk meselesi. Belki olaya horoz gibi bakmak lazım :-D

Benim bildiklerim bunlar. Yanlışım varsa düzeltirsiniz :-)

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Bu arada bunn gibi tamamen D ile ilgili konular bence ders arası yada D programlama dili forumlarına taşınmalı. İzin almadan bir şey yapmak istemedim.

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Alıntı (canalpay):

>

Aslında farkı var. Çünkü switch ile tek değişkenin değerine bakacağına ve o değer eşit mi diye bakarken kullanacağın değerin sabit olduğunu derleyiciye garanti ediyorsun. İfte garanti etmiyorsun. İf ile İki üç tane değişkende kullanabilirdin. Karşılaştırdığın değerlerde değişken olabilirdi. İf sadece içindeki mantıksal ifadeyi işletir true ise içindeki kodu işletir değilse işletmez. Ancak switch öyle bir şey yapmaz. Ama derleyici bizim için abilik yapıp if ile kullandığın kodu switch ile kullanabiliyorsan kod hızlansın diye o kodu senin yerine switch'e çeviriyor. Yani derleyici senin için eniyileştirme yaptı.
Yani aslında farkı yok :-) Yani varda yok ta ama yokta var. Yani aslında yumurta tavuk meselesi. Belki olaya horoz gibi bakmak lazım :-D
Evet fark var ama çok küçük ve bunu ilerleyen satılarda göstereceğim. Ama yumurta&tavuk meselesi kadar ikilemde kalacağımız bir mesele değil. Çünkü assembly koduna bakınca çok net ve kesin bir dille ifade edilebilir...

Örneğin D'nin ürettiği kodlara bakınca, rahatlıkla "switch diye bir şey yok" diyebiliyorum. Bunlar sadece bir dil olanağı. Sen ise bazı if()'lerin switch'e çevrildiğinden bahsediyorsun. Bence böyle bir şey yok ama tersi var...:)

Ayrıca i++ ile ++i arasında görünüm dışında hiç bir fark yok! Belki eskiden varmış ama şu an yok. Çünkü ikisi de arttırma (INC) komutunu kullanıyor. Merak ediyorsan assembly kodunu inceleyebilirsin.

Şu küçük farkı da aşağıya nakledeyim ama ikisi de birbirine çok benzer olduğu için sadece switch() case halini yapıştıracağım:

void main() {
int a = 41;
//if(a==3 || a==5 || a==11 || a==41) printf("-ok-\n");/*
final switch(a) case 3, 5, 11, 41: printf("-ok-\n");//*/
} /* Assembly karşılığı:
   _Dmain:
     push EBP
     mov  EBP,ESP
     mov  EAX,029h
     cmp  EAX,3
     je   ekranaYaz
     cmp  EAX,5
     je   ekranaYaz
     cmp  EAX,0Bh
     je   ekranaYaz
     cmp  EAX,029h
   '---------- SON SORGU ----------
     je   ekranaYaz ' if() sorgusunda jne oluyor
     push 7
     mov  ECX,offset FLAT:_D4dene12__ModuleInfoZ@SYM32
     push ECX
     call _d_switch_error@PC32
   '---------- SON SORGU ----------
   */

Dip Not: Yukarıdaki kodlarda SON SORGU olarak ifade edilen kısım if()'li sürümünde bulunmamakta. Sadece jne ekranaYaz satırı yer almakta. Bu durumda switch deyimini kullanmanın başka bir farkı da ortaya çıkıyor. Olası hatalara karşı bitiş bölümü (final takısı) ya da default: etiketi deneteleniyor. Yani _d_switch_error altyordamı çağrılıyor.

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Bu arada assembly bilmeyenler için komutların anlamlarını satır satır yazarak açıklamaya çalışayım:

  • 'push EBP' satırı, ana işaretçinin (BasePointer) gösterdiği adresin yedeği alınır
  • 'mov EBP,ESP' satırı, yığının son kaldığı yer (Stack Pointer) kaydedilir (EBP = ESP)
  • 'mov EAX,029h' satırı, AX yazmaçına 41 sayısı yerleştirili
  • 'cmp EAX,3' satırı, yazmaçtaki değer 3 sayısı ile karşılaştırılır (41 == 3 ?)
  • 'je ekranaYaz' satırı, eşitse altyordama dallanır
  • 'cmp EAX,5' satırı, yazmaçtaki değer 5 sayısı ile karşılaştırılır (41 == 5 ?)
  • 'je ekranaYaz' satırı, eşitse altyordama dallanır
  • 'cmp EAX,0Bh' satırı, yazmaçtaki değer 11 sayısı ile karşılaştırılır (41 == 11 ?)
  • 'je ekranaYaz' satırı, eşitse altyordama dallanır
  • 'cmp EAX,029h' satırı, yazmaçtaki değer 41 sayısı ile karşılaştırılır (41 == 41 ?)
  • 'je ekranaYaz' satırı, eşitse altyordama dallanır

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 02, 2012

Alıntı (acehreli):

>

Hele case'in aralıklı kullanımı gibi çok yeni bir olanağa bakınca Salih'in bulgularına şaşırmamak gerekse de şaşırıyorum: :) dmd nasıl olur da o case'i if'li eşdeğerine dönüştürmez? Çok basitçe yapabilirmiş gibi geliyor... (?)
Sanırım aralık işaretini (..) görünce derleyici otomatik olarak "look-up table" oluşturmaya başlıyor. İşte o yüzden biz programcılık tarihinde switch() case'in hızlı olduğunu söylüyoruz. Ancak bahsi geçen aralık iki koşul ile ifade edilebildiğini, DMD'nin genç bir dil olmasından dolayı tercih etmediğini görüyoruz. Belki de bunu tespit etmesi (sense) zor bir olgudur?

Alıntı (acehreli):

>

Peki ben soruyu tersten sorayım: i++ ifadesini ++i ifadesine yeğliyor musun, yoksa önemsizce öylesine mi yazıyorsun? Eğer i++ ifadesini yeğliyorsan neden yeğliyorsun? :) (Bunu yeğleyen çok programcı var.)
Çoğunlıkla fark olmadığı durumlarda i++; kullanımı daha çok hoşuma gidiyor. Belki bir benzeri olan i+=1;'e dönüşümün kolay olmasın dolayı tercih ediyorum.

Elbette bazı kullanımlarında fark var. Örneğin writeln(++i) yapmamız, INC komutunu CALL'ın önüne alır. Açıkçası for() içinde kullanımında ben bir fark göremedim. Sadece göze öyle güzel gözüküyor olabilir.

Teşekkürler...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 03, 2012

Alıntı (acehreli):

>

Böyle incelemelerin çok yararlı oldukları doğru ama bir derleyicinin ürettiği kodlara bakarak bir dil olanağına sırt çevirmemeliyiz. if ve case'ten duruma uygun olanını veya daha anlamlı gelenini serbestçe kullanabilmeliyiz.

Ali beye katılıyorum. Eğer bizim istediğimiz şeyleri yapamıyorsa bu noktada eksikliğin derleyicide olduğunu düşünüyorum.

Alıntı (acehreli):

>

Evet, sonuçta ++i ile i++ aynı bedelde ve aynı anlamda olmuş oluyorlar. Peki ben soruyu tersten sorayım: i++ ifadesini ++i ifadesine yeğliyor musun, yoksa önemsizce öylesine mi yazıyorsun? Eğer i++ ifadesini yeğliyorsan neden yeğliyorsun? :) (Bunu yeğleyen çok programcı var.)

Bu arada, i++ C++'ta daha yavaş olmak zorundadır D'nin sonradan arttırma işlecini otomatik olarak halletmesi C++'ta yoktur. Onun için her operator++(int) tanımında programcı bunu kendisi yapmak zorundadır:

> MyInt MyInt::operator++ (int)
> {
>     MyInt m_r(*this);  // Once bu nesnenin kopyasini al; bunu dondurecegiz
>     ++*this;           // Bu nesneyi normal arttirma isleciyle arttir
>
>     return m_r;        // Eski degeri tasiyan kopyayi dondur
> }
> ```

>
>

C++'den gelen alışkanlıktan dolayı ben her zaman ön ek olan arttırma işlecini ++i şeklinde kullanıyorum. Demek D için böyle bir hız farkı yok. İyiymiş :)

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
September 02, 2012

Tamam; Konuyu Ders Arası'na taşıdım.

Böyle incelemelerin çok yararlı oldukları doğru ama bir derleyicinin ürettiği kodlara bakarak bir dil olanağına sırt çevirmemeliyiz. if ve case'ten duruma uygun olanını veya daha anlamlı gelenini serbestçe kullanabilmeliyiz. Bizim istediğimiz olmasını sağlamak derleyicinin görevidir. dmd'nin özellikle genç bir derleyici olduğu için ürettiği kodların yavaşlığı biliniyor. Benzer biçimde gdc'nin de özellikle deneyimli bir derleyici olduğu için hızlı kodlar ürettiği biliniyor.

Hele case'in aralıklı kullanımı gibi çok yeni bir olanağa bakınca Salih'in bulgularına şaşırmamak gerekse de şaşırıyorum: :) dmd nasıl olur da o case'i if'li eşdeğerine dönüştürmez? Çok basitçe yapabilirmiş gibi geliyor... (?)

Alıntı (Salih Dinçer):

>

Ayrıca i++ ile ++i arasında görünüm dışında hiç bir fark yok!

Yani buradaki kullanımında, değil mi? Yoksa aralarında anlamsal olarak çok büyük fark var:

  • ++i: i'yi arttır

  • i++: i'yi arttır ama ifadenin değeri olarak i'nin eski değerini kullan

Sen burada i++ ifadesinin değeri kullanılmadığı için derleyici tarafından koddan çıkartıldığından bir fark olmadığını söylüyorsun. O zaman senin kodunun anlamı biraz daha uzun: i'yi arttır; aslında ifadenin değeri olarak i'nin eski değeri kullanılır ama burada o değer koda eklenmez; sonuçta i arttırılmış olur.

Evet, sonuçta ++i ile i++ aynı bedelde ve aynı anlamda olmuş oluyorlar. Peki ben soruyu tersten sorayım: i++ ifadesini ++i ifadesine yeğliyor musun, yoksa önemsizce öylesine mi yazıyorsun? Eğer i++ ifadesini yeğliyorsan neden yeğliyorsun? :) (Bunu yeğleyen çok programcı var.)

Bu arada, i++ C++'ta daha yavaş olmak zorundadır D'nin sonradan arttırma işlecini otomatik olarak halletmesi C++'ta yoktur. Onun için her operator++(int) tanımında programcı bunu kendisi yapmak zorundadır:

MyInt MyInt::operator++ (int)
{
   MyInt m_r(*this);  // Once bu nesnenin kopyasini al; bunu dondurecegiz
   ++*this;           // Bu nesneyi normal arttirma isleciyle arttir

   return m_r;        // Eski degeri tasiyan kopyayi dondur
}

Eğer yukarıdaki tanım bir başlık dosyasında değilse derleyici hiçbir şey yapamaz. i++ gördüğü zaman o işlevi çağırmak zorundadır ve o işlev de o kopyayı almak ve döndürmek zorundadır. O dönüş değeri gözardı edilse bile kopya için bedel ödenmiştir.

D'de bu sorun yok.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

« First   ‹ Prev
1 2