Article View: pl.comp.objects
Article #15755Re: pl.comp.objects FAQ (Frequently Asked Questions and more)
From: Sektor van Skijlen
Date: Tue, 20 Mar 2007 18:03
Date: Tue, 20 Mar 2007 18:03
352 lines
17093 bytes
17093 bytes
Dnia Mon, 19 Mar 2007 02:18:29 +0100, SasQ skrobie:
> > 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 :)
No, mniej więcej o to chodzi.
Oczywiście i tak w efekcie wszystko można po części sprowadzić do absurdu, ale
jedna rzecz i tak zawsze będzie zachowana: obiekty typów wartościowych zawsze
mają swoją własną kopię zawartości.
> if (moj.zawartosc == twoj.zawartosc) ...
> > (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 :|
Jaki jest sens sprawdzania, czy dwa portfele zawierają tyle samo pieniędzy?
Można, tylko po co? Jeśli już, to chcemy sprawdzić, czy np. w portfelu jest
"wystarczająca ilość" pieniędzy.
Możemy nawet na upartego zrobić coś takiego (opiszę to już bardziej
abstrakcyjnie): chcemy "zbalansować" jakieś stany z dwóch obiektów, pod
warunkiem, że opisująone jakąś ilość. Chcemy więc sprawdzić, czy któryś z nich
ma wartość większą niż drugi. Ale robimy to w tym celu, że chcemy wykonać
równomierne rozłożenie wartości w obu obiektach, więc i tak zamiast robić
jakieś porównania, to odejmujemy wartości od siebie.
W większości przypadków zaś, sprawdzenie stanu polega na porównaniu go z jakąś
istniejącą wartością, może stałą, może też z innym stanem, który wyznacza,
powiedzmy, wymagany "poziom" stanów - chociaż też tam zwykle się używa
operatora <, a nie ==.
Ja wiem, że teoretycznie może taka sytuacja istnieć, ale skoro jest ona taka
unikalna, to nie ma co mówić o typowości takiego porównania - jest ono właśnie
czymś bardzo nietypowym.
> > 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?
I tak zwykle tak właśnie jest. I nawet opłaca się takie coś opakować w
strukturę z bardzo istotnego powodu: w ten sposób można dostosować do takiego
konta różne inne elementy definicji typu. W przeciwnym razie za każdym razem
trzeba pamiętać, żeby operację wykonać poprawnie.
> > 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.
Ciepło, ale to stwierdzenie jest nieprecyzyjne. Bo co uważamy za typ
podstawowy?
> Hmm... a jak to jest z referencjami i wskaźnikami?
Referencje - jeśli chodzi o C++ - to jest akurat taki dość wyjątkowy typ. W
zasadzie to nawet nie powinien być uważany za oddzielny typ. Jest to
podstawowa forma, w jakiej istnieje każdy obiekt określany symbolem i oznacza
coś, co ma swoje odwzorowanie w pamięci. Wskaźnik na to coś wskazuje.
I wskaźnik już jest typem wartościowym.
> 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.
No właśnie... widzisz, chodzi tutaj o to, że string w sposób naturalny jest
typem wartościowym i jak się porównuje stringi, to nigdy nikomu nie chodzi o
porównanie ich tożsamości.
Tu problem polega na tym, że w C nie myślano o specjalizowanym zastosowaniu
operatora ==, a tekst w cudzysłowiu jest tylko uproszczonym zapisem tablicy z
zerem na końcu (a po nim przejął to tak C++).
> > 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?
No, gdyby portfel z tą samą ilością pieniędzy był zawsze tym samym
portfelem... tyle że wtedy portfel byłby powiązany z wartością.
W Smalltalku na przykład istnieje coś takiego jak "symbol", który jest
specjalną formą napisu. Niby to samo, ale różni się tym, że każdy symbol jest
unikalny. To oznacza, że jak napiszesz nazwę symbolu, który już istnieje, to
ten istniejący zostanie znaleziony i zwrócony. W takim przypadku nie ma w
całym systemie dwóch obiektów typu symbol o tej samej zawartości. W takiej
sytuacji - zauważ - porównanie tożsamości daje zawsze ten sam wynik, co
porównanie zawartości.
Ale ogólnie jest to podyktowane odpowiednimi właściwościami, szczególnie
pojęć, jakie tworzysz w projekcie programu.
Podawałem przykład "salda" i "koloru" jako typów wartościowych. Chodzi o
podobieństwo do tych typów, i co np. jest dla ciebie tym samym. Co to znaczy,
że "kolor jest ten sam", co znaczy "saldo jest to samo", a co znaczy z kolei
"portfel jest ten sam"?
A jeśli i tak nie wiesz, to najprościej stwierdzić to w ten sposób:
Utwórz a
b = a
Zmodyfikuj b
Jeśli spodziewasz się, że po takim czymś 'a' powinno zostać zmodyfikowane, to
jest to typ obiektowy.
Typy obiektowe poza tym często posiadają powiązania. Powiązania te często
powodują nawet, że nie da się ich klonować. Typy wartościowe nigdy nie
posiadają powiązań.
> >> 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."
Względem języka tak. Ale fizycznie one nie muszą istnieć.
> A teraz mi tu siejesz trwogę, że są "gołymi" wartościami
> nie trzymanymi nigdzie? :P
W praktyce tak :)
Nie no, formalnie to one oczywiście są obiektami stałymi, a także jest prawdą,
że do przechowywania wartości jakiś obiekt musi być. Ale ten obiekt jest
traktowany wyłącznie instrumeentalnie.
> 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
No są. W programie ;)
Tzn. dokładnie to jest tak: formalnie, to są obiekty stałe. Tzn. jeszcze
inaczej: to MOGĄ BYĆ obiekty stałe. Problem po prostu polega na tym, że co
prawda C++ pozwala ci zrobić tak:
const int& a = 5, & b = 5;
A nawet tak:
if ( &a == &b )
Ale to porównanie nie zwróci ci żadnej sensownej informacji. Może ci zwrócić
true, może ci zwrócić false. No dobrze, jeśli tym stałym nadasz różne
wartości, to wtedy powinno ci zawsze zwrócić false ;)
Literał zapisany w języku sam z siebie jest jedynie wartością. Dopiero gdy
zostanie użyty, może się z niego zrobić obiekt - ale dopiero gdy zostanie
użyty jako obiekt i dopiero w miejscu, w którym będzie użyty jako obiekt tym
obiektem będzie. Wcześniej jest tylko zapisem w programie.
> >> Jeśli chodzi ci o tożsamość, to już trzeba by zapisać to
> >> np. tak:
> >>
> >> if (&x == &y) ...
> >>
> >> i wtedy już wiadomo, czy "x jest y" ;)
> >
> > I w których operacjach wynik tego porównania ma znaczenie,
> > a w których nie ma?
> Nie rozumiem pytania :|
Pytanie proste: co możesz umieścić za tym 'if', żeby twój program miał sens?
> > 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.
No ale po co?
Właśnie na tym polega istota typów wartościowych: informacja, jaką one niosą,
to jest wyłącznie wartość. Obiekty, które są typów wartościowych, mogą zatem
pojawiać się i znikać co chwilę, kompilator w zależności od opcji
optymalizacji może tych obiektów tworzyć więcej, lub mniej i tak dalej. Po
prostu te obiekty są jedynie użyte instrumentalnie - ich tożsamość nie powinna
cię interesować; te obiekty kompilator niech sobie tworzy, jak mu się podoba,
a dla ciebie istotna powinna być tylko wartość.
> > 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...
Tak. Javowy String ;)
> 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.
Tak, ale to nie jest jeszcze takie proste.
Tak, jak mówiłem o Smalltalkowych symbolach: możesz stworzyć grupę obiektów,
która będzie symbolicznie oznaczać określone wartości.
W twoim przypadku najlepiej by było np. stworzyć listę tych wartości i mieć je
jako "stałe". Podobnie jak stałe enum. Zaznaczasz przy tym, że:
- tylko twoja klasa ma możliwość tworzenia takich obiektów. Użytkownikowi
wolno jedynie korzystać z już gotowych
- każdy obiekt typu TrybGraficzny jest unikalny
I w takim przypadku, oczywiście, najsensowniej jest, jeśli porównujesz te
obiekty po tożsamości. Tyle że skoro obiekty są unikalne, to i tak da ono ten
sam wynik, co porównanie zawartości. Ale fakt, że zoptymalizowałeś to na
porównanie tożsamości, to tylko kwestia implementacji.
Jeśli ilość trybów jest dynamiczna, to wtedy musisz zorganizować jakieś
mapowanie na unikalne wartości (w sensie, na coś, co jest łatwiej porównać).
> 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, jak najbardziej. W takim przypadku po prostu przygotowujesz zestaw
wszystkich możliwych wartości na początku, a potem posługujesz się do nich
wskaźnikiem jako symbolem określonego trybu.
Zauważ zresztą, co dla ciebie w praktyce oznacza stwierdzenie, że "tryb
graficzny x to ten sam tryb co y".
To nie znaczy, że "x i y" mają tą samą rozdzielczość pionową, tę samą
rozdzielczość poziomą i tę samą głębokość kolorów".
To znaczy, że x np. oznacza tryb graficzny numer 4, a y też tryb graficzny
numer 4. A tryb graficzny numer 4 to dopiero ma... itd.
Zauważ też, że nie wykonujesz żadnych operacji na obiektach typu
TrybGraficzny, które mogłyby wyprodukować nowe obiekty typu TrybGraficzny.
Gddyby takie były, to oczywiście też musiałyby być wykonane tak, żeby w wyniku
dostać inny obiekt typu TrybGraficzny.
> > 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 ;)
C++, na przykład. Nie mówię, że w ogóle tylko C++, bo istnieje jeszcze parę
innych, których to dotyczy.
W D jest to może nawet odrobinę lepiej zrobione (chodzi mi o "auto class").
> > 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?
Widzisz, problem polega na tym, że rozróżnienie między kopiowaniem płytkim, a
głębokim jest konieczne, bo czasem potrzebne jest nam współdzielenie
wewnętrznych danych (w tym modyfikowalnych), a czasem chcemy mieć własną
kopię. Kopiowanie obiektów wartościowych odpowiada kopiowaniu głębokiemu. Z tą
tylko różnicą, że w zależności od możliwości języka możemy to zorganizować
przez copy-on-write (czyli kopiowanie jest efektywnie płytkie, ale tylko do
pierwszej modyfikacji).
Pojęcia kopiowania płytkiego i głębokiego są pojęciami niskiego poziomu i ich
używanie jest konieczne, jeśli język nie dostarcza żadnych bardziej ogólnych
narzędzi pozwalających na zrobienie takiego typu, czy też najprościej mówiąc,
programista uważa, że tak mu lepiej.
> > 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...
W javie przede wszystkim tej trzeciej instrukcji nie skompilujesz.
Na tym właśnie polega myk ze zorganizowaniem typu wartościowego przez
niemodyfikowalny typ obiektowy.
--
// _ ___ 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:
<etp7ku$kqo$1@kujawiak.man.lodz.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!newsfeed.pionier.net.pl!news.man.lodz.pl!not-for-mailReferences:
<pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.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!newsfeed.gazeta.pl!newsfeed.pionier.net.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: Tue, 20 Mar 2007 18:03:10 +0000 (UTC)
Organization: LODMAN - Metropolitan Area Network in LODZ, Poland
Lines: 352
Sender: Michal Malecki <sektor@zly.kis.p.lodz.pl>
Message-ID: <etp7ku$kqo$1@kujawiak.man.lodz.pl>
References: <pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.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: zly.kis.p.lodz.pl
Mime-Version: 1.0
X-Trace: kujawiak.man.lodz.pl 1174413790 21336 212.191.89.2 (20 Mar 2007 18:03:10 GMT)
X-Complaints-To: abuse@man.lodz.pl
NNTP-Posting-Date: Tue, 20 Mar 2007 18:03:10 +0000 (UTC)
User-Agent: tin/1.6.2-20030910 ("Pabbay") (UNIX) (SunOS/5.9 (sun4u))
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit