Thread overview
Belirsiz sayıda parametre alan işlevler (variadic functions) için bir çözüm
Mar 09, 2015
Salih Dinçer
Mar 09, 2015
Salih Dinçer
January 08, 2015

Bugün öğrendiğim bir bilgiyi aktarıyorum: İşlevler 'int[] sayılar...' ve 'int[] sayılar' ile yüklenebiliyorlarmış.

Belirsiz sayıda ama aynı türden parametre alan işlevler o parametreleri dilim olarak görürler:

import std.stdio;

void main()
{
   auto s = S(-1, 0, 1, 10, 42);    // <-- Normal parametre değerleri
}

struct S
{
   this(int[] sayılar...)    // <-- Dilim olarak görür
   {
       writefln("%((%s)%| %)", sayılar);
   }
}

Çağrılan yerde dilim söz dizimi kullanılmadığı halde işlev tarafında dilim görülür. Kurucu işlev sayıları parantez içinde yazdırıyor:
'
(-1) (0) (1) (10) (42)
'
Buraya kadar bir sorun yok çünkü sayıları yazdırdık ve işimiz bitti. Ancak, bu forumda daha önce de konuşmuş olduğumuz gibi, burada önemli bir ayrıntı var: Parametre olarak görünen dilim, program yığıtında oluşturulur. Dolayısıyla, bir yapı (veya sınıf) üyesine atansa ömrü çoktan bitmiş olabileceğinden hataya neden olur.

Bu sefer, aldığımız dilimi bir üyeye atayalım ve sonra kullanalım:

import std.stdio;

S S_yap()
{
   return S(-1, 0, 1, 10, 42);

}  // <-- UYARI: Oluşturulan dilimin yaşamı bu noktada sona erer

struct S
{
   int[] sayılar;

   this(int[] sayılar...)
   {
       this.sayılar = sayılar;  // <-- Sonradan kullanmak üzere saklıyoruz
   }

   void kullan()
   {
       writefln("%((%s)%| %)", sayılar);
   }
}

void main()
{
   auto s = S_yap();
   s.kullan();
}

s'nin üyesi olan 'sayılar' çoktan sonlanmış olan elemanları gösterdiklerinden çıktısı şunun gibi bozuk olabilir:
'
(1882273040) (32767) (4203658) (0) (11)
'
Yukarıdaki hatalı sonuca düşmemek için 'int[] sayılar...' parametresinin .dup ile kopyalanması gerekir. Bunun bir masrafı olduğu açık...

İşin garibi, 'int[] sayılar...' söz dizimi normal dilimleri de kabul eder. Dolayısıyla, S_yap() içindeki S açıkça dilim ile oluşturulsa sorun yoktur:

   return S([-1, 0, 1, 10, 42]);    // <-- Yaptığımız tek değişiklik

Açıkça oluşturulan dilimin yaşamı çöp toplayıcı tarafından yönetildiğinden döndürülen S nesnesi yaşadığı sürece yaşamaya devam eder. Güzel:
'
(-1) (0) (1) (10) (42)
'
Dolayısıyla, çağıran açıkça dilim verdiğinde .dup ile kopyalamaya gerek yoktur.

Buna rağmen, ben bu güne kadar hep .dup ile kopya almak gerektiğini düşünüyordum. Bugün öğrendiğime göre, 'int[] sayılar...' ile 'int[] sayılar' yüklenebiliyormuş. Belirsiz sayıda parametre birincisine, açıkça dilim ikincisine gidiyormuş. Dolayısıyla, hangisinde .dup ile kopya almamız gerektiğini bilebiliyoruz:

import std.stdio;

S S_yap()
{
   return S(-1, 0, 1, 10, 42);
}

struct S
{
   int[] sayılar;

   this(int[] sayılar...)
   {
       /* Belirsiz sayıda parametre (variadic) ile çağrıldık. Bu dilim
        * program yığıtındadır. Hataya düşmemek için kopyalamamız gerek: */
       this.sayılar = sayılar.dup;
   }

   this(int[] sayılar)
   {
       /* Açıkça dilim ile çağrıldık kopyalamaya gerek yok. */
       this.sayılar = sayılar;
   }

   void kullan()
   {
       writefln("%((%s)%| %)", sayılar);
   }
}

void main()
{
   auto s = S_yap();
   s.kullan();

   auto s2 = S([ 1000, 2000 ]);
   s2.kullan();
}

Her iki kullanım da hatasız:
'
(-1) (0) (1) (10) (42)
(1000) (2000)
'
Ali

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

March 09, 2015

İlginç şekilde, aşağıdaki gibi Türkçe karakterleri değiştirip http://dlang.org sitesinde denediğimde, doğru biçimde çalıştığını gördüm. Yani değerler görülüyor! Bu durumda, web için bir tür özel yaşam süreleri belirlenmiş olabiir.

#!/usr/bin/env rdmd
import std.stdio;

S S_yap() {
   return S(-1, 0, 1, 10, 42);
}
struct S {
   int[] sayilar;

   this(int[] sayilar...) {
       this.sayilar = sayilar;
   }

   void kullan() {
       writefln("%((%s)%| %)", sayilar);
   }
}
void main() {
   auto s = S_yap();
   s.kullan();
}

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

March 09, 2015

Çok iyi anladım!

Ama bunun derleme esnasında hata vermesi gerekmez mi? En azından, geçerliliğini yitirmiş değişkenlere erişildiğini satır numarası ile birlikte uyarı görebilceğimiz bir derleme parametresine sahip miyiz?

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

March 09, 2015

Belki rdmd'nin bir etkisidir. Ben dmd ile elle derliyorum.

Yaşamı sona ermiş değişkene erişmenin tanımsız davranış (undefined behavior) olduğunu biliyoruz. Tanımsız davranışla ilgili bir talihsizlik, programın tam da istenen biçimde işlemesiz ve bu tür hataların bazen yıllarca farkedilmemesidir.

Program sonra örneğin ilgisiz bir printf satırı eklendiğinde hatalı işler.

Burada dilimin ömrünün sona erdiğinden söz ediyoruz. Ömürleri sona erdi diye elemanların değerlerinin silinmeleri söz konusu değildir. O yüzden, o eleman değerleri bellekte aynen duruyorlar. Program aynı yeri sanki yaşıyorlarmış gibi kullanınca da beklenen değerler görünüyor. Araya başka bir ifade eklense o yer başka bir amaçla kullanılabilir ve hata ortaya çıkabilir.

Ali

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

March 10, 2015

Alıntı (Salih Dinçer):

>

derleme esnasında hata vermesi gerekmez mi

Evet, derleyici bütün kodu görebildiği durumlarda yardımcı olabilir ama dmd henüz o kadar akıllı değil. (ldc ve gdc için de durum aynı çünkü onlar da dmd'yi kullanıyorlar).

Ancak, bu uyarının verilemeyeceği durumlar da var: Parametrelerini 'int[] sayilar...' biçiminde alan işlevin içeriği bu derlemede mevcut olmayabilir. Örneğin, işlev yalnızca bildirilmiştir (declared) ama tanımı daha önce bir .o dosyası olarak derlenmiştir. S_yap()'ın derlendiği anda derleyici kodda hata olmadığını varsaymak zorunda olduğundan hata veya uyarı veremez.

Ali

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