Article View: pl.comp.objects
Article #15729Re: Wartości i obiekty
From: Sektor van Skijlen
Date: Mon, 12 Mar 2007 21:28
Date: Mon, 12 Mar 2007 21:28
251 lines
14011 bytes
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-mailReferences:
<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