Thread overview
Referans türleri ve değer türleri ile ilgili güzel bir açıklama
Dec 05, 2014
Mengu
Jun 26, 2015
kerdemdemir
December 02, 2014

İngilizce D.learn haber grubunda şu soru soruldu:

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

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--+
| --------> | +---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; | |
| | ---------->| +---------+ |
| +---------------+ | +-------------+
+-------------------+
'
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; | |
| | ---------->| +---------+ |
| +---------------+ | +-------------+
+-------------------+ ^
|
+----U v------------+ |
| int y; | |
| +---C c---------+ | |
| | ------------------+
| +---------------+ |
+-------------------+
'
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 gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

December 05, 2014

mukemmel bir cevap degil mi?

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

tesekkurler ceviri icin.

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

June 26, 2015

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

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

June 26, 2015

Alıntı (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
}

Alıntı:

>
> 	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

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