Thread overview
Şablon yapıların bir dizide üye eleman olarak tutulması
Jun 29, 2019
kerdemdemir
Jun 30, 2019
kerdemdemir
June 29, 2019
struct AdjustableVal ( T = double )
{
	this ( string name, T initVal, T maxVal, T minVal, T step, bool goUpForSafety )
	{
		this.name = name;
		this.initVal = initVal;
		this.curVal = initVal;
		this.maxVal = maxVal;
		this.minVal = minVal;
		this.step = step;
		this.goUpForSafety = goUpForSafety;
	}

	void GoSafer()
	{
		if (goUpForSafety)
		{
			curVal += step;
			curVal = min( curVal, maxVal);
		}
		else
		{
			curVal -= step;
			curVal = max( curVal, minVal);
		}

		writeln("Adjusted the value ", name, " : ", curVal, " : ", initVal );
	}

	void GoGreedy()
	{
		bool goUpForGreed = !goUpForSafety;
		if ( goUpForGreed )
		{
			curVal += step;
			curVal = min( curVal, maxVal);
		}
		else
		{
			curVal -= step;
			curVal = max( curVal, minVal);
		}
	}

   int opCmp(T s) const {
   	return s < curVal;
   }

	T opBinary(string op)(T rhs)
	{
	    return mixin("rhs"~op~"curVal");
	}

	string name;
   T initVal;
	T curVal;
	T maxVal;
	T minVal;
	T step;
	bool   goUpForSafety;
}

Yukardaki gibi bir yapıyı

AdjustableVal[] adjustableList; gibi bir dizi içinde nasıl üye eleman olarak kullanabilirim.

Şimdilik AdjustableVal!double[] olarak idare edeceğim. Ama integer olarak kullanma durumumda oluştu.

Interface olur, inheritance olur bir fikriniz varsa sevinirim.

Erdemdem

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

June 30, 2019

Abi örnek verdiğin için teşekkür ederim.

Yaşlanıyormuyum ben artık bitler, göstericiler filan çok karışık geliyor ne yazıkki.

Alıntı:

>

AdjustableVal şablonunu interface'ten türetmek ve bir o arayüzün dizisini kullanmak. Bunun sakıncası, her elemanın class olmasının gerekmesi ve bu yüzden iki 64 bitlik gizli gösterge taşımasıdır. (vtbl göstergesi, ve synchronized olanağını desteklemek için gereken ama nadiren işe yarayan 'monitor' göstergesi.) Ek olarak, işlemler sanal işlevler üzerinden yapılacaksa, asıl işlevlerin vtbl üzerinden atlayarak yapılması gerekeceğinden belki de yavaşlık getirecektir. (Denemeden bilinemez.)

Benim bellek veya performans gibi sınırlamalarım yok. Benim için önemli olan okunabilir olması ve basit olması.

Bir denedim interface yapmayı ama çıkamadım işin içinden .

Çünkü


   int opCmp(T s) const {
   	return s < curVal;
   }


	int opBinary(string op)(T rhs)
	{
	    return mixin("rhs"~op~"curVal");
	}

Methodları şablon methodlar. Eğer bunları kullanmak isteyeceksem "cast" kullanmam gerekecek buda kodu karmaşıklaştıracak.

Acaba nasıl çıkabilirim işin icinden interface kullanarak.

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

June 30, 2019

Bildiğin gibi, AdjustableVal dizisi tanımlayamazsın çünkü AdjustableVal tür değil, tür şablonu. O yüzden AdjustableVal!double[] kullanıyorsun çünkü AdjustableVal!double o şablondan üretilmiş bir tür...

double yerine int kullanma ihtiyacı, bu durumda bellek kullanımı dışında bir fark getirmez çünkü 64 bitlik double türü 32 bitlik int'in bütün değerlerini taşıyabilir. (IEEE 64 bitlik tür, değer için 53 bit kullanır.) Tabii, bölme işleminde kalanın göz ardı edilmemesi gibi konular int'ten farklılık getirir.

Eğer gerçekten AdjustableVal!int türü gerekiyorsa, AdjustableVal!double ile aynı dizide kullanılabilmeleri için aklıma gelen üç seçeneğin var:

  • AdjustableVal şablonunu interface'ten türetmek ve bir o arayüzün dizisini kullanmak. Bunun sakıncası, her elemanın class olmasının gerekmesi ve bu yüzden iki 64 bitlik gizli gösterge taşımasıdır. (vtbl göstergesi, ve synchronized olanağını desteklemek için gereken ama nadiren işe yarayan 'monitor' göstergesi.) Ek olarak, işlemler sanal işlevler üzerinden yapılacaksa, asıl işlevlerin vtbl üzerinden atlayarak yapılması gerekeceğinden belki de yavaşlık getirecektir. (Denemeden bilinemez.)

  • Discriminated union denen yöntemi kullanarak her elemanı AdjustableVal!int ve AdjustableVal!double olarak aynı yerde barındırmak. (Bkz std.variant) Ama yine yer kazanamamış olursun çünkü her eleman, en büyük tür olan AdjustableVal!double kadar yer tutar.

  • Farklı türnden elemanları örneğin bir ubyte (veya void) dizisinde tutmak ve her elemanın hangi asıl türden olduğunu bir şekilde kendik belirlemek. Ondan sonra, duruma göre tam türüne değiştirerek kullanmak: cast(AdjustableVal!double) gibi... Hangi tür olduğunu, goUpForSafety'nin kullanılmayan 7 bitinden birisinden yararlanarak belirleyebilirsin: bool yerine 8 bitlik BitField kullanırsın ve bir biti goUpForSafety anlamına gelir, ikincisi double veya int olduğunu belirler ve diğer 6'sı başka işler için kullanılır. Bu yöntemin garipliği, hizalama (alignment) olarak 8 bayt isteyen double'ın duruma göre 4 baytlık veya tek baytlık adrese rastlamasıdır. Ama sanırım x86 mimarisinde buna izin veriliyor ama sanırım yavaşlık getirebiliyor. (Denemeden bilinemez.) Ne demek istediğiminin bir örneğini göstereceğim.

Ek: Üçüncü yöntem rasgele erişimi etkin olarak sağlayamaz; elemanlara sırayla erişmek gereken durumlarda kullanılabilir.

Ali

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

June 30, 2019

Üçüncü yöntemin bir örneği:

import std.stdio;
import std.bitmanip;
import std.range;

alias MyBits = bitfields!(
 bool, "int_mi", 1,
 bool, "goUpForSafety", 1,
 ubyte, "", 6); // Doldurma bitleri (toplam bit sayısı 8'in katı olmalı)

struct Bitler {
 mixin(MyBits);
}

// En az yer kaplamak için align(1)'ler:
align(1)
struct Foo(T = double) {
align(1):
 Bitler bitler;
 string name;
 T initVal;
 T curVal;

 // Not: Burada goUpForSafety'yi bool olarak kullanmak okunurluk açısından yanlıştır.
 //      Onun yerine Flag!"goUpForSafety" goUpForSafety gibi bir çözüm gerekir ama burada konu dışı.
 this(bool goUpForSafety, string name, T initVal, T curVal) {
   static if (is (T == double)) {
     this.bitler.int_mi = false;

   } else static if (is (T == int)) {
     this.bitler.int_mi = true;

   } else {
     static assert (false, "Yalnızca double veya int kullanılabilir");
   }

   this.bitler.goUpForSafety = goUpForSafety;
   this.name = name;
   this.initVal = initVal;
   this.curVal = curVal;
 }
}

struct FooDizisi {
 ubyte[] yer;

 void ekle(T)(Foo!T eleman) {
   // eleman'ı oluşturan baytları diziye oldukları gibi ekliyoruz
   yer ~= (cast(ubyte*)&eleman)[0..eleman.sizeof];
   // Burada emplace kullanan bir çözüm azıcık daha hızlı olabilir. (Denemek gerek.)
 }

 // Desteklediği her tür için farklı işlev alan bir işlev:
 void hepsiyle(alias doubleİşlevi,
               alias intİşlevi)()
 {
   auto geçici = yer[];

   while (!geçici.empty) {
     // Her elemanın en başındaki bitlere bakıyoruz.
     const bitler = *cast(Bitler*)(geçici.ptr);

     // Duruma göre Foo!double veya Foo!int olarak kullanarak uygun işlevi çağıracağız
     if (bitler.int_mi) {
       intİşlevi(*cast(Foo!int*)(geçici.ptr));
       geçici = geçici[Foo!int.sizeof..$];

     } else {
       doubleİşlevi(*cast(Foo!double*)(geçici.ptr));
       geçici = geçici[Foo!double.sizeof..$];
     }
   }
 }
}

void main() {
 FooDizisi dizi;

 foreach (i; 0..10) {
   if (i % 2) {
     dizi.ekle(Foo!int(false, "bu bir int", -i, i));

   } else {
     dizi.ekle(Foo!double(true, "bu bir double", i + 0.5, double(i) / 100));
   }
 }

 writefln!"Dizide %s bayt:\n%(%02x %)"(dizi.yer.length, dizi.yer);

 // Burada isimsiz işlevlerle kullanıyoruz:
 dizi.hepsiyle!(
   (Foo!double d) {
     writefln!"elimde bir double var: %s"(d);
   },

   (Foo!int i)     {
     writefln!"elimde bir int var: %s"(i);
   });

 // Burada aynı işlev şablonunu kullanıyoruz:
 dizi.hepsiyle!(benimŞablon!double, benimŞablon!int);
}

void benimŞablon(T)(Foo!T eleman) {
 writefln!"init / cur: %s"(eleman.initVal / eleman.curVal);
}

Ali

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

June 30, 2019

Aslında bitlerde bir zorluk yoktur. Yani kavramsal olarak kafayı zorlayan bir şey yok. Tek zorluk, henüz bilmemek olabilir. O da biraz zaman harcayarak halledilir. :)

Göstergede yaptığım tek şey de şu: Her nesne, baytlarından oluşan bir ubyte dizisi olarak düşünülebilir. Nesnenin adresi alınır ve orası ubyte dizisi olarak kullanılır. Bitti.

interface konusu aşağıdaki gibi. Ancak, opCmp'un nasıl yazılacağı açık değil. Örneğin, acaba bütün Foo!int'ler Foo!double'lardan önce mi gelecek? Yoksa her nesnenin değeri alınacak da onlar mı karşılaştırılacak? Eğer öyleyse int'in değeri double'a dönüştürülüp karşılaştırma o türde mi yapılacak? Foo'nun her alt türünün değerinin olacağı garanti mi? vs.

Aslında T'nin neden int veya double olabildiğini tam anlamadığımı farkediyorum. Çünkü neden hep double olmasın? double, int'in bütün değerlerini de barındırabilir. Açıklarsan kodun eksiklerini tamamlayabiliriz.

import std.stdio;
import std.algorithm;

interface Foo {
 void bar();

 int opCmp(Foo that) const;
}

class FooImpl(T = double) : Foo {
 T val;

 this(T val) {
   this.val =  val;
 }

 override void bar() {
   writefln!"%s türünde %s"(T.stringof, val);
 }

 override int opCmp(Foo that) const {
   // Nasıl bir mantık kullanacağız?
   return 0;
 }
}

void main() {
 Foo[] dizi;

 foreach (i; 0..10) {
   if (i % 2) {
     dizi ~= new FooImpl!double(double(i) / 2);

   } else {
     dizi ~= new FooImpl!int(i * 10);
   }
 }

 dizi.each!(e => e.bar());
 writefln!"%(%s\n%)"(dizi);
}

Ali

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