Thread overview
Aralıkta foreach ile sayaçlı ilerlemek için sequence ve zip
Feb 03, 2012
Salih Dinçer
Feb 04, 2012
Salih Dinçer
Feb 05, 2012
Salih Dinçer
Feb 05, 2012
Salih Dinçer
January 23, 2012

foreach diziler üzerinde sayaçlı ilerlemeyi destekler. İki döngü değişkeni tanımlandığında birincisi sayaçtır, ikincisi de eleman:

   foreach (i, eleman; [ 10, 20, 30 ]) {
       writeln(i, ": ", eleman);
   }

Çıktısı:

'0: 10
1: 20
2: 30
'

Ne yazık ki bu kendi türlerimiz için otomatik değildir. Kendimiz sayaçlı döngü istediğimizde türümüz için sayaç da alan bir opApply() tanımlamamız gerekir. Şurada "Döngü sayacı" başlığı altında var:

http://ddili.org/ders/d/foreach_opapply.html

O yöntem hem daha zordur hem de foreach desteği aralık işlevleri ile zaten verildiğinde gereksiz kabul edilmelidir. Ne yazık ki D bu konuda bir destek getirmiyor ama std.range modülünde yeterli olanak varmış.

Şöyle bir aralığımız olsun:

import std.stdio;

struct FibonacciSerisi
{
   int baştaki = 0;
   int sonraki = 1;

   enum empty = false;

   @property int front() const
   {
       return baştaki;
   }

   void popFront()
   {
       int ikiSonraki = baştaki + sonraki;
       baştaki = sonraki;
       sonraki = ikiSonraki;
   }
}

void main()
{
   auto aralık = take(FibonacciSerisi(), 10);

   foreach (eleman; aralık) {
       writeln(eleman);
   }
}

Sayaç gerekmediği zaman güzelce çalışıyor:

'0
1
1
2
3
5
8
13
21
34
'

Ama işte foreach sayacı eklediğimizde çalışmıyor:

   foreach (i, eleman; aralık) {  // <-- HATA
       writeln(i, ": ", eleman);
   }

Derleme hatası türlerin çıkarsanamadıklarını belirtiyor (çünkü aralığımızın opApply()'ı yok. (Zaten dikkat ederseniz aralık bizim aralığımız değil, take()'in döndürdüğü aralık.)):

'Error: cannot infer argument types'

Yeni öğrendiğim yöntem şu:

import std.range;

// ...

   foreach (i, eleman; zip(sequence!"n"(), aralık)) {
       writeln(i, ": ", eleman);
   }

Şimdi çalışıyor:

'0: 0
1: 1
2: 1
3: 2
4: 3
5: 5
6: 8
7: 13
8: 21
9: 34
'

sequence bir seri eleman üretmek için kullanılıyor. Şablon parametresinde 'n' ile ifade edilen şey sequence'ın otomatik olarak arttırdığı bir değer. O değer, yalnızca 'n' yazınca 0, 1, 2, ... diye gidiyor. (sequence'a işlev parametreleri de verilebiliyormuş; o parametreler de şablon parametresi içinde a[0], a[1], vs. olarak yazılıyorlarmış ama denemedim.)

zip ise kendisine verilen bir veya daha fazla aralığı aynı hızda adım adım ilerletiyor. Yani sequence'ı bir ilerletirken aralık'ı da bir ilerletiyor. Sonuç olarak da kendisine verilen aralık adedi kadar elemanı bulunan bir çokuzlu döndürüyor.

O çokuzlunun elemanlarının foreach döngü değişkenlerine karşılık gelmeleri ve döngü içinde 'i' ve 'eleman' olarak kullanılabilmelerine de çok şaşırıyorum. :)

Ali

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

February 04, 2012

Merhaba,

Çok çok ilgimi çeken işleçler (cycle, sequence, take, zip vb.) bunlar. Sayenizde güzel şeyler öğreniyoruz. An itibariyle aralıklar (range.d (https://bitbucket.org/goshawk/gdc/src/89eccdc0155e/d/phobos2/std/range.d)) hakkında daha çok şey öğrenmek için çabalıyorum. Kaynak kodlarını bile incelediğime göre, kendi adıma teşekkür ederim...:)

Bu arada aşağıdaki gibi ikinci bir 'foreach' ile kod aynı şekilde çalışıyor. Herhalde başta devingen dizi (dynamic array) kurduğum için böyle. Belki tek bir tanesi bile yeter. Peki 'auto' ile kurduğumuzda aralık değişkeninin türü ne oluyor?

import std.stdio, std.range;

struct FibonacciSerisi { ... }

void main()
{
	int[] aralık;
	foreach (i; take(FibonacciSerisi(), 10)) aralık ~= i;
	foreach (i, fib; aralık) writeln(i, ": ", fib);
}

Sevgiler, saygılar...

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

February 04, 2012

Alıntı (Salih Dinçer):

>

Bu arada aşağıdaki gibi ikinci bir 'foreach' ile kod aynı şekilde çalışıyor. Herhalde başta devingen dizi (dynamic array) kurduğum için böyle.

Yani elemanları önce bir diziye yerleştiriyorsun ve ondan sonra sayaçlı bir foreach kullanıyorsun. O çalışıyor çünkü dizilerde sayaçlı foreach olanağı var. (Zaten kendi aralıklarımızda eksikliğini çekiyorduk.)

Senin gösterdiğin yöntemin farkı, bütün elemanları baştan oluşturmasının gerekmesidir. Oysa zip'li olanda aralıklar foreach'in her ilerletişinde tek adım ilerletiliyorlar. Çok az bir şey yazılı ama şuradaki "Tembellik" başlığından akratıyorum:

http://ddili.org/ders/d/araliklar.html

Alıntı:

>

Aralık döndüren işlevlerin başka bir yararı, o aralıkların tembel olarak kullanılabilmeleridir. Bu hem program hızı ve bellek kullanımı açısından çok yararlıdır, hem de sonsuz aralıkların var olabilmeleri zaten bütünüyle tembellik olanağı sayesindedir.

Tembel aralıklar işlerini gerektikçe ve parça parça gerçekleştirirler. Bunun bir örneğini FibonacciSerisi aralığında görüyoruz: Elemanlar ancak gerektikçe popFront() işlevinde teker teker hesaplanırlar. FibonacciSerisi eğer tembel yerine hevesli bir aralık olsaydı, yani kullanılmadan önce bütün aralığı üretmeye çalışsaydı, sonsuza kadar işlemeye devam ederdi. Ürettiği elemanları saklaması da gerekeceği için sonsuz sayıdaki elemana da yer bulamazdı.

Hevesli aralıkların başka bir sakıncası, sonlu sayıda bile olsalar belki de hiç kullanılmayacak olan elemanlar için bile gereksizce yer harcayacak olmalarıdır.

Phobos'taki çoğu algoritma gibi take() ve filter() da tembellikten yararlanırlar.

Alıntı:

>

Peki 'auto' ile kurduğumuzda aralık değişkeninin türü ne oluyor?

take() bir işlev. Onun yaptığı tek şey, asıl aralığı gerçekleştiren türde bir aralık nesnesi döndürmek. O nesnenin türünü typeof(nesne).stringof ile öğrenebiliriz. Yukarıdaki bağlantıda .stringof aratınca onun da bir örneği var. take() Take isminde bir yapı şablonu döndürüyor. Sanırım yukarıdaki kullanımda Take!FibonacciSerisi türünde.

Ali

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

February 04, 2012

Hocam kusura bakmayın... :rolleyes:

Sanırım yine asıl olayı kavramadım ve bu yüzden değerli vaktinizi uzun açıklamalar yapmak zorunda bırakarak harcıyorum. Ama bu vesileyle aralıkları (range) bitirdim...:)

Ben olabildiğince her şeyi basit ve bildiğim yoldan algılamaya çalışıyorum. Ancak sonsuza giden bu algoritma (sanırım tembel diyorsunuz?) meğer bir tane de SınırlıFibonacciSerisi (-bknz. aşağıdaki kod) varmış. Hatta şurada (http://ddili.org/forum/post/4551) da aynı sorunu iota ile irdelemişsiniz...

struct SınırlıFibonacciSerisi {
   int sınır, öndeki;
   FibonacciSerisi seri;

   this(int sınır)
   {
       this.sınır = sınır;
       this.öndeki = seri.front;
   }

   @property bool empty()
   {
       return öndeki >= sınır;
   }

   @property int front()
   {
       return öndeki;
   }

   void popFront()
   {
       seri.popFront();
       öndeki = seri.front;
   }
}

Teşekkürler...

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

February 04, 2012

Alıntı (Salih Dinçer):

>

sonsuza giden bu algoritma (sanırım tembel diyorsunuz?)

Sonsuz aralıkların var olabilmesi için tembellik gerekir ama aynı değiller.

Başka bir gözlem: tembellik olmasa aralıkları kısıtlamak şarttır.

Verilerin hevesli (eager) olarak üretilmeleri, hepsinin daha kullanılmaya başlanmadan üretilmeleridir. Emirli dillerde çoğunlukla öyle olur.

Tembel (lazy) olarak üretilmeleri ise gerektikçe üretilmeleridir. Fonksiyonel dillerde çok yaygındır.

Bilenler için, verilerin gerektikçe üretilmeleri kavramı Python'un generator ve coroutine'lerine de benzer.

Şu örnek nasıl? Elimizde çok büyük sayıda değer var ve biz bu değerlerden en küçük 3 tanesini istiyoruz. Emirli dillerde ne yapılır? Değerler baştan sıralanırlar ve biz en baştaki 3 tanesini kullanırız. Bu hevesli algoritma kullanılmayacak olan elemanları da sıraladığı için savurgandır.

Tembel algoritma ise elemanları baştan sıralamaz. Ancak biz en baştakine erişmek istediğimizde örneğin quick sort ile en küçük elemanı bulur. Dizinin diğer elemanları sıralanmamışlardır. Ondan sonra biz ikinciyi isteyince bu sefer ikincinin bulunduğu bölümü sıralar ve bize ikinciyi verir. Yani tembel bir şekilde ve ancak mutlaka gerektikçe iş yapar.

Ali

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

February 05, 2012

Alıntı (acehreli):

>

Şu örnek nasıl? Elimizde çok büyük sayıda değer var ve biz bu değerlerden en küçük 3 tanesini istiyoruz. Emirli dillerde ne yapılır? Değerler baştan sıralanırlar ve biz en baştaki 3 tanesini kullanırız. Bu hevesli algoritma kullanılmayacak olan elemanları da sıraladığı için savurgandır.
Üstadım, en sondaki 3 değer demek istemiş olabilir misin? Şu yüzden sorma gereği duydum; sonuçta emirli dillerde baştan sıralarken ilk üç tanesine kadar hesapla" diye bir sınır koyabiliriz. Yanılıyor muyum?

Ayrıca diğer diller ile karşılaştırma konusunu ben de çok sık yapıyorum. Hatta inanıyorum ki D dili, Go'dan bir şeyler kapabilir. Eğer lisans konusunda bir sıkıntı veya ECMA dışına çıkmada bir sınır yoksa yapılması taraftarıyım. Yetkililer ile görüşürseniz bu temennimi iletmenizi rica ederim. Örneğin Go'da fib() işlevi şu şekilde yapılmış:

Alıntı:

>
> func fib() func() int {
> 	a, b := 0, 1
> 	return func() int {
> 		a, b = b, a+b
> 		return b
> 	}
> }
> ```


Biraz lambda'ya benziyor sanki. Bir de asal sayıları filitreleyen algoritması var ki bunun tamamını paylaşmalıyım. Çünkü channel diye henüz anlamadığım bir olay var...:)

Alıntı:
>
>

// A concurrent prime sieve

// See "Prime Numbers" section of the tutorial:
// http://golang.org/doc/go_tutorial.html

package main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
for i := 2; ; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch) // Launch Generate goroutine.
for i := 0; i < 10; i++ {
prime := <-ch
print(prime, "\n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
ch = ch1
}
}

>

Kaynak: http://golang.org

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

February 05, 2012

Ayrıntılı cevabın için çok teşekkür ederim. Azıcık lambda'ya da girip diğer konuya dallandık ama çok lezzetli olmaya başladı...:)

Alıntı (Ali Çehreli):

>

C++'ta bir de nth_element var. O da tam n'inci yerdeki elemanı verir ama geri kalanlar sıralanmamışlardır.

Örneğin baştaki elemana erişmek istendiğinde yalnızca gereken sayıda sıralama halledilir ve en baştaki bulunabilir. İkinci eleman bile sıralanmamış olabilir. (Tabii belki de o da yan etki olarak sıralanmıştır ama örneğin onuncu elemanın "şu sekiz taneden" birisi olduğu biliniyordur ama daha hangisi olduğu belli değildir.)

Tabii sıralama yalnızca bir örnek. Genel olarak şöyle de tarif edilebilir: Elemanları verecek düzenek kurulur ama elemanlar gerektikçe oluşturulurlar. Örneğin FibonacciSerisi hazır olarak elemanları üretmek için beklemektedir. Ancak gerektikçe işlem yapar.
İşte benim de asal sayılar ile ilgili çalışmanın (-bknz. bwSieve (<code.google.com/p/bitwise-sieve/>)) ürünü olarak gerçekleştirmek istediğim bu; sayılar üretilmeden, kaydırma işleçleri (shift operators) ile dolaylı yoldan p{n}'i bulmak. Kuramsal açıdan mümkün görünüyor ama hala kodlama da sorunlar yaşıyorum. Sanırım D ile bunu aşacağım ve bilimsel bir bildiri ile önce Türkiye'de yayınlayacağım inşaallah...:)

O yüzden başta da dediğim gibi çok lezzetli bir tartışma bu. Lütfen devam edelim ve mümkünse Go'nun özellikleri ve D ile benzerliklerini de irdeleyelim.

Alıntı (Ali Çehreli):

>

Öncelikle, o işlev Fibonacci serisini biraz eksik oluşturuyor, değil mi? Çünkü aslında serinin 1 1 ile başlaması gerekmez mi? D'deki karşılığı şu değerleri üretti: 1 2 3 5 8 ... Herhalde Go'daki de öyledir. (?)
Evet, öyle...:)

Alıntı (Ali Çehreli):

>

Orada a,b birlikte bir çokuzlu (tuple) oluşturuyorlar. Çokuzlular D'de dilin iç olanağı değillerdir. Bu konuda çok istekte bulunanlar olmuştu ama sonuçta o olanak dile eklenmedi. Ne yapalım... :) Şimdilik en iyi çözüm Tuple şablonu ve onu oluşturan tuple() işlevi. Şurada anlatılıyor: http://ddili.org/ders/d/cokuzlular.html
'Tuple!()' ile ilk karşılaşmam sanırım bu başlık yani aralıklar ile oldu. Aslında şablon mudur hala karıştırıyorum. Hatta struct'ın bu halini class ile çok karıştırıyorum. Benzerlikleri aşikar ama farklı olan tam olarak nedir hala tam anlamış değilim...:)

Dün isimsiz işlev (lambda function) ile denemeler yaparken bunu değişken olarak kullanmak zorunda kaldım. Çünkü arabirim işlevi (interface function) iletişim kuramıyor ve derleyici "'Tuple!(uint uint)' to uint convert" gibi bir zorunluluktan bahsediyordu. Sonunda 'auto''ları daha dikkatli kullanarak bunları yerleştirdim; ne kadar garip gelse de...:)

Dediğim gibi henüz yeni şeylere tam alışamadım. Hatta e-posta ile bahsettiğim sıralama olaylarından:

/*
sort.d (05.02.2012)
*/
import std.algorithm, std.range, std.stdio;

   struct fib {
       ulong a, b = 1;
       static immutable bool empty = false;

       @property ulong front() const { return a; }
       void popFront() {
           a = a + b;
           b = a - b;
       }
   }//*/

void main(string[] args) {
   ulong[] fibSerisi, evrilenVeri;

   foreach (f; take(fib(), 19)) evrilenVeri ~= f;
   writeln(evrilenVeri, "\n");
   fibSerisi = evrilenVeri.dup;

   sort!((a, b) { return a > b; })(evrilenVeri);
   writeln(evrilenVeri, "\n");

   sort!("a < b")(evrilenVeri);
   writeln(evrilenVeri, "\n");
   assert (fibSerisi == evrilenVeri);
}

'sort!()' ile ifade edilen şeyler bana hep garip geliyor. Ne bileyim 'sort(dizi)'; şekline alışmışım...:)

Sevgiler, saygılar...

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

February 05, 2012

Alıntı (Salih Dinçer):

>

Alıntı (acehreli):

>

Şu örnek nasıl? Elimizde çok büyük sayıda değer var ve biz bu değerlerden en küçük 3 tanesini istiyoruz. Emirli dillerde ne yapılır? Değerler baştan sıralanırlar ve biz en baştaki 3 tanesini kullanırız. Bu hevesli algoritma kullanılmayacak olan elemanları da sıraladığı için savurgandır.
Üstadım, en sondaki 3 değer demek istemiş olabilir misin? Şu yüzden sorma gereği duydum; sonuçta emirli dillerde baştan sıralarken ilk üç tanesine kadar hesapla" diye bir sınır koyabiliriz. Yanılıyor muyum?

Tabii ki. Emirli dillerde öyle yapılmak zorunda olduğunu söylemek istemedim. Algoritmaların hevesli olmalarına alışmışızdır demek istedim. Belki de emirli dil değil de geleneksel dil demeliydim.

C++'ta "sıralamada baştaki üçünü" ver diye bir algoritma var:

http://www.cplusplus.com/reference/algorithm/partial_sort/

Ama o bile tam tembel sayılmaz çünkü belki de birinciyi görür görmez daha fazla istemeyeceğim bile. Yani "bana baştaki üçünü sıralı olarak ver" demek bile yeterince tembel değil. Tembel olunca bütün işlemler yalnızca ve yalnızca gerçekten gerektikçe işletilirler.

C++'ta bir de nth_element var. O da tam n'inci yerdeki elemanı verir ama geri kalanlar sıralanmamışlardır.

Örneğin baştaki elemana erişmek istendiğinde yalnızca gereken sayıda sıralama halledilir ve en baştaki bulunabilir. İkinci eleman bile sıralanmamış olabilir. (Tabii belki de o da yan etki olarak sıralanmıştır ama örneğin onuncu elemanın "şu sekiz taneden" birisi olduğu biliniyordur ama daha hangisi olduğu belli değildir.)

Tabii sıralama yalnızca bir örnek. Genel olarak şöyle de tarif edilebilir: Elemanları verecek düzenek kurulur ama elemanlar gerektikçe oluşturulurlar. Örneğin FibonacciSerisi hazır olarak elemanları üretmek için beklemektedir. Ancak gerektikçe işlem yapar.

Alıntı:

>

D dili, Go'dan bir şeyler kapabilir.

Go çıktığında D'ye ne kadar benzediğine şaşırmıştık. :) D'den aldılar denemez ama dil tasarımlarında ne önemliyle bütün modern diller doğal olarak o olanakları içeriyorlar.

Alıntı:

>

bu temennimi iletmenizi rica ederim

D'cilerin diğer dilleri ayrıntılarıyla bildiklerini biliyorum. Şu sıralarda D'ye büyük değişiklik gelemeyeceğinden eminim. Yeni lambda yazımı bile çok şaşırtıcıydı.

Alıntı:

>

Örneğin Go'da fib() işlevi şu şekilde yapılmış:

Alıntı:

>
> > func fib() func() int {
> > 	a, b := 0, 1
> > 	return func() int {
> > 		a, b = b, a+b
> > 		return b
> > 	}
> > }
> > ```

>
> Biraz lambda'ya benziyor sanki.

Anladığım kadarıyla fib(), işlev döndüren bir işlev. (?)

Alıntı:
> Çünkü channel diye henüz anlamadığım bir olay var...:)

Ben de bilmiyorum ama Go'nun en büyük özelliğinin goroutine dedikleri kavram olduğunu biliyorum.

Ali

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

Alıntı (Salih Dinçer):

>

Örneğin Go'da fib() işlevi şu şekilde yapılmış:

Alıntı:

>
> > func fib() func() int {
> > 	a, b := 0, 1
> > 	return func() int {
> > 		a, b = b, a+b
> > 		return b
> > 	}
> > }
> > ```


Öncelikle, o işlev Fibonacci serisini biraz eksik oluşturuyor, değil mi? Çünkü aslında  serinin 1 1 ile başlaması gerekmez mi? D'deki karşılığı şu değerleri üretti: 1 2 3 5 8 ... Herhalde Go'daki de öyledir. (?)

Orada a,b birlikte bir çokuzlu (tuple) oluşturuyorlar. Çokuzlular D'de dilin iç olanağı değillerdir. Bu konuda çok istekte bulunanlar olmuştu ama sonuçta o olanak dile eklenmedi. Ne yapalım... :) Şimdilik en iyi çözüm Tuple şablonu ve onu oluşturan tuple() işlevi. Şurada anlatılıyor:

 http://ddili.org/ders/d/cokuzlular.html

Yukarıdaki örneğin bire bir karşılığı D'de şöyle olabilir:


import std.stdio;
import std.typecons;

int delegate() fib()
{
auto ab = tuple(0, 1);

return delegate int() {
ab = tuple(ab[1], ab[0] + ab[1]);
return ab[1];
};
}

void main()
{
auto f = fib();
writeln(f(), f(), f(), f(), f());
}



return ile döndürülen delegate'in türünün açıkça belirtilmesi gerekmez. Daha kısa olarak:


int delegate() fib()
{
auto ab = tuple(0, 1);

return {
ab = tuple(ab[1], ab[0] + ab[1]);
return ab[1];
};
}



Dönüş türü olarak da auto kullanılabilir:


auto fib()
{
auto ab = tuple(0, 1);

return {
ab = tuple(ab[1], ab[0] + ab[1]);
return ab[1];
};
}



Tabii o zaman işlevin belgelerine veya gerçekleştirmelerine bakmak zorundayızdır. Yoksa auto tek başına bir şey ifade etmiyor.

Bu kadar küçük bir işlevde önemli değildir ama çokuzlu üyelerine indeksle erişmek istenmediğinde Tuple ile ve üye isimleriyle de oluşturulabilir:


auto fib()
{
auto seri = Tuple!(int, "a", int, "b")(0, 1);

return {
seri = tuple(seri.b, seri.a + seri.b);
return seri.b;
};
}



O, D'nin mixin'inin marifeti. Dizgi olarak verdiğimiz isimlerden derleme zamanında gerçek üyeler oluşturuyor ve seri.a diye kullanmamıza izin veriyor. mixin'ler de "katma" ismiyle şuradalar:

 http://ddili.org/ders/d/katmalar.html

Ali

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