🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.objects
Article #15729

Re: Wartości i obiekty

#15729
From: Sektor van Skijlen
Date: Mon, 12 Mar 2007 21:28
251 lines
14011 bytes
Dnia Sun, 11 Mar 2007 23:15:23 +0100, Marcin 'Qrczak' Kowalczyk skrobie:
> Dnia 11-03-2007, nie o godzinie 15:29 +0000, Sektor van Skijlen
> napisał(a):

> > Obojętnie, jak mamy zaimplementowany sposób porównania wartości i tożsamości,
> > obojętnie też, czy typ jest wartościowy, czy obiektowy, dla każdego typu
> > istnieje tylko i wyłącznie jeden sensowny sposób porównania.

> To jest prawie prawda.

> Po pierwsze, co tłumaczę od paru postów, zdarza się, że typ ma
> niejednoznaczny charakter i czasem jest używany w roli typu
> wartościowego, a czasem typu z tożsamością. Zgadzam się, że to
> jest kłopotliwe, ale to bywa faktem.

A ja również tłumaczę od paru postów, że na taką okoliczność powinny zostać
stworzone osobne typy statyczne.

To prawda, że możesz używać tego samego typu języka jako logicznego typu
wartościowego i niewartościowego, tyle że to sprowadza język do
niskopoziomowego rozwiązania, w którym tego samego typu można używać do
przechowywania logicznie różnych rzeczy.

Jeśli mamy język taki, jak C, to rozumiem, że za bardzo nie mamy wyjścia.
Dlatego tam używa się tego samego typu - tablicy char - jako łańcucha
tekstowego i jako tablicy o elementach typu char.

Natomiast dla języka wysokopoziomowego to po prostu wstyd, żeby nie zrobić
tego na osobnych typach. Rozumieli to zarówno twórcy C++, jak i Javy, i C#
(nb. Tcl-a też, dlatego też tam trzeba stosować pewne myki, żeby móc
przekształcić napis na listę znaków). Najwyraźniej nie zrozumieli tego twórcy
języków Haskell i D.

Jeśli taki typ ma osobne specyfiki dla różnych przypadków, to i tak
przynajmniej jednej specyfiki zawsze trzyma się jeden obiekt (czyli nie
zmienia się nagle z typu obiektowego w wartościowy w trakcie działania). Jeśli
tak, to można użyć osobnych definicji typów dla każdego obiektu używającego
określonej specyfiki.

> Po drugie czasem (rzadko) chcemy porównać tożsamość obiektów typów
> wartościowych dla niskopoziomowych optymalizacji.

Rozumiem - ale taka niskopoziomowa optymalizacja powinna być operacją
szczególnego rodzaju, być dostępna tylko dla operacji bezpośrednio operujących
na takim typie (lub specjalnie do tego dozwolonych), a przynajmniej powinna
być wykonywalna za pomocą osobnych funkcji porównania.

Nie chodzi mi przecież o to, żeby całkowicie zabronić możliwości sprawdzania
tożsamości obiektów typów wartościowych. Chodzi o to, żeby każdy typ posiadał
operator porównania, który porównuje w sposób adekwatny do rodzaju typu.

> Po trzecie są relacje, które - jeśli się uprzeć - można nazwać inaczej
> niż równość, ale potocznie są to jakieś odmiany równości. Na przykład
> sprawdzenie, czy dwa ciągi mają równe odpowiadające sobie elementy, bez
> sprawdzenia, czy te ciągi w ogóle są tego samego typu.

Można, owszem, ale to nie jest porównanie wartości, tylko porównanie
zawartości zbiorników (operacja specyficzna dla zbiornika, bo nie da się
takiej operacji uogólnić na "po prostu obiekt"). Powinna być zatem dozwolona,
ale jako specyficzna operacja, a nie porównanie.

Dlaczego w C++ zbiorniki nie posiadają zdefiniowanego operatora porównania,
tylko tego typu porównania wykonuje się algorytmami equal i mismatch?

> Po czwarte tradycyjna równość zmiennoprzecinkowa nie jest relacją
> równoważności (z powodu NaN) ani nie jest zachowywana przez operacje
> arytmetyczne (z powodu -0.0). Jest więc powód, żeby traktować ją jako
> osobną operację niż podstawowa równość.

Nie no równość wartości zmiennoprzecinkowych to jest już osobna bajka. Jest to
drobny wyłom w systemie typów wartościowych/obiektowych. Problem jest właśnie
z tym NaN, bo gdyby nie to, to jeszcze na upartego zachowałaby się zasada, że
wartość a skopiowana z b jest równa b.

Nie wiem w tym momencie, czy typy zmiennoprzecinkowe można traktować jak
wartości. Dla mnie najsensowniejszym rozwiązaniem byłoby zdefiniowanie jako
operatora porównania porównania "wewnętrznego", czyli z zachowaniem zależności
między przypisaniem a porównaniem. Natomiast osobno do tego należałoby
stworzyć funkcję porównującą dwie liczby zmiennoprzecinkowe z zadanym
epsilonem.

Ci, co wymyślili ten numer z NaN, najwyraźniej nie zdawali sobie sprawy z
tego, jakie zamieszanie tym zrobią.

Zastanawiam się nawet, czy nie byłoby znacznie sensowniejsze uznanie liczb
zmiennoprzecinkowych za typy niewartościowe. Tożsamość przez obiekt lepiej by
się dla nich sprawdzała.

> A to jeszcze nie wszystko.

No to co jeszcze?

Przecież można by do tej wyliczanki dołożyć stringa, którego możesz porównywać
np. bez case'a, z pominięciem znaków kontrolnych itp.

Powtarzam: nie zaprzeczam, że powinny być zdefiniowane różne metody
porównywania dostępne dla danego obiektu, ale powinna być zawsze JEDNA
operacja najwłaściwszego porównania zdefiniowana dla danego typu.

> > >    w innym języku obiekt jest klonowany (czymś w rodzaju metody Clone).
> > 
> > Tak - pod warunkiem, że pozwala się na klonowanie obiektów.

> Zawsze "się pozwala", bo nie ma czego zabraniać - to jest operacja jak
> każda inna, jeśli się nie oczekuje, że będzie automatycznie dostępna
> dla dowolnego typu.

Niestety, bzdury opowiadasz. Istnieje coś takiego, jak unikalne wartości -
wartość jednego pola, która w każdym obiekcie musi być inna. Często są to
nawet obiekty generowane na rzecz innych, istniejących obiektów. Efekt jest
taki, że żebyś mógł poprawnie tworzyć obiekt przez klonowanie, to musiałbyś
podczas klonowania uwzględnić fakt, że wartości niektórych jego pól nie mogą
być skopiowane, tylko muszą zostać wygenerowane na nowo. Czy taki obiekt
będzie jeszcze klonem?

Klonowanie zresztą to nie jest taka prosta sprawa. Pamiętam, jak w mudzie była
funkcja klonująca moba. Trzeba było tam uwzględnić mnóstwo rzeczy: jeśli
postać była wierzchowcem, to nowa postać była "nieosiodłana". Jeśli postać
dosiadała wierzchowca, to nowa postać była tworzona na ziemi. Jeśli postać
zajmowała mebel, to nowa postać musiała również stać obok (tzn. tak było po
wniesieniu mojej poprawki do buga, bo poprzednio nowa postać była tworzona na
tym samym meblu - autor najwyraźniej zapomniał, że meble mają też swoją
pojemność i niekoniecznie dwie postacie będą mogły siedzieć na jednym
krześle). Kolejnym problemem jest to, czy klonowanie postaci powinno pociągać
za sobą klonowanie posiadanych przez nią przedmiotów, czy też nowa postać ma
być stworzona "na golasa" (nie pamiętam już, jak to rozwiązano). Gdyby
pociągać za sobą tworzenie przedmiotów, to dodatkowo niektóre mudy mają
wprowadzoną ekonomię - czyli takie sklonowanie pociąga za sobą dodatkowo
deflację. I tak można by wyliczać bez końca.

Co więcej, często źródłem danych dla tworzenia nowego obiektu są jakieś
specyficzne dane, które trzeba odpowiednio pobrać (być może zarejestrować,
skojarzyć z czymś). W takim przypadku nie da się stworzyć obiektu na podstawie
danych z innego obiektu, bo dane, które posiada ten hipotetyczny obiekt
źródłowy są przygotowane tylko i wyłącznie dla niego.

Jak ty sobie wyobrażasz np. sklonowanie obiektu oznaczającego plik, gniazdo,
sesję, czy zbiornik właścicielski (zbiornik z dynamicznie tworzonymi
obiektami, które zbiornik posiada na własność, tzn. usuwa je razem ze sobą)?

Więc co oznacza "zawsze się pozwala"? Nie mówiąc o tym, że w dalszej części
zdania twierdzisz, że można zabronić, więc nie rozumiem...

> > > b) Typowy przykład: liczba.
> > 
> > >    Zmienna wskazuje na niezmienialny obiekt innego języka reprezentujący
> > >    bieżący stan obiektu C++. Metodom C++, które zmieniały stan obiektu,
> > >    odpowiadają operacje zwracające nowy obiekt, który jest przypisywany
> > >    z powrotem tej zmiennej.
> > 
> > Pod warunkiem, że mamy możliwość "zakodowania" przypisania do zmiennej
> > trzymającej ten niezmienialny obiekt.

> Jasne; miałem na myśli imperatywną, przypisywalną zmienną. Słowo
> "zmienna" jest niestety wieloznaczne i część języków programowania,
> tak jak matematyka, używa go w znaczeniu lokalnej nazwy jakiejś
> wartości, niekoniecznie zmieniającej się w czasie.

Wiem, o co chodzi ze zmienną w obu przypadkach.

Problem tylko w tym, czy język pozwala zrobić jakiś trik, który zakoduje
operację modyfikującą obiekt tak, żeby wyglądała jak modyfikująca, a w
rzeczywistości będzie tworzyć nową kopię obiektu.

> > >    Tam, gdzie w C++ obiekt jest przekazywany przez wartość, w innym
> > >    języku przekazywana jest bieżąca wartość zmiennej. Tam, gdzie
> > >    w C++ obiekt jest przekazywany przez referencję, w innym języku
> > >    przekazywana jest referencja na zmienną.
> > 
> > A co z porównywaniem?

> Nie rozumiem pytania. Dla obiektów tej kategorii porównanie jest dość
> podstawową operacją, zdefiniowaną zależnie od typu. No i normalnie jest
> oferowane, najczęściej w formie wspólnego interfejsu dla różnych typów.

No właśnie. A co z implementacją?

> > Nie, Qrczak. String to nie jest tablica znaków - jak już się czepiamy
> > skrzywionego myślenia, to i ja się poczepiam: twoje myślenie jest skrzywione
> > przez języki, w których string jest reprezentowany przez listę znaków (jak np.
> > Haskell).

> Etam. W C string też jest w zasadzie tablicą znaków,

Ale Haskel, w odróżnieniu od C, nie jest językiem niskopoziomowym, z
siermiężnymi właściwościami umożliwiającymi ledwie co zrobienie struktur, żeby
jakoś operować własnymi typami.

> a w C++ mało do
> tego brakuje.

Nawet jeśli można trochę ponaciągać fakty, żeby to stwierdzić, to nie zmienia
faktu, że jednak zrobiono do tego osobne typy.

> Szablon basic_string jest nawet parametryzowany typem
> znaku.

Complex też jest parametryzowany typem wartości zmiennoprzecinkowej, i co z
tego?

> Ten typ ma trochę więcej wymagań niż typ elementu wektora, ale
> poza tym różnica jest mała.

Różnica jest zasadnicza. Gdyby nie to, nikt by się nie silił na robienie
osobnego typu dla stringa i vectora. Popatrz na język D - tam nie ma osobnego
typu dla stringa, a string jest jednocześnie tablicą znaków (czyli, inaczej
mówiąc, tablica znaków posiada całą funkcjonalność potrzebną dla stringa).

> W Kogucie, podobnie jak w Pythonie i Perlu, w ogóle nie ma osobnego
> typu znakowego. Kiedy znak jest materializowany w programie, jest
> reprezentowany przez string długości 1.

W Tcl-u też tak jest, więc to chyba nic niezwykłego jak na język skryptowy.

> Na pewno więc string jako
> tablica znaków nie jest moim skrzywieniem.

Rozumiem, że musisz się jakoś bronić ;).

Ale na poważnie - fakt, że w wielu językach string jest tożsamy z tablicą
znaków, uważam za pomyłkę. Tablica znaków jest czymś koncepcyjnie innym od
stringa, a jeśli coś jest koncepcyjnie inne, to system typów powinien pozwalać
na wyrażenie tego osobnym typem. Język, który tego nie ma, ma "naleciałości
asemblerowe".

> > > Przykłady języków, które mają wyłącznie zmienialne stringi mimo
> > > paradygmatu, który do tego nie pasuje: Lisp, Scheme, OCaml, Ruby
> > > (czasem są zmienialne łącznie ze zmianą długości, a czasem nie).
> > 
> > Tzn. jak? Musisz jawnie skopiować string, jeśli chcesz, żeby źródłowy się nie
> > zmienił?

> Tak. Nie jest to aż tak błędogenne, jak mogłoby się wydawać, jeśli się
> stosuje konwencję, że zmienia się tylko te stringi, które się przed
> chwilą utworzyło.

Ależ jaja sobie robisz. Nie jest to aż tak błędogenne, jeśli się pamięta. Nie
zapominaj, że bardzo często używasz tego argumentu przeciwko C++.

> Czyli string jest w zasadzie traktowany jako typ
> niezmienialny, imperatywne jest tylko jego tworzenie.

Bardzo dobrze - ale niech w takim razie język sam zadba o to, żeby tego
stringa nie dało się zmienić po jego utworzeniu.


-- 
//  _    ___         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: <et4gmk$9ru$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-mail
References: <pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.pl> <esd1k1$9bp$1@bandai.magma-net.pl> <esemb6$2r3$1@kujawiak.man.lodz.pl> <eses6u$8fm$1@inews.gazeta.pl> <esf3am$r52$1@bandai.magma-net.pl> <esfg25$b2m$1@kujawiak.man.lodz.pl> <1173137761.17765.52.camel@qrnik> <esmtmk$ii1$1@kujawiak.man.lodz.pl> <1173310324.15622.77.camel@qrnik> <esuufm$jfv$1@kujawiak.man.lodz.pl> <1173568302.9234.25.camel@qrnik> <et178b$f2o$1@kujawiak.man.lodz.pl> <1173651323.19503.18.camel@qrnik>

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: Wartości i obiekty
Date: Mon, 12 Mar 2007 21:28:52 +0000 (UTC)
Organization: LODMAN - Metropolitan Area Network in LODZ, Poland
Lines: 251
Sender: Michal Malecki <sektor@zly.kis.p.lodz.pl>
Message-ID: <et4gmk$9ru$1@kujawiak.man.lodz.pl>
References: <pl-comp-objects-faq-1-1166202604@ict.pwr.wroc.pl>   <esd1k1$9bp$1@bandai.magma-net.pl> <esemb6$2r3$1@kujawiak.man.lodz.pl> <eses6u$8fm$1@inews.gazeta.pl> <esf3am$r52$1@bandai.magma-net.pl> <esfg25$b2m$1@kujawiak.man.lodz.pl> <1173137761.17765.52.camel@qrnik> <esmtmk$ii1$1@kujawiak.man.lodz.pl> <1173310324.15622.77.camel@qrnik> <esuufm$jfv$1@kujawiak.man.lodz.pl> <1173568302.9234.25.camel@qrnik> <et178b$f2o$1@kujawiak.man.lodz.pl> <1173651323.19503.18.camel@qrnik>
NNTP-Posting-Host: zly.kis.p.lodz.pl
Mime-Version: 1.0
X-Trace: kujawiak.man.lodz.pl 1173734932 10110 212.191.89.2 (12 Mar 2007 21:28:52 GMT)
X-Complaints-To: abuse@man.lodz.pl
NNTP-Posting-Date: Mon, 12 Mar 2007 21:28:52 +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