Thread overview
Aralıklarla çalışmak aslında içiçe bir sarmal mı?
October 04

Şöyle bir kodum var:

import std.algorithm, std.range, std.stdio;

enum limit = 100;

void main() {
  auto range = iota(2, limit);
  auto r = range.filter!(n => n % 2);

// ve tek sayılardan oluşan bir aralık(r)

auto t = r.take(10).filter!(x => x > 10);
  typeof(t).stringof.writeln(": ", t);
} /* Çıktısı:
FilterResult!(__lambda2, Take!(FilterResult!(__lambda1, Result))): [11, 13, 15, 17, 19, 21] */

* main() içindeki 1. satır bana Result türünde tek sayıları veriyor.
* filter uyguladığım için sonra __lambda1 ile birleşip başka bir tür
* take ile ilk 10 tanesi Take() içinde
* ve yine 10'dan büyükleri istediğimden 2. __lambda ile içiçe birleşiyor

Biz de gerçekten bir  aralık döndürsek, örneğin hiç filter kullanmadan isPrime() ile  11, 13, 17, 19 asal sayılarını aralık olarak döndürebilir miyiz?

October 04
On 10/4/21 6:18 AM, Salih Dincer wrote:

> Biz de gerçekten bir  aralık döndürsek, örneğin hiç filter kullanmadan
> isPrime() ile  11, 13, 17, 19 asal sayılarını aralık olarak döndürebilir
> miyiz?

Bunun kolay bir yolu, std.concurrency.Generator'dur:


http://ddili.org/ders/d/fiberler.html#ix_fiberler.Generator,%20std.concurrency

Yani, sıradan bir işlev yazıyoruz ve o işlev "aralığın" elemanlarını yield() ile üretiyor.

Ali


October 05

Herkese merhaba,

Ali hocanın ders örneğindeki sorunun sonuçlarını her seferinde ekrana yazdığımızda tükettiğimizi (sanırım buna hevesli mi diyorduk?) gördüm. Sanki her bir satır, öncekinin bıraktığı bayrağı yerden alıp Fibonacci Maratonu'nda koşmaya devam ediyordu...:)

Yani aşağıdaki çiftli örneğin devamında, ekrana yazdırma satırlarından sadece sonuncuyu etkin bırakırsanız istediğim sonuç (13 ve 21) çıkmakta. Buradan kod içinde değişiklik yapmamız gerektiği sonucu çıkabilir mi?

Değişikliği sadece main()'den sonraki ilk satırın başına // koyduğunuzda altındakinin etkin olduğunu (toggleMark*) ve aralığın daha doğru çalıştığını görmekteyiz.

import std.algorithm, std.concurrency;
import std.stdio, std.range;

struct FibonacciRange {
    long a = 0, b = 1;
    enum empty = false;

    long front() const {
        return a;
    }

    void popFront() {
        auto t = a;
        a = b;
        b = t + b;
    }
}

/* Bu alias std.range.Generator ile olan bir isim
 * çakışmasını gidermek içindir.
 */

alias FiberAralığı = std.concurrency.Generator;

void fibonacciSerisi() {
    int baştaki = 0;
    int sonraki = 1;

    while (true) {
        yield(baştaki);

        const ikiSonraki = baştaki + sonraki;
        baştaki = sonraki;
        sonraki = ikiSonraki;
    }
}

void main() {
    auto range = new FiberAralığı!int(&fibonacciSerisi);/*
    FibonacciRange range;//*/

    auto fib10 = range.take(10);
    typeof(fib10).stringof.writeln;
      writefln("[%(%s, %)]\n", fib10);

    auto fibTek = fib10.filter!(n => n % 2);
    typeof(fibTek).stringof.writeln;
      writefln("[%(%s, %)]\n", fibTek);

    auto son10FibTek = fibTek.filter!(x => x > 10);
    typeof(son10FibTek).stringof.writeln;
      writefln("[%(%s, %)]\n", son10FibTek);

} /* Çıktısı:
   * Take!(Generator!int)
   * [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
   *
   * FilterResult!(__lambda1, Take!(Generator!int))
   * [55, 89, 233, 377, 987, 1597, 4181]
   *
   * FilterResult!(__lambda2, FilterResult!(__lambda1, Take!(Generator!int)))
   * [6765, 17711, 28657, 75025, 121393, 317811, 514229]
*/

* Ben buna kısaca toggleMark diyorum çünkü satır sonundaki etkisiz //*/ karakterler üsteki başka satırlar ile etkileşime girebiliyor. Aynı şeyi blok kodlarda, iki blok arasına /*/ koyarak da yapabilirsiniz. Bu trick pek az kişi tarafından bilinmekte ve Python gibi diller haricinde kullanıp kullanmamakta serbestsiniz!

Başarılar...

October 05

Tekrar Merhaba,

Şu kodu denemeyi unutmuşum:

void main() {
    auto seri = new Generator!int(
      delegate() {
        int b, s = 1;

        while (true) {
          yield(b);

          const t = b;
          b = s;
          s = t + b;
        }
      }
    );

    auto fib2 = seri.take(10).
         filter!(n => n % 2).
         filter!(x => x > 10);

    writefln("[%(%s, %)]", fib2);

} /* ÇIKTISI:
   * [13, 21]
   */

Sanırım Ali hocanın, bu sorun için önerisi de aynı olacaktı: Aralıkları tek satırda çalıştırmak?

Ama kanımca, yield() olanağı diğer aralıkların döndürdüğü gibi sonuç vermiyor ya da ne bileyim Generator ile ilgili. Bir bakıma, ilişkilendirilen işlev birbirinden bağımsız ama öncekinin kaldığı yerden çalışmakta. Haksız mıyım?

October 04
On 10/4/21 10:37 PM, Salih Dincer wrote:

> sadece sonuncuyu etkin bırakırsanız istediğim sonuç (13 ve 21) çıkmakta.

Ne yazık ki aralıklar bu gibi garipliklere yol açabiliyor:

- Aralık nesnesi kopyalandığında baştan mı başlayacak yoksa kaldığı yerden mi?

- Elemanlar referans mı yoksa kopya mı?

- .front ucuz bir işlem mi yoksa her işlettiğimizde hesap mı yapıyor.

- vs.

fibonacciSerisi() işlevinin baştan başlamamasının nedeni, hesaplarını yaparken kullandığı 'baştaki' ve 'sonraki' değişken çiftinden yalnızca bir adet bulunması ve koddaki bütün nesnelerin aynı değişkenlerin etkilenmelerine neden olmaları.

FibonacciRange yapısının durumunda ise kopyalanan şey o yapı nesnesinin ilk durumu. Sanırım o yüzden baştan başlıyor.

Sonuçta, evet, aralıklarla ilgili böyle sorunlar olabiliyor. Neyse ki beni hiç rahatsız etmedi.

> ```*``` Ben buna kısaca toggleMark diyorum çünkü satır sonundaki etkisiz

Ben toggleMark'ı benimseyemedim çünkü // karakterlerini hangi satıra ekleyeceğimi ve bunun etkisinin nereye kadar devam edeceğini göremiyorum. Bunun nedeni, herhalde önemli karakterlerin satırların sonlarında yer almaları.

>      auto range = new FiberAralığı!int(&fibonacciSerisi);/*
>      FibonacciRange range;//*/

O örnekte kolay çünkü yalnızca iki satırdan oluşuyor ama satır sayısı artınca her satırın sonuna mı bakmak gerekecek? Beni aşıyor. :)

Bu vesileyle bir D olanağını hatırlatmak isterim:

  version (garip) {
    auto range = new FiberAralığı!int(&fibonacciSerisi);

  } else {
    FibonacciRange range;
  }

Şimdi şöyle diyebiliriz: "Kodu 'garip' version belirteciyle derlerseniz bir gariplik olduğunu göreceksiniz." Şimdi benim en az iki seçeneğim var:

a) İstersem programa şu satırı ekleyip derlerim:

  version = garip;

b) İstersem de derleme satırına şu seçeneği eklerim:

  $ dmd -version=garip deneme.d

> Bu trick pek az kişi tarafından bilinmekte

Neden acaba? ;)

Ali


October 04
On 10/4/21 11:05 PM, Salih Dincer wrote:

> Sanırım Ali hocanın, bu sorun için önerisi de aynı olacaktı: Aralıkları
> tek satırda çalıştırmak?

Her duruma uygun olmayabilir.

> Ama kanımca, yield() olanağı diğer aralıkların döndürdüğü gibi sonuç
> vermiyor ya da ne bileyim Generator ile ilgili.

Generator, program yığıtındaki değişkenleri kullanıyor.

> Bir bakıma,
> ilişkilendirilen işlev birbirinden bağımsız

Bağımsız olmasını istediğimizde 'delegate'i bir işlevden döndürebiliriz:

auto aralıkYap() {
  return new Generator!int(
    delegate() {
      int b, s = 1;

      while (true) {
        yield(b);

        const t = b;
        b = s;
        s = t + b;
      }
    }
  );
}

  auto seri = aralıkYap();

// ...
   auto fib3 = aralıkYap().take(10);

Şimdi fib3, baştan başlar.

> ama öncekinin kaldığı yerden
> çalışmakta. Haksız mıyım?

Haklısın. Aralıkların zayıflıklarını gösteriyorsun. :)

Ali


October 06

On Tuesday, 5 October 2021 at 06:24:58 UTC, Ali Çehreli wrote:

>

On 10/4/21 11:05 PM, Salih Dincer wrote:
Bağımsız olmasını istediğimizde 'delegate'i bir işlevden döndürebiliriz:

import std.algorithm, std.concurrency;
import std.stdio, std.range;

    void main()
    {
        auto seri = new Generator!real (
          delegate() {
            real pi = 3;
            real n = 2;
            int count = 0;
            while (++count) {
              yield(pi);
              real result = n;
              size_t loop = 2;
              while(loop--) result *= ++n;
              if(count % 2 ) pi += 4/result;
              else pi -= 4/result;
            }
          }
        );

        auto pi1 = seri.take(10);
        auto pi2 = seri.take(10);

        writefln("%(%.13f\n%)", pi1);
        writefln("%.21f", 3.141_592_653_589_793_23);
        writefln("%(%.13f\n%)", pi1);
        writefln("%(%.13f\n%)", pi1);
        writefln("%(%.13f\n%)", pi1);
    }  /*
        * 10 term, 3-digit correct
        * 100 term, 6-digit correct
        * 1000 term, 9-digit correct
        * 9999 term, 12-digit correct
        * 100000 term, 15-digit correct
        */

Ali hocam bu delegate ve range olayında bir şey dikkatimi çekti. Yukarıdaki koddan anladığım kadarıyla pi1 ile pi2 arasında hiç fark yok. Tamam, tanımlarken de aynı şeyi tanımlıyorum ama sanki tanımlandığı sırada veriler işlenmiyor, değil mi? Ne zaman işletilirse ve kaldığı yerden döngüyü take(x) kadar devam ettiriyor.

Sanki yavaş yavaş her şey daha crystal!

Teşekkürler...

October 05
On 10/5/21 9:11 PM, Salih Dincer wrote:

> Yukarıdaki koddan anladığım kadarıyla pi1 ile pi2 arasında hiç fark yok.

Yani, ikisi de tek delegate'i kullanıyorlar. Evet, fark yok.

> Tamam, tanımlarken de aynı şeyi tanımlıyorum ama sanki tanımlandığı
> sırada veriler işlenmiyor, değil mi?

Doğru. Burada bir çok olanak bir arada işliyor:

- Fiber, ana fiber'e yield() ile sonuç üretir. Ana fiber de işçi fiberi call() ile tekrar ilerletir. Biz call()'u görmüyoruz çünkü call() Generator tarafından ve büyük olasılıkla onun popFront() işlevi tarafından çağrılıyor.

- Bütün aralık nesnelerinde olduğu gibi, buradaki Generator ve onu kullanan take() hep tembel olarak işliyorlar.

Sonuçta, evet, hiç veri işlenmiyor.

> Ne zaman işletilirse ve kaldığı
> yerden döngüyü take(x) kadar devam ettiriyor.

Evet.

Ali