Article View: pl.comp.objects
Article #15757Re: pl.comp.objects FAQ (Frequently Asked Questions and more)
From: Sektor van Skijlen
Date: Wed, 16 Jan 2008 23:57
Date: Wed, 16 Jan 2008 23:57
386 lines
18997 bytes
18997 bytes
Jasna dupa!
Prawie rok upłynął od tej korespondencji, a tu nagle odpowiedź! Dosłownie
jakbym znalazł paczkę cukierków miętowych za szafą podczas przedświątecznego
sprzątania :)
Ale ok - mogę to pociągnąć :)
Dnia Wed, 16 Jan 2008 01:45:20 +0100, SasQ skrobie:
> Dnia Sun, 18 Mar 2007 23:11:57 +0000, Sektor van Skijlen napisał(a):
> >>> Typ wartościowy jest typem, którego obiekt jest identyfikowany
> >>> zawsze przez wartość. Więc jeśli portfel z tą samą ilością
> >>> pieniędzy nie jest tym samym portfelem, to portfel nie jest
> >>> typem wartościowym.
> >
> >> Hmm... ale chyba jest dobrym przykładem, że czasami chcemy go
> >> potraktować jako typ wartościowy, a czasami jako niewartościowy:
> >
> >> if (portfel1==portfel2) //Czy mają tyle samo pieniędzy?
> >> if (portfel1 IS portfel2) //Czy to ten sam portfel, czy inny?
> >
> > Nie.
> >
> > if ( portfel1->ilosc_pieniedzy()
> > == portfel2->ilosc_pieniedzy() ) // czy mają tyle samo
> > // pieniędzy
> >
> > Nie porównujesz "dwóch portfeli", tylko porównujesz stan dwóch
> > portfeli
> Aaa no to już chyba zaczynam qmać, co masz na myśli :)
> Jeśli mamy sobie takie coś:
> int x = 2;
> int y = 2;
> i teraz porównujemy je tak:
> if (x == y) ...
> to obchodzi nas wyłącznie fakt, że wartości obiektów są takie same.
Znów nie do końca zrozumiałeś. Przy takim podejściu to się możemy bujać z tym
w nieskończoność.
Oczywiście, to co napisałeś powyżej, jest prawdą, jak najbardziej, ale po
prostu zaczynasz iść z rozumowaniem wciąż w przepaść, tylko po przeciwnej
stronie.
> Nie obchodzi nas, że są to dwa różne obiekty przechowujące tą samą
> wartość. Równość wartości traktujemy jako równość obiektów, a
> wartość utożsamiamy z samym obiektem. O to chodzi? :>
Nie. Nie ma czegoś takiego, jak "równość obiektów".
Jeśli przyjmiemy określenie, że obiekt może być obiektem wartościowym, to
wtedy stwierdzenie "a jest b" określa nam, że a i b posiadają te same
wartości. Tylko dlatego, że istnieje pojęcie wartości dla takich obiektów. I
dlatego właśnie utożsamiamy dane "a" z wartością, jaką obiekt referowany przez
"a" ("referować" można również przez samą nazwę, tak to nazwijmy) ma aktualnie
zapisaną w swojej wewnętrznej reprezentacji. Równie dobrze jednak "a" może
referować do stałej i wtedy symbolicznie utożsamia się to z samą wartością.
Nadal jednak "a jest b" będzie wyrażać to samo.
Cechą obiektów wartościowych jest identyfikacja po wartości, to znaczy, reguły
opisujące cokolwiek z użyciem a będą zawsze identyczne, gdy a zastąpisz b, pod
warunkiem, że a == b.
> Kolor jest typem wartościowym, bo jeśli mamy:
> Kolor pierwszy = czerwony;
> Kolor drugi = czerwony;
> i porównamy je:
> if (pierwszy == drugi) ...
> to dla nas będzie to "ten sam kolor" [mimo że leży w różnych
> miejscach].
Zgadza się - a jeśli użyjesz gdzieś koloru pierwszy, to równie dobrze możesz
użyć koloru drugi i efekt będzie zawsze ten sam.
> Przy takim postawieniu sprawy rzeczywiście portfel nie jest
> typem wartościowym, bo gdy zrobimy tak:
> Portfel moj = 500; //w moim portfelu śpi 500 zł
> Portfel twoj = 500; //w twoim portfelu śpi 500 zł
> to kiedy je porównujemy, domyślne rozumienie tego porównania
> jest inne:
> if (moj == twoj) ...
> bo tym razem wygląda to jak sprawdzenie, czy to są dwa różne
> portfele, czy może ten sam, i nie obchodzi nas co jest w środku i
> że oboje mamy tyle samo pieniędzy w swoich portfelach.
> Tutaj zawartość portfela nie jest utożsamiana z nim samym.
> Jeśli byśmy chcieli porównywać zawartość portfeli, to musimy
> to jasno powiedzieć:
> if (moj.zawartosc == twoj.zawartosc) ...
No właśnie.
Wszystko ma mocny związek z tym, co dla ciebie oznacza stwierdzenie "a jest
b".
> > (pomijając już bezsens porównywania dwóch stanów z dwóch
> > różnych obiektów, ale to tak na marginesie).
> A tego to nie skumałem :|
Nie no jakieś tam zastosowania do tego można by znaleźć - np. jeśli jeden z
obiektów "osiągnął" jakąś-tam wartość stanu, którą osiągnął również inny
obiekt, to coś tam szczególnego można zrobić... po prostu rzadko tego typu
porównania mają jakikolwiek sens. Zwykle porównuje się to z jakąś stałą, aby
stwierdzić jakieś warunki nt. tego stanu. Np. jaki ma sens porównanie
zawartości jednego i drugiego portfela? Można co najwyżej sprawdzić, czy jest
w nim "wystarczająco pieniędzy" na jakąś transakcję, ewentualnie można na tych
wartościach wykonywać jakieś operacje, np. zsumować zawartość dwóch portfeli.
Ale stwierdzanie, czy w jednym portfelu jest tyle samo pieniędzy, co w drugim,
ma akurat mało sensu.
> > if ( portfel1 == portfel2 ) // czy to ten sam portfel
> No spox, teraz wchodzi do garka ;)
> > Ja wiem, że jak komuś odbija kolba i musi sobie nawymyślać
> > wypasione definicje operatorów (w tym ==), to nic się na to
> > nie poradzi, ale to bynajmniej nie powoduje, że portfel
> > nagle staje się typem wartościowym.
> Hmm... czyli sugerujesz, że w przypadku portfela robienie
> z niego typu wartościowego jest jakimś kaskaderstwem i
> lepiej użyć semantyki, w której jego zawartość jest osobnym
> obiektem składowym?
Tak, dokładnie tak. Niekoniecznie zresztą jako obiekt składowy, zwykle robi
się do tego odpowiednie metody, które tą wartością operują. Ale ilość
pieniędzy jest akurat tylko jednym z istniejących stanów portfela.
> > Powtarzam: typ jest wartościowy wtedy (i tylko wtedy), gdy
> > tożsamość poznajemy po jego wartości.
> No czyli chyba każdy typ podstawowy taki jest.
Niekoniecznie jest to prawda we wszystkich językach, ale w wielu tak.
> Hmm... a jak to jest z referencjami i wskaźnikami?
To też wartości. Jeśli chodzi o referencję w C++ to jest to zrobione trochę
bardziej jak trik językowy, konkretnie nie ma żadnego dostępu do wartości w
przypadku referncji.
Wskaźnik jest wartością. To, na co wskaźnik wskazuje, może być obiektem (lub
funkcją).
> Albo tablice znakowe - ostatnio na pl.comp.lang.c był taki
> przypadek, że koleś porównywał wskaźniki do takiego samego
> [ale nie tego samego] ciągu znaków. Coś w stylu:
> char* str1 = "test";
> ...
> if (str1 == "test") ...
> i się dziwił, czemu ten warunek mu się nie spełnia, skoro
> "stringi są te same" ;J Może to jest pole dla twojej
> teorii? :> Bo tutaj ewidentnie ktoś miał na myśli porównanie
> wartości, a wyszło mu porównanie tożsamości i to go
> wpuściło w maliny.
Ale to nie jest kwestia tego, że == porównuje wartości i co z nimi.
To, co w tym porównaniu zostało porównane to wskaźniki na tablice znakowe
służące do przechowania wartości stringów, a nie "wartości stringów".
char* to nie jest "string". String to może być np. std::string.
> > Jeśli więc ilość pieniędzy w portfelu nie stanowi o tym,
> > że to ten sam portfel, to nie jest to typ wartościowy.
> Czyli to jest wybór arbitralny, czy jednak podyktowany
> jakimiś właściwościami?
Czy jesteś w stanie stwierdzić, że istnieje coś takiego jak "wartość" w
przypadku portfela? Czyli coś takiego, co potrafi portfel jednoznacznie
określić.
> >> No cóż, ale C++ już taki dziwny jest, że nie ma "gołych"
> >> wartości [a przynajmniej nic mi o tym nie wiadomo], tylko
> >> wszystko jest trzymane w jakichś obiektach ;)
> >
> > A takie coś jak np. 119 to jest gdzie trzymane twoim zdaniem?
> To się zdecyduj. Niedawno jak pytałem czy stałe dosłowne są
> obiektami stałymi, czy jedynie wartościami "nie opakowanymi" w
> żadnym obiekcie, to mi odpowiedziałeś:
> "Są obiektami stałymi. Świadczy o tym fakt, że można nimi
> inicjalizować stałe referencje."
> A teraz mi tu siejesz trwogę, że są "gołymi" wartościami
> nie trzymanymi nigdzie? :P
Same wartości, owszem, nie są trzymane nigdzie. Mogą być w określonych
warunkach stałymi obiektami, ale stałe obiekty typów wartościowych można
utożsamiać z samymi wartościami.
> Jakby tego było mało, to mi jeszcze później napisałeś, że
> "Wszelkie inne stałe dosłowne nie leżą nigdzie w pamięci"
> No to nie rozumiem: obiekty których nigdzie nie ma? :P
Powiedzmy to w ten sposób: istnieje stały obiekt, jeśli posiada jakąś nadaną
nazwę w systemie nazw, lokalizację, gdzie mogą się znajdować (bo mogą się
znajdować np. wewnątrz innego, niekoniecznie stałego obiektu). Samo 119 jest
tylko symbolicznym określeniem wartości, co nie znaczy, że nie można
"sztucznie" z tego zrobić obiektu stałego, przypisując do stałej referencji.
> > Najprościej to jest mniej więcej wytłumaczyć tak:
> >
> > Typ wartościowy jest identyfikowany przez wartość. To oznacza,
> > że w przypadku typów wartościowych porównywanie tożsamości
> > obiektów typów wartościowych (o ile w ogóle jest dostępne w
> > języku - w Javie np. nie jest dostępne) nie daje żadnej istotnej
> > informacji, tzn. takiej, na której można w czymś polegać w
> > programie
> No tak, w sumie masz rację. Ale jest jedno ale: zarządzanie
> takimi obiektami wartościowymi w pamięci. Użytkownik takich
> obiektów może je sobie traktować jak wartościowe, przekazywać
> go mając na myśli jego wartość, porównywać go mając na myśli
> porównanie wartości, ale wciąż będą pewne części programu,
> w których chciałby wiedzieć, w jakim miejscu pamięci leży
> ten obiekt, albo czy dwa obiekty leżą w różnych miejscach
> pamięci, czy może jest to to samo miejsce pamięci.
Teoretycznie jest to prawda, ale do czego miałoby to służyć?
Sensownym przykładem niezwykłego typu wartościowego jest string. Nikt nie
zaprzeczy, że jest to wartość - identyfikacja tego, czy jest to "ten sam
string" polega na sprawdzeniu, czy składa się z tych samych znaków. Natomiast
co do miejsca w pamięci - cóż, w przypadku stringa z gcc w "pamięci" siedzi
dokładnie jeden wskaźnik na "jakąś pamięć" (w której upchnięta jest tablica
znaków i dodatkowe dane). Ta pamięć jest w zupełnie innym miejscu, więc jakie
ma znaczenie fakt, gdzie akurat siedzi ten jeden wskaźnik do tej pamięci?
> > W językach, w których wszystko jest obiektem, typy wartościowe
> > są symulowane poprzez niemodyfikowalne obiekty. To znaczy,
> > każdy obiekt typu wartościowego musi zostać za każdym razem
> > wyprodukowany na nowo.
> Hmm... coś mi to przypomina...
> Czy obiekt klasy TrybGraficzny mógłby być typem wartościowym?
> Chodzi mi o taki obiekt przechowujący informacje o parametrach
> trybu graficznego. Coś mi się zdaje, że tak, bo tutaj:
> TrybGraficzny pierwszy = { 800, 600, 16 };
> TrybGraficzny drugi = { 800, 600, 16 };
> ...
> if (pierwszy==drugi) ...
> porównanie oznacza porównanie wartości, a nie tożsamości.
Oczywiście, jak najbardziej, ale nie tak całkiem prosto.
Tryb graficzny to typowy przykład typu o ograniczonym zestawie wartości
(teoretycznie każdy typ wartościowy ma ograniczony zestaw wartości, ale w
przypadku takich np. BigInteger czy zwykłego float to się nie sprawdza :).
Powinno się mieć do tego celu:
- zestaw wartości szybkich w obsłudze (np. liczb całkowitych), które
przyporządkowuje się danemu trybowi
- tablicę, gdzie dla każdego z tych trybów przechowuje się jego
charakterystyki
I teoretycznie mógłby to być nawet typ o zmiennej liście dopuszczalnych
wartości. Co oznacza, że dysponujesz na starcie PREDEFINIOWANYM zestawem
trybów, który jest zebrany w określonej tablicy, a zmienna przechowuje tylko
liczbę całkowitą określającą jego identyfikator.
W takim przypadku pierwszy == drugi wewnętrznie porównuje oczywiście liczby
całkowite, ale logicznie chodzi tutaj o to, że porównuje się, czy tryb
"pierwszy" to ten sam tryb, co "drugi". Jest tak, jeśli oba przechowują
identyfikator tego samego trybu, czyli ten sam identyfikator.
> W moim kodzie chciałem dodatkowo zagwarantować stałość
> takich obiektów, tzn. żeby były właśnie niemodyfikowalne.
> Raz utworzone z pewnymi wartościami mają je już do końca
> życia [bo parametry tego samego trybu graficznego nigdy
> się nie zmieniają]. Są one produkowane raz, przez obiekt
> wyciągający je z karty graficznej, i później już się
> nie zmieniają. Mogą być jedynie kopiowane, ale wykonanie
> kopii sprawia, że mamy wciąż "ten sam" tryb graficzny,
> tylko przechowywany w dwóch miejscach ;J
> [to miało zagwarantować, że nikt niepowołany nie stworzy
> sobie własnego obiektu klasy TrybGraficzny z dowolnie
> ustawionymi, nieprawidłowymi wartościami].
> Czy ten mój TrybGraficzny nie jest przypadkiem idealnym
> przykładem typu wartościowego? :J [o ile dobrze to zrozumiałem]
Tak, ale właśnie dlatego najlepiej zrealizować go w ten sposób, żeby móc
dysponować predefiniowanym zestawem wartości, którymi - dla wygody - operujesz
"behind the scene" odwołując się po identyfikatorach do określonych
"wartości".
> > W językach, które naturalnie wspierają typy wartościowe
> O jakich językach mowa? Wolałbym to wiedzieć, bo wtedy
> mógłbym sobie popatrzeć jak tam to jest zrobione i może
> ułatwiłoby mi to zrozumienie sprawy na przykładach ;)
Na przykład C++ i C#. Java też niby wspiera, ale tylko swoje własne wbudowane.
W przypadku C# zresztą nie pamiętam jak dokładnie jest z porównywaniem typów
struct, więc też nie wiem na ile to wsparcie jest naturalne.
C++, w odróżnieniu od wielu innych języków (co jest zresztą cechą
odziedziczoną z C) postawił na nierozróżnianie typów wartościowych i
niewartościowych, pozostawiając to użytkownikowi. Konsekwencją tego jednak
jest konieczność stosowania * i & w przypadku typów niewartościowych. Różni go
to od C# i Javy, gdzie postawiono na rozróżnianie tych typów, niestety
paskudną konsekwencją w przypadku Javy (przykładowo) jest fakt, że typów
wartościowych nie można definiować, a nadomiar złego typ w naturalnie sposób
wartościowy String jest w Javie zrobiony jako niewartościowy - przez co Java
jest do dziś chyba jedynym językiem, w którym jedynym bezpiecznym sposobem
sprawdzenia, czy string s jest niepusty, jest:
if ( !"".equals( s ) )
co w każdym normalnym języku (Qrczak by dodał "oraz C++" :) pisze się:
if ( s != "" )
A dodatkowo Java i C# dołączyły radośnie do języka C w gronie języków, które
rozróżniają "null string" i "pusty string". Ja swojego współpracownika w
projekcie (lepiej się znającego na Javie) za nic tak nie goniłem, jak za
dopuszczanie do powstawania "null string".
> > Oczywiście teorię typów wartościowych i niewartościowych [...]
> > można olać, jeśli ktoś uważa, że mu nie pasuje. Tylko że
> > alternatywą jest stwierdzenie, że mamy kilka różnych definicji
> > porównania, kopiowanie płytkie i głębokie itd.
> Mógłbyś uściślić na czym polega związek typów wartościowych
> z płytkim/głębokim kopiowaniem?
Związek jest dokładnie żaden.
Albo stosujemy teorię typów wartościowych i niewartościowych (i wtedy mówimy o
kopiowaniu wartości, w tym wartości tych typów lub wartości
referencji/wskaźnika, a w przypadku typów niewartościowych co najwyżej o
klonowaniu, które dodatkowo nie musi być operacją dostępną dla każdego typu
lub niekoniecznie tworzyć faktycznie wierną kopię swojego wzorca), albo
twierdzimy, że to nam jest niepotrzebne i wtedy męczymy się z pojęciami
płytkiego i głębokiego kopiowania i uważamy, żeby kopiować tam, a nie kopiować
tu itd.
> > Nawiasem mówiąc, w Rubym spierdzielili sprawę ze stringami
> > totalnie. String w ogólności jest typem wartościowym - niestety
> > w Rubym jest on ehm... takim dziwnie-hybrydowym. To znaczy,
> > modyfikacje przenoszą się na inne obiekty oznaczane
> > identyfikatorami:
> >
> > a = "dupa"
> > b = a
> > b[0] = "p"
> > print a
> A czy w Javie przypadkiem nie jest tak samo?
> No chyba że tam się wtedy robi kopia przy modyfikacji przez b.
> Już nie pamiętam...
To zależy.
W Javie przytomnie zdecydowano, że String jest typem stałym (nie można
modyfikować tego obiektu w miejscu - obiekt typu string posiada swoją tablicę
znaków nadaną w momencie tworzenia once forever). Natomiast StringBuffer i
StringBuilder jak najbardziej (nazwy tych typów zasługują na osobny wątek: oba
robią dokładnie to samo i mają nawet chyba taki sam interfejs, a różnią się
tylko tym, że w jednym z nich operacje są synchronizowane: który to z nich,
kto potrafi odpowiedzieć z pamięci?).
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethouris(O)gmail.com>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"Java is answer for a question that has never been stated"
Technical Details
Message-ID:
<fmm5kd$nt4$1@kujawiak.man.lodz.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.rmf.pl!agh.edu.pl!news.agh.edu.pl!newsfeed.silweb.pl!news.man.lodz.pl!not-for-mailReferences:
<pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.pl> <esfchn$t5r$1@kujawiak.man.lodz.pl> <esgboi$8i2$2@bandai.magma-net.pl> <esi2kk$skh$2@kujawiak.man.lodz.pl> <esi6uk$4mt$1@bandai.magma-net.pl> <esmu4p$ii1$2@kujawiak.man.lodz.pl> <esn1nl$7q7$1@bandai.magma-net.pl> <esv08v$o5d$1@kujawiak.man.lodz.pl> <et6pse$eab$1@bandai.magma-net.pl> <et7emm$cf1$1@kujawiak.man.lodz.pl> <etak85$krq$1@bandai.magma-net.pl> <eteh83$he8$1@kujawiak.man.lodz.pl> <pan.2007.03.16.21.49.44.873488@go2.pl> <etkgvt$2b$1@kujawiak.man.lodz.pl> <pan.2007.03.19.01.18.29.79961@go2.pl>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.rmf.pl!agh.edu.pl!news.agh.edu.pl!newsfeed.silweb.pl!news.man.lodz.pl!not-for-mail
From: Sektor van Skijlen <ethouris@guess.if.gmail.com.is.valid.or.invalid>
Newsgroups: pl.comp.objects
Subject: Re: pl.comp.objects FAQ (Frequently Asked Questions and more)
Date: Wed, 16 Jan 2008 23:57:01 +0000 (UTC)
Organization: LODMAN - Metropolitan Area Network in LODZ, Poland
Lines: 386
Sender: Michal Malecki <sektor@student.kis.p.lodz.pl>
Message-ID: <fmm5kd$nt4$1@kujawiak.man.lodz.pl>
References: <pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.pl> <esfchn$t5r$1@kujawiak.man.lodz.pl> <esgboi$8i2$2@bandai.magma-net.pl> <esi2kk$skh$2@kujawiak.man.lodz.pl> <esi6uk$4mt$1@bandai.magma-net.pl> <esmu4p$ii1$2@kujawiak.man.lodz.pl> <esn1nl$7q7$1@bandai.magma-net.pl> <esv08v$o5d$1@kujawiak.man.lodz.pl> <et6pse$eab$1@bandai.magma-net.pl> <et7emm$cf1$1@kujawiak.man.lodz.pl> <etak85$krq$1@bandai.magma-net.pl> <eteh83$he8$1@kujawiak.man.lodz.pl> <pan.2007.03.16.21.49.44.873488@go2.pl> <etkgvt$2b$1@kujawiak.man.lodz.pl> <pan.2007.03.19.01.18.29.79961@go2.pl>
NNTP-Posting-Host: student.kis.p.lodz.pl
Mime-Version: 1.0
X-Trace: kujawiak.man.lodz.pl 1200527821 24484 212.191.89.21 (16 Jan 2008 23:57:01 GMT)
X-Complaints-To: abuse@man.lodz.pl
NNTP-Posting-Date: Wed, 16 Jan 2008 23:57:01 +0000 (UTC)
User-Agent: tin/1.8.3-20070201 ("Scotasay") (UNIX) (FreeBSD/6.2-RELEASE-p3 (sparc64))
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit