Merhaba,
Aşağıdaki gibi, BüyükHarf(E)
'leri aralık olarak döndüren bir yapımız olsun. Bunun sınırlarını bir enumerator(enum, E
) ile pratik şekilde ve derleme zamanında kurulurken bildirebiliriz; örnekteki ARALIK
gibi...
enum ARALIK { başı = 'A', sonu = 'F' }
struct BüyükHarf(E)
{
invariant(E.başı >= 65, "\n A harfinden az olamaz");
invariant(E.sonu <= 90, "\n Z harfinden fazla olamaz");
//static
private char im = E.başı;
@("InputRange İşlevleri:") //<-- kabul, UDA'in gereksiz kullanımı :)
{
bool empty() { return im > E.sonu; }
char front() { return im; }
void popFront() { ++im; }
}
auto opCall(char c)
in(c >= 65, "\n A harfinden az olamaz")
{
auto öncesi = im;
//scope(exit) im = öncesi;
im = c;
return this;
}
}
import std.stdio;
void main()
{
BüyükHarf!ARALIK harfler;
harfler.writeln; // "ABCDEF"
foreach(harf; harfler('C'))
{// opCall() özelliği--^
harf.write(" ");
}
writeln; // "C D E F "
harfler.writeln; // "CDEF" ops!!!
}
/* opCall()'da scope(exit) varsa, aldığım çıktı:
dmd -w -run "struct.d" (/home/salihdb/Belgeler/YeniD/Forumdan Sorular dizininde)
ABCDEF
C D E F
ABCDEF
Derleme başarılı.
*/
Elbette bu kadar tantanaya gerek yoktu. Maksadımız öğrenmek, yoksa aşağıdaki gibi (tabi F'yi dahil etmeyecekti!) tek satırla aralık oluşturabilirdik ve kodumuzun herhangi bir yerinde kullanabilirdik:
import std.range : iota;
auto harfAralık = ARALIK.başı.iota!char(ARALIK.sonu);
Ama ekstralar kullanmadan bir şeyi yapamazdık: Aralığın başladığı nokta private
değişken olduğuna göre, bunu değiştirip oradan itibaren gezinmesi kolay olmazdı. Tamam, aşağıdaki gibi filitrelemek elbette mümkün ama arkaplanda bir "skipping with iteration" olsa gerek:
import std.algorithm : filter;
harfAralık.filter!(c => c >= 'C').writeln; // "CDE"
Öte taraftan, Ali hocanın InclusiveRange()
gibi aralık oluşturan bir yapıya ihtiyacımız var. Çünkü iota()
bizim için pratik değil; hele bu örnekte hiç! Eğer F'yi de dahil etmek isteseydiniz ve ARALIK
'ı kullanarak iota!char(başı, sonu + 1)
işe yaramayacaktı! Özellikle char
'ı belirtmemize rağmen şu çıktıyı alacaktık:
[67, 68, 69, 70]
Çünkü otomatik olarak int'e bir tür dönüşümü gerçekleşti, bence saçma! Örneğimize geri dönersek...
Klasik üçlü InputRange
işlevleriyle pekala aralıkta gezinebiliriz. Özellikle foreach()
kullanırsak aralığın yedeğini alacağı için tükenmiş olması dıştaki satırları etkilemeyecekti; nedense opCall()
ile istisna!
auto opCall(char c) {
auto öncesi = im;
scope(exit) im = öncesi;
im = c;
return this;
}
Yukarda çalışma zamanı[1]
sözleşme öğesini kaldırıp, scope(exit)
'ı etkinleştirdim. Bu kapsamdan çıkıldığında im
değişkenini tekrar öncesine dönmesini sağlar. Yani yan etkiyi düzeltmek için ekstra özelliğe ihtiyacımız vardı ve bunu bize D sağladı.
Sorulara gelince; bu yan etki niye var ve daha temiz/güvenli bir aralık üreten yapı kodlanabilir miydi? Bunu soruyorum çünkü foreach()
'in aralığı tüketmeden önce yedeğini aldığını biliyorum. Elbette kapsam (main
) dışında kolaylık işlevleri[2]
yapılabilirdi. Ama yedeğini aldığım nesnenin opCall
'ını çağırdığımı ve bunun static char im
diye bir değişkeni olmadığını düşünüyorum!
- Kabul, bu derleme zamanında hata vermesi sağlanabilirdi.
- Kolaylık işlevlerini denemedim çünkü çok basitti ama yedeğini aldığımda
opCall
içinde ekstra bir yedekleme ihtiyacı duymadım.
Teşekkürler...
Salih Dinçer