Merhaba,
Birkaç gündür opApply() ile ilgili denemeler yapıyorum. Gerçi bu sene nisan ayında da karşılaşmış değerini bilememişim. İşte o günden gelen örneğin (hem aralık hem opApply ile çalışan) gelişmişi:
auto applyRange(R)(R list)
{
alias T = typeof(list.iter);
struct Range
{
R list;
int opApply(scope int delegate(size_t, T node) dg)
{
size_t counter;
while (auto current = list.Next)
{
if (auto r = dg(++counter, current))
return r;
}
list.rewind();
return 0;
}
}
return Range(list);
}
auto Next(R)(R list)
=> list.iter = list.iter.next;
import std.stdio;
enum limit = 10;
void main()
{
// önce limit kadar bir liste oluşturulur...
auto list = new SimpleList!ulong;
foreach(n; 1 .. limit + 1)
{
list.insertFront(n);
}/* bu listeyi doğrulamak için Gauss'un formulü
yedeği alındıktan sonra uygulanır ve varsayılan
limit = 10 olduğu için sonuç 55 olmak zorundadır
*/
auto tmp = list.dup();
list.rewind();
size_t sum; // null karakter gelene kadar topla:
do sum += list.getItem; while(list.Next);
// işte, doğru çalıştığının kanıtı
auto gaussian(T)(T n)=> (n * n + n) / 2;
assert(gaussian(limit) == sum);
// ekrana da yazalım:
sum.writeln("<- 55 doğru olan"); // 55
// 10 sayısı dahil 1'den itibaren toplamlar 55 ise,
auto next = limit + 1;
tmp.insertFront(next);
tmp.rewind();
sum = 0;
foreach(t; tmp) sum += t.item;
assert(gaussian(limit) + next == sum);
sum.writeln("<- 66 doğru olan"); // 66
// sonraki sayı (next) 11 olacağından 66 olması gerekir.
tmp.rewind();
// son test, listeyi rewind() yaptıktan sonra gerçekleşir:
auto range = tmp.applyRange();
foreach(i, r; range)
{
assert(i == r.item);
}/* evet, listede 0'ı (ilk düğümü) saymazsak 11 sayı varmış
* bunu opApply'dan gönderilen i sayacı (counter) ile anldık.
* Üstelik döngü bittikten sonra tekrar rewind() yaptığından
* aralık teorik olarak tüketilmiyor. Ancak dikkat sayaç 0'dan
* değil 1'den başladığı için assert hata vermiyor!
*/
// v-- tek sorun i'yi kullanmasak bile tanımlanmalı :)
foreach(i, r; range) r.item.write(" ");
writeln; // 1 2 3 4 5 6 7 8 9 10 11
// Programın sonu: İşte aralık listeleniyor ama tükenmiyor
}
struct Node(T)
{
T item;
Node* next;
}
class SimpleList(T) // support with InputRange
{
Node!T * root, iter;
this(T item = 0)
{
iter = new Node!T(item, null);
root = iter;
}
auto rewind() => iter = root;
SimpleList dup()
{
auto backup = new SimpleList();
backup.root = root;
backup.iter = iter;
return backup;
}
auto getItem() => iter.item;
void insertFront(T item)
{
iter.next = new Node!T(item, null);
this.Next;
}
bool empty() const => iter is null;
auto front() inout => iter;
auto popFront() => iter = this.Next;
}
Aslında kod kendini açıklıyor ve burada kullanılan bağlı liste ve kolaylık işlevleri ile üst düzey bir şey yapılıyor ki zamanında burada tartışılmıştı. Ben sadece konuyu dahili sayaç seviyesine kadar geliştirdim. Çünkü dizilerden alışık olduğumuz bu yapıyı, foreach() içine ekleyebildiğimiz 2. parametre ile koda dahil edebiliyorduk.
Kodun içinde main() sonlarındaki test ile bir soruna işaret ediyorum. Eğer opApply() ile 2. bir parametre foreach() içine gönderecekseniz, kullanmasanız bile tanımlamanız gerekiyor. Ayrıca bu parametreler 2'den fazla da olabilir, bilginiz olsun. Peki bilmeyenler için opApply() nedir:
Aslında foreach(), InputRange aralığı olsa bile ilk buna bakıyor; empty, front ve popFront işlevleri implement edilse dahi bunlar by-pass oluyor. Böylece, tanımladığınız döngü aslında foreach() içinde çalışıyormuş gibi düşünün. Malum orada, parametreler içinde bir delegate (temsilci) tanımlıyoruz.
Güzel olanak değil mi?