Thread overview
Faydalı Yan Etkili Bir Taşma Örneği
Feb 24, 2022
Salih Dincer
Feb 24, 2022
Ali Çehreli
Feb 27, 2022
Salih Dincer
Feb 27, 2022
Ali Çehreli
Apr 04, 2022
Salih Dincer
Apr 04, 2022
Ali Çehreli
Apr 05, 2022
Salih Dincer
Mar 11, 2022
Salih Dincer
February 24, 2022

Merhaba,

Aşağıdaki çok çok temel bir konu. Aralıklar yoluyla bir yığından (Stack) ters/düz veri çekeceğiz. Aslında daha çok dinamik bir dizinin üzerinde gezineceğiz.

struct Stack
{
	string s;
	size_t index;

	@system
	{
		string toString() { return s; }
		bool empty() { return index >= s.length; }
		
		void popFront() { index++; }
		char front() { return s[index]; }
		
		alias back = front;
		void popBack() { index--; }
	}

	@property push(char c)
	{
		s ~= c;
	}
	
	@property pop()
	{
		//return s[index++];/*
		scope(exit)
		{
			popFront();
		}
		return front();//*/	
	}
}

import std.stdio;

void main ()
{
	auto deneme = Stack("deneme");
	
	with(deneme)
	{
		foreach(n; 0..3) // soldan 3 karakter çek
		{
			pop.write;
		}
		push('1');
    }

    foreach_reverse(c; deneme) // kaldığı noktadan başa gez
    {
		c.write;
	}
	writeln;        // denened
	deneme.writeln; // deneme1
}

Aslında ForwardRange'de (popFront, front) bir sorun yok çünkü her şey istendiği gibi çalışıyor. Ancak işin içine BidirectionalRange'i de dahil edince tersten dizi üzerinde geziniyoruz. Tabi index'in türü size_t, yani işaretsiz. Bu durumda 0'dan sonra size_t.max'a çıkıyor ama Range violation hatası almıyoruz çünkü empty() akıllıca kurgulanmış.

Başarılar...

February 24, 2022
On 2/24/22 03:31, Salih Dincer wrote:

Öyle gibi ama ben ne zaman >= veya <= kullansam acaba bir hatamı gizliyor muyum diye şüphelenirim. Örneğin, eğer index hep teker teker değişecekse, ta C++ zamanlarımdan beri çoğu C++'çının aksine, hep != kullanırdım. Çünkü eğer döngüden çıkamazsam indeks değerinde bir hata yapmış olduğumu anlardım. Neyse... O benim kuruntum... :)

Ama bu kodda çok temel bir karışıklık var: Topluluk (depo) işiyle ilerleme işini birbirine karıştırıyor. (Yanılmıyorsam Java'da öyleymiş: ilerleme işlemleri topluluğun üye işlevleriymiş.) Oysa hem C++'ın erişicilerinin (iterator) hem de D'nin aralıklarının en büyük yararları, o işlerin birbirlerinden ayrılmalarıdır. Bu sayede aynı topluluk üzerinde birden fazla biçimde ilerleyebiliriz.

Yani burada push() ve pop() Stack'e ait olmalı ama aralık işlevleri örneğin StackRange diye farklı bir türe ait olmalı. O zaman sınırsız sayıda StackRange oluşturup kullanabiliriz.

Bunun bir yolu, Stack'e opSlice işlevi eklemek ve aralık işlevlerini opSlice'ın döndürdüğü türe vermektir. Ben şurada öyle yapmışım:

  http://ddili.org/ders/d/islec_yukleme.html#ix_islec_yukleme.opSlice

(O başlık altında gösterilen kodların daha önce yazılmış olan ÇiftUçluKuyruk'a eklenecekleri düşünülmüş.)

Topluluklarla aralıkları birbirine karıştırmamızın bir nedeni, D'nin dinamik dizileridir çünkü onlar da hem aralık işlevleri sunarlar hem de onlara ~ ve ~= işleçleriyle eleman ekleyebiliyoruz. Ama yine de, orada bir fark var: Eleman eklendiğinde yeni bir dizi oluşur ve yalnızca kendisi yoluyla eklenen dilim o elemana da erişim sağlamaya başlar. Yani, yine de birden fazla dilim bulunabiliyor. Keşke öyle olmasaymış ama durum tarihsel nedenlerle böyle. :)

Ali

February 27, 2022

On Thursday, 24 February 2022 at 17:54:22 UTC, Ali Çehreli wrote:

>

Bunun bir yolu, Stack'e opSlice işlevi eklemek ve aralık işlevlerini opSlice'ın döndürdüğü türe vermektir. [...]

Ertelediğim bu konuda bir şeyler denemeye çalıştım. Walter'ın 10 sene önceki Voldemort Types makalesini tekrar okudum. Şöyle saçma sapan bir verimiz olsun:

string data = "TAMAMI AYRILACAKLAR" ~
" ANCAK BU FARKLI BOYUTTA KODLANIR" ~
" YA DA TAMAMEN MANTIKLI YAZILIRSA" ~
" HATASIZ KARAKTERLER GENERATE ONE" ~
" CODING TWENTY WORDS FORTH-COMING";

Burada boşluk karakteri ile ayrılmış tam 20 sözcük var ve biz istesek dil olanağı strip ile bunları tek tek ekrana yazabilirdik:

int dataLength;
foreach(word; data.split)
    word.writeln(": ", ++dataLength);

assert(dataLength == 20);

Ama acaba dilimleri kullanarak verimizi Slice isminde bir yapının taşıyacağı aralık şeklinde döndürebilir miydik? Deneyelim:

auto slice(string E)(string data)
{
  import std.string;

  long index = data.split.length;

  struct Slice
  {
    long back, next = -1;
    @system
    {
      bool empty()
      {
        return index < 0;
      }

      auto front()
      {
        return data[back..next];
      }

      void popFront()
      {
        --index;
        back = ++next;
        next = indexOf(data, E, next);
        if(next == -1) next = data.length;
      }
    }
  }

  Slice s;
  s.popFront();
  return s;
}

import std.range, std.stdio;

void main()
{
  auto birAralık = veri.slice!" ";
  foreach(s; birAralık)
    s.writeln;

  assert(birAralık.empty); /* aralık,
  kesinlikle boş ama tüketilen birAralık!
  */

  auto dörtTaneÇek = veri.slice!" ".take(4);
  string[] birDizi = dörtTaneÇek.array;

  assert(birDizi.length == 4);

  // İşte görsel ve leziz kanıtı:
  birDizi[$-2..$].writef!"%-(%s %) BİR DİZİ!";
}/* ÇIKTISI:

    ANCAK BU BİR DİZİ!
  */

İyi pazarlar...

February 27, 2022
On 2/26/22 23:34, Salih Dincer wrote:

> dil olanağı strip ile

(split demek istemişsin çünkü onu kullanmışsın.) Ancak, split() hevesli (eager) bir algoritmadır ve dizi döndürür. Onun yerine, std.algorithm.splitter'ı kullansak tam seninki gibi olurdu:

auto slice(string E)(string data)
{
  import std.algorithm;
  return data.splitter;
}

Tek fark, main içindeki ilk assert() satırında oluşuyor. Bu farkın nedeni, D aralıklarının bir konuyu belirsiz bırakmasıdır: 'birAralık' foreach'te kullanılırken kopyalanır (çünkü her şey otomatik olarak hep kopyalanır). Peki, foreach ilerlerken main'in ilk satırında tanımlanan 'birAralık' tükenmeli midir tükenmemeli midir? D aralıkları bu konuyu belirsiz bırakır. O yüzden data.splitter ifadesi ile senin aralık türün farklı davranıyor.

Ali

March 11, 2022

On Thursday, 24 February 2022 at 11:31:40 UTC, Salih Dincer wrote:

>
struct Stack
{
	string s;
	size_t index;

	@system
	{
		string toString() { return s; }
		bool empty() { return index >= s.length; }
		
		void popFront() { index++; }
		char front() { return s[index]; }
		
		alias back = front;
		void popBack() { index--; }
	}

	@property push(char c)
	{
		s ~= c;
	}
	
	@property pop()
	{
		//return s[index++];/*
		scope(exit)
		{
			popFront();
		}
		return front();//*/	
	}
}

import std.stdio;

void main ()
{
	auto deneme = Stack("deneme");
	
	with(deneme)
	{
		foreach(n; 0..3) // soldan 3 karakter çek
		{
			pop.write;
		}
		push('1');
    }

    foreach_reverse(c; deneme) // kaldığı noktadan başa gez
    {
		c.write;
	}
	writeln;        // denened
	deneme.writeln; // deneme1
}

Gönül isterdi ki, bu konunun başlığı 'taşma'lı değil de 'dilim'li bir şey ile değişseydi! Gerçi başka bir başlıkta devam edeceğiz ama daha da geliştirene kadar bu basit ama etkili Stack yapısında (belki string sarmalaması mı deseydik daha doğru olur?) dil olanağındaki dilimi kullandığımızda şöyle muhteşem bir şey oluyor:

// ... önceki kodlar aynı ...
  @property pop(size_t x)
  {
    scope(exit)
    {
      this.index += x;
    }
    return s[index..index + x];
  }
}

import std.stdio;

void main ()
{
  auto test = Stack("Deneme 1 ");

  with(test)
  {
    foreach(n; 0..9)
    {
      // önce 3 karakter çek
      auto buffer = pop(3);
      buffer.writeln;

      // sonra bunları yığının sonuna ekle
      foreach(c; buffer)
      {
        push(c);
      }
    }
  }
  test.writeln;
} /* ÇIKTISI:
Den
eme
 1
Den
eme
 1
Den
eme
 1
Deneme 1 Deneme 1 Deneme 1 Deneme 1
   */

Aslında buna benzer bir şeyi pekala chunks ve arkadaşları ile yapabiliriz ama kendi yapınızla özelleştirilmiş uygulamalar mümkün. Kısaca başlangıcı şöyle de olabilirdi:

  import std.algorithm : each;
  import std.range : chunks;
  data.chunks(3)
      .each!(str =>
        str.writeln
      );
April 04, 2022

On Sunday, 27 February 2022 at 16:06:30 UTC, Ali Çehreli wrote:

>

std.algorithm.splitter'ı kullansak tam seninki gibi olurdu:

auto slice(string E)(string data)
{
  import std.algorithm;
  return data.splitter;
}

Merhaba Ali hocam,

Bunu yeni fark ediyorum. İşlev parametresi olan E, nasıl oluyor da splitter'a verilebiliyor onu merak ettim?

Yani E değil de atıyorum S deseydik bu kadar pratik olmazdı sanırım, değil mi?

Teşekkürler...

April 04, 2022
On 4/4/22 07:17, Salih Dincer wrote:
> On Sunday, 27 February 2022 at 16:06:30 UTC, Ali Çehreli wrote:
>>
>> std.algorithm.splitter'ı kullansak tam seninki gibi olurdu:
>> ```d
>> auto slice(string E)(string data)
>> {
>>   import std.algorithm;
>>   return data.splitter;
>> }
>> ```
>
> Merhaba Ali hocam,
>
> Bunu yeni fark ediyorum. İşlev parametresi olan E,

(Şablon parametresi demek istedin.)

E'yi herhalde tam anlamadan senin kodundan doğrudan kopyalamışım. splitter() o kullanımda galiba gönünmeyen karakterleri göze alır: boşluk, satır başı, tab, vs.

Ali

April 05, 2022

On Monday, 4 April 2022 at 16:08:03 UTC, Ali Çehreli wrote:

>

[...]
E'yi herhalde tam anlamadan senin kodundan doğrudan kopyalamışım. splitter() o kullanımda galiba gönünmeyen karakterleri göze alır: boşluk, satır başı, tab, vs.

Aaa evet, isWhite() işlevi. Ben de şaşırdım; deneyince çalıştı ve kendi kendime ne büyük kolaylık dedim! Meğer şablon parametresini dikkate almıyormuş 😀

Madem split!splitter diye bir şeyler var ve ben bunları hep karıştırıyorum*, kendi sdb.container modülümdeki kolaylık işlevlerine, sizin diziDizisi() işlevi yanına bunu da ekliyorum:

auto slice(string E)(string data)
{
  import std.algorithm;
  return data.split(E);
}
  • (*) Bu split()'i karıştırmam eskiye dayalı. Önceden başka modüllerdeydi ve hatta ismi separate gibi bir şeydi. En azından std.array mi yoksa std.string de mi karmaşasından uzaklaşıyoruz. Gerçi public import olabilir o ikisinden de ulaşılıyor olmalı?

Teşekkürler...