Forum: Ders Arası RSS
Referans türleri ve değer türleri ile ilgili güzel bir açıklama
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ı: Referans türleri ve değer türleri ile ilgili güzel bir açıklama
İngilizce D.learn haber grubunda şu soru soruldu:

  http://forum.dlang.org/thread/qrzbwbxckmpkuhwnwjzc@forum.d…

H. S. Teoh'un yanıtını beğendim ve Türkçe'ye çevirdim.

Hep başıma geldiği gibi, şu üç deyimi aynı anlamda kullandım: "referans olmak", "işaret etmek", "erişim sağlamak".

Ali

On Mon, Dec 01, 2014 at 04:42:36AM +0000, WhatMeWorry via Digitalmars-d-learn wrote:

> D'nin referans türlerinin (sınıflar, dinamik diziler, vs.) her zaman için
> dinamik bellekte (heap) olduklarını söyleyebilir miyiz? Benzer biçimde,
> değer türleri (yapılar, statik diziler, vs.) de her zaman için program
> yığıtında (stack) mıdırlar? Yoksa böyle söyleyince konuyu fazla mı
> basitleştirmiş oluyoruz?

Fazla basitleştirildiği doğru çünkü değer türleri de dinamik bellekte bulunabilirler (örneğin, `BirYapı* gösterge = new BirYapı(...)`). Ancak, hem buna nadiren gerek duyulur hem de gerek duyulduğunda class kullanmak aslında daha uygun olur. Ek olarak, sınıf nesnelerini program yığıtında (veya yapı nesnelerini dinamik bellekte) kuran emplace() de var; ve statik sınıf dizilerinin elemanları da dinamik bellekte olmak zorundadır çünkü onların yerleştirilecekleri program yığıtı yoktur.

> Sormamın nedeni, yapıların sınıf üyeleri olabileceği gibi sınıfların da yapı
> üyeleri olabilir, değil mi? Öyleyse, böyle karışık türler konusunda ne
> söyleyebiliriz? Değer veya referans türü olması en dıştaki türün hangi
> çeşitten olduğuna mı bağlıdır?

Öyle düşünmek açıklamaya yetmiyor. Daha uygun olan bir düşünce tarzı şöyle: değer türü == "tam burada bulunan" ve referans türü == "başka bir yerde bulunan". Örneğin, şöyle bir yapı olsun:
    struct S {
        int x;
    }
main() içinde S türünden bir değişken tanımlandığında S'nin içeriği "tam burada"dır. Yani, işlevin işletilmesi sırasında program yığıtındadır:
    void main() {
        S s; // "tam burada", yani program yığıtında
    }
S türünde bir üye tanımlandığı durumda da S'nin içeriği onu içeren kapsamdadır:
    class C {
        S s;    // s, C'nin içeriğinin parçasıdır
    }
 
    struct T {
        S s;    // s, T'nin içeriğinin parçasıdır
    }
Bunu aşağıdaki şekil ile gösterebiliriz:

    +--C nesnesi--+
    | +---S s---+ |
    | | int x;  | |
    | +---------+ |
    +-------------+

Üye s, C değişkeninin yaşadığı bellek bloğunun bir parçasıdır.

Öte yandan, bir sınıf nesnesi "başka bir yerde"dir. C türünde bir değişken tanımlandığında o değişken nesnenin kendisi değil, başka bir yere işaret eden bir göstergedir:
    void main() {
        C c = new C();
    }

    Program yığıtında:      Dinamik bellekte:
    +------C c------+       +--C nesnesi--+
    |  <referans> --------> | +---S s---+ |
    +---------------+       | | int x;  | |
                            | +---------+ |
                            +-------------+

Görüldüğü gibi, c değişkeni program yığıtındadır ve asıl nesneyi içermemektedir; c, asıl nesnenin bulunduğu dinamik bellekteki bir yere işaret etmektedir.

Yapı içinde sınıf olan duruma bakalım.
    struct U {
        int y;
        C c;
    }
 
    void main() {
        U u;
    }

    Program yığıtında:         Dinamik bellekte:
    +----U u------------+      +--C nesnesi--+
    | int y;            |      | +---S s---+ |
    | +---C c---------+ |      | | int x;  | |
    | |  <referans> ---------->| +---------+ |
    | +---------------+ |      +-------------+
    +-------------------+

Görüldüğü gibi, u oldukça ilginç bir nesnedir çünkü kendisi hem program yığıtında hem de dinamik bellekte bulunmaktadır! Kendisinin 'int y' ve 'c' parçaları program yığıtındadır ama 'c'nin işaret ettiği nesne dinamik bellektedir. U'nun yığıtta bulunan parçaları değer türünde olan üyeleridir, c parçası ise referans türündedir -- örneğin, aşağıdaki ifadeyi işletelim:
    U v = u;
v, 'int y' üyesinin bir kopyasını içerir ama 'C c' üyesi u'nun da işaret etmekte olduğu aynı C nesnesidir:

    Program yığıtında:         Dinamik bellekte:
    +----U u------------+      +--C nesnesi--+
    | int y;            |      | +---S s---+ |
    | +---C c---------+ |      | | int x;  | |
    | |  <referans> ---------->| +---------+ |
    | +---------------+ |      +-------------+
    +-------------------+             ^
                                      |
    +----U v------------+             |
    | int y;            |             |
    | +---C c---------+ |             |
    | |  <referans> ------------------+
    | +---------------+ |
    +-------------------+

u.y ve v.y değişkenlerinde değişiklik yapmak karışıklık yaratmaz. Ancak, u.c'yi değiştirmek v.c'yi de değiştirecektir çünkü her ikisi de dinamik bellekteki aynı nesnenin referansıdır.

Bunun hangi durumlarda yararlı olabileceğini merak ediyor olabilirsiniz. D dilimlerinin bu kadar kullanışlı olmalarının temeli aslında buna dayanır: Dinamik diziler perde arkasında bir adet değer türündeki üyeden, bir adet de referans türündeki üyeden oluşurlar:
    struct _d_array(T) {
        size_t length;
        T* ptr;
    }
Bir dizinin bir dilimi alındığında bu türden bir yapı nesnesi kopyalanır ve onun .length ve .ptr üyelerine uygun değerler atanır; artık iki farklı kopya dinamik bellekte bulunan aynı diziye erişim sağlamaktadırlar. Buradaki önemli nokta, _d_array'in değer türü olması nedeniyle başlangıçtaki dilim nesnesinin değişmemiş olması, buna rağmen _d_array'in referans üyesi sayesinde asıl elemanların yeni dilim yoluyla da değiştirilebilmesidir. Örnek:
    void main() {
        int[] a = [1,2,3]; // asıl dizi
        int[] b = a[0 .. 1]; // asıl dizinin bir dilimi
 
        assert(a == [1,2,3]); // asıl dilim değişmez
 
        b[0] = 4; // dizi içeriği yeni dilim yoluyla değişebilir
        assert(a[0] == 4); // ve bu asıl dizide de gözlemlenir
    }
> Sormadan edemeyeceğim. Belki de kendim denemeliyim ama bir yapı üyesinin
> sınıf üyesinin yapı üyesinin sınıf üyesinin... gibi bir durum olsa? Buna
> hangi durumlarda gerek olabileceğini bilmiyorum ama teoride de olsa sormak
> istedim.

[...]

Aslında hiç de teoride kalan bir soru değildir. Farkında bile olmadan dizi dilimi olarak zaten her gün kullanıyorsun. :-) Örneğin, aşağıdaki dizi dizisine bakalım:
    int[][] dizi;
'dizi', değer türü .length üyesinden ve referans türü .ptr üyesinden oluşan _d_array türünde bir değişkendir. 'dizi'nin .ptr üyesi, kendileri _d_array türünde olan elemanlara erişim sağlar; bu üyelerin kendi .length üyeleri (bunlar dinamik bellektedir!) ve kendi .ptr üyeleri vardır (bunlar alt dizilere işaret ederler). Bir başka deyişle, .length üyeleri "tam burada"dırlar ('dizi' değişkenininki program yığıtında olabilir ama alt dizilerinki dinamik bellektedir) ama .ptr üyeleri "başka bir yerde" olan elemanlara işaret ederler (normalde dinamik bellektedirler ama emplace() ile herhangi bir yerde de bulunabilirler).

T
Bu mesaj acehreli tarafından değiştirildi; zaman: 2014-12-02, 23:54.
Mengu (Moderatör) #2
Kullanıcı başlığı: NONSERVIAM
Üye Tem 2009 tarihinden beri · 347 mesaj · Konum: Dersaadet
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
mukemmel bir cevap degil mi?

suan mesela dilimleri cok cok daha iyi anladim. her cumlesi bilgi dolu bir cevap.

tesekkurler ceviri icin.
http://www.mengu.net - some kind of monster
kerdemdemir #3
Üye Eyl 2013 tarihinden beri · 123 mesaj · Konum: Danimarka
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Arkadaşlar Merhaba,

"D Learn" forumunda sormak istiyorumdum fakat konu ile ilgili gibi geldi burda sorayım dedim
 
auto autoCorrelation(R)(R range)
        if (isRandomAccessRange!R)
{
    auto residual = residualPowerOf2(range.length);
    auto fftResult = range.chain(repeat(0, residual)).fft();
    
    auto autoCorrResult = fftResult.zip(fftResult.map!(a => a * a.conj())).
                            map!( a=> a[0] * a[1] ).
                            inverseFft().
                            dropBack(residual); 
            
    auto finalResult = autoCorrResult.take(1).
                        chain(autoCorrResult[$/2..$]).
                        chain(autoCorrResult[1..$/2]).
                        map!(a => a.re);
                        
    return finalResult ;
}
 
auto normalCorrelation = autoCorrelation(range).map!(a => a*2);
auto normalCorrelation[0] -= 2; ==> normalCorrelation.opIndex(cast(uint)index) is not an lvalue

Acaba niye L-value değil. Kendimce yukarda anlattığın örnekteki b[0] = 4'e benzetiyorum.

Ayrıca daha garip bir konu :

    auto finalResult = autoCorrResult.take(1).
                        chain(autoCorrResult[$/2..$]).
                        chain(autoCorrResult[1..$/2]);
            // map!(a => a.re); ==> bu satırı comment out yapıyorum. Ve complex!double dönüyorum
 
auto normalCorrelation = autoCorrelation(range).map!(a => a*2);
auto normalCorrelation[0] -= 2; ==> Kod derleniyor fakat gerçek dizide hiç bir değişiklik olmuyor

Ali Hocam sen tüm kodu görmeyi seversin eğer vaktin olursa aşağıdaki linkte bulabilirsin;

http://codrspace.com/kerdemdemir/d-code-yinsfft/

İlk örnek niye L-value değildi?
İkinci örnekte sadece tipin değişmesinde sonra derleme hatası neden gelmedi? 

Saygılarımla
Erdem
acehreli (Moderatör) #4
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ı
kerdemdemir:
auto normalCorrelation = autoCorrelation(range).map!(a => a*2);
auto normalCorrelation[0] -= 2; ==> normalCorrelation.opIndex(cast(uint)index) is not an lvalue

Lvalue, var olan değişken anlamındadır. normalCorrelation ise autoCorrelation(range)'in döndürdüğüne map'in uygulanmış hali oluyor. map ise genelde yeni değer üreten bir algoritma olduğuna göre (örneğin, yukarıdaki, verilen a değerine karşılık a*2 diye yepyeni bir değer (ifade) üretiyor).

normalCorrelation[0], yeni bir ifadedir ve dolayısıyla rvalue'dur.

Aynı sorun şu kodda var:

import std.range;
import std.algorithm;
 
void main()
{
    auto r = 5.iota.map!(a => a * 2);
    r[0] = 42;    // <-- DERLEME HATASI
}

    auto finalResult = autoCorrResult.take(1).
                        chain(autoCorrResult[$/2..$]).
                        chain(autoCorrResult[1..$/2]);
            // map!(a => a.re); ==> bu satırı comment out yapıyorum. Ve complex!double dönüyorum
 
auto normalCorrelation = autoCorrelation(range).map!(a => a*2);
auto normalCorrelation[0] -= 2; ==> Kod derleniyor fakat gerçek dizide hiç bir değişiklik olmuyor
İkinci soruyu anlamadım ama anlaşılan, o durumda normalCorrelation[0] asıl eleman dediğimiz elemana değil, olasılıkla onun bir kopyasını döndürüyor. Dolayısıyla, değişiklik kopyayı etkiliyor.

Bunun gibi sorunlarla aralık kodlarında karşılaşılabiliyor: Asıl eleman mı? Kopyası mı? Geçici mi, kalıcı mı? vs.

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-19, 19:44:01 (UTC -08:00)