Değer ve referans tiplerle ilgili

Referans tipleri anlatırken sıkça şuna benzer bir örnek veriliyor:

a = [1,2]
b = [3,4]
a = b
b[0] = 5
print(a[0])

Bunun çıktısının 5 olduğu gösterilerek referans tiplerin değer tiplerden farkı anlatılıyor. Aynı zamanda “string, bir referans tiptir” cümlesini de söylüyor aynı kişiler. Bunun üzerine string ile aynısını denediğimde bir değer tip gibi davrandığını görüyorum:

a = "bir"
b = "iki"
a = b
b = "üç"
print(a) 

Burada çıktı olarak “üç” beklerken “iki” alıyorum. Bunun nedeni nedir acaba? String, bir referans tip değil midir?

İlk örnekte bir nesnenin (listenin) içeriğini değiştirirken ikinci örnekte bir değişkenin sakladığı nesneyi değiştiriyorsunuz.

Bildiğim kadarıyla referans tiplerde a = b gibi bir atama yapıldığında değişkenin sakladığı nesne değil yalnızca referans numarası değiştiriliyor.

İlk örnekte print(a) yapıldığında çıktı [5, 4] olacaktır. Bunun sebebi; b[0] = 5 tanımladık.
İkinci örnekte a = b diyerek "iki" string değerini a değişkenine tanımladık. a değişkeninin değeri artık "iki" iken, b adındaki değişkenin değerini de "üç" olarak tanımladık.
Dolayısıyla;
print(a) çıktısı iki
print(b) çıktısı üç olacaktır.

"Referans tipi"ni ve "referans numarası"nı tanımlarsanız daha rahat yardım edeceğim. Bunları tam olarak hangi anlamda kullanıyorsunuz?

1 Beğeni

İyi niyetiniz için çok teşekkür ederim. Fakat değer ve referans tiplerini anlamaya çalışıyorum ben de zaten. Yani bu kavramlara hakim birinin yardımına ihtiyacım var.

Standart tanimlari mevcut: Value type and reference type - Wikipedia

Fakat deger/referans tipi ayrimi tek basina ise yarayan bir ayrim degil. Kelimeler kimi tartismalari kolaylastirmak icin uretilen bir nevi soyutlama olduklarindan oturu tartismalarin disinda tek baslarina pek bir ise yaramiyorlar.

C’deki herhangi bir pointer turunun referans turu mu yoksa referans degeri tasiyan bir deger turu mu oldugu hangi soyutlama seviyesinden baktiginiza gore degisir ornegin. (Linkteki tablonun dogrulugu da)

O yuzden bu tipler konusulurken onlerindeki tartismayi rica edecegim. Soru gayet guzel mesela:

a = [1,2]
b = [3,4]
a = b
b[0] = 5
print(a[0]) # 5
a = "bir"
b = "iki"
a = b
b = "üç"
print(a) # "iki"

Cevabi basit ve refrerans/deger tipleriyle bir alakasi yok malesef: Yazdigin kodlar farkli, paralellikleri yok.

Ilk kodda a ile b ayni objeye isaret (refer) ediyor, biri uzerinden yaptigin degisikligi digeri uzerinden gorebiliyorsun. Ikinci kodda a ile b ayni objeye isaret ederken b’yi degistirip farkli bir objeye isaret ettiriyorsun, herhangi bir degisiklik yok.

Ilk koddaki gibi b[0] = 5; print(a[0]) diybileseydin ayni sonucu alirdin.

Veya, ters perspektiften bakarsak:

Neden?

a = [1,2]
b = [3,4]
a = b
b = [5,6]
print(a)

Burada cikti olarak [5,6] mi bekliyorsun ki?

3 Beğeni

“Referans tipi” bir soyutlama oldugundan oturu hangi tabanda konustugumuzu netlestirmemiz lazim.

Mesela burada Python’in calisma sekli anlatiliyor fakat “referans” obje icindeki objelere, garbage collection detaylarina, isletim sisteminin sundugu dis kaynaklara filan deginmek icin kullaniliyor, referans tiplerine dogrudan bir deginme yok. Burada da degisken dedigimiz seylerin objelere verilmis isimler oldugunu ogreniyoruz mesela.

Ilk linkteki okumanin sonucu (bu konu kafani kurcaliyorsa okumani tavsiye ediyorum, ve sadece Python ogrenmek icin degil) Python’daki tum degiskenlerin klasik anlamda referans tipi oldugu. (Ama Python’da “referans” veya “deger” tipi diye bir sey olmadigi. Objeler, turleri, degerleri, id’leri, ve implementasyon CPython ise hafiza adresleri oldugu.)

3 Beğeni

Teşekkür ederim. Yaptığım örneklerin arasındaki farkı anladım. Fakat şu halde değer tipler ile referans tipler arasında uygulamada bir farktan söz edebilir miyiz? Bu farkı ortaya koyan bir kod örneği yazmak mümkün mü?

Tabi:

#include <iostream>

int main()
{
    int x = 1;

    std::cout << x << std::endl; // 1

    int val_x = x;
    val_x = 2;

    std::cout << x << std::endl; // 1

    int& ref_x = x;
    ref_x = 42;

    std::cout << x << std::endl; // 42
}
1 Beğeni

C’de dolayli:

#include <stdio.h>

int main(void)
{
    int x = 1;

    printf("%d\n", x); // 1

    int val_x = x;
    val_x = 2;

    printf("%d\n", x); // 1

    int *ref_x = &x;
    *ref_x = 42;

    printf("%d\n", x); // 42

    printf("%p\n", ref_x); // != 42, == &x
    printf("%d\n", *ref_x); // 42

    return 0;
}

Dolayli, cunku x'e esdeger olan sey ref_x degil, *ref_x.

1 Beğeni

Çok teşekkür ederim. Ayrıca özür dilerim, birkaç gündür cevabınıza bakamamıştım. *ref_x'in başındaki * ve &x'in başındaki & işaretlerinin ne olduklarını bilmediğim için pek anlayamadım.

Deger tipleri ile referans tipleri arasindaki farkin uygulamasini soruyordun, degil mi? O kismin gayet acik olmasi lazim: Iki ornekte de, val_x’i degistirmek x’i degistirmezken ref_x’i degistirmek x’i degistiriyor cunku val_x’ler deger turlerinde ve ref_x’ler referans turlerinde.

Gerisi C++ ve C dillerinin detaylari, dilleri bilmeyen birinin anlamasi beklenemez zaten.

(Aklinda spesifik bir dil var miydi?)

1 Beğeni

Bu arada tanim esnasinda * pointer (dinamik referans (hedefi degisebilen)) ve & reference (statik referans (hedefi tanim esnasinda, bir kere belirlenen)) turleri tanimliyor.

Kullanim esnasinda * operatoru pointer'i refere ettigi objeye donusturuyor. & operatoru bir objeyi onu refere eden pointer'a donusturuyor.

1 Beğeni

Daha önceki cevabınızdan anladığım kadarıyla Python’da bunu göstermek mümkün değil. Aşina olduğum diğer dil C#.

class Program
{
    struct FooStruct { public int x; };
    class  FooClass  { public int x; };

    static void Main(string[] args)
    {
        {
            FooStruct x = new FooStruct { x = 1 };
            System.Console.WriteLine("{0}", x.x); // 1

            FooStruct val_x = x;
            val_x.x = 2;
            System.Console.WriteLine("{0}", x.x); // 1
        }

        {
            FooClass x = new FooClass { x = 1 };
            System.Console.WriteLine("{0}", x.x); // 1

            FooClass ref_x = x;
            ref_x.x = 42;
            System.Console.WriteLine("{0}", x.x); // 42
        }
    }
}
1 Beğeni