Forum: Ders Arası RSS
Belirsiz sayıda parametre alan işlevler (variadic functions) için bir çözüm
acehreli (Moderatör) #1
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Belirsiz sayıda parametre alan işlevler (variadic functions) için bir çözüm
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
Avatar
Salih Dinçer #2
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
İ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();
}
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #3
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
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
Avatar
Salih Dinçer #4
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Ç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?
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #5
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
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
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Forum: Ders Arası RSS
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-22, 02:52:40 (UTC -08:00)