Archive for Programming

Nowoczesne wyliczanie

2009-11-04 1:33

W chyba każdy języku posiadającym pojemniki (jak wektory czy listy) istnieje koncepcja iteratorów: obiektów, które pozwalają na przeglądanie kolekcji i są uogólnieniem wskaźników. W najprostszym pozwalają one tylko na pobranie aktualnego elementu i przejście do następnego, ale jest to zupełnie wystarczające do celów wyliczania.
Z wierzchu więc wyglądają one całkiem prosto i przejrzyście – zwłaszcza, jeśli język udostępnia pętlę typu foreach, która ładnie i przezroczyście je opakowuje. Dlatego może wydawać się dziwne, czemu zazwyczaj mechanizm ten jest używany właściwie tylko dla pojemników; w teorii bowiem za pomocą iteratorów (zwanych gdzieniegdzie enumeratorami) można by było przeglądać dosłownie wszystko.
Weźmy chociażby wyszukiwanie plików na dysku – sporo programów w jakimś momencie swojego działania musi znaleźć pliki np. o danej nazwie w określonym katalogu. Wtedy zwykle zakasujemy rękawy i piszemy odpowiednią procedurę rekurencyjną lub bawimy się ze stosem czy kolejką. A czy nie fajniej byłoby, gdyby dało się to zrobić po prostu tak:

  1. for (FsIterator it = ListFiles("c:\\", "*.exe"); it; ++it) { /* zrób coś */ }

oczywiście przeszukując w ten sposób również podkatalogi bez jawnego “wchodzenia” do nich?… Według mnie to by było bardzo fajne :)

Od razu zaznaczę więc, że wbrew pozorom taki iterator jest jak najbardziej możliwy do napisania. Problemem jest jednak to, jak należy przechowywać jego stan. Kiedy wyszukiwanie czy przeglądanie zaimplementowane jest bezpośrednio jako jedna funkcja, robi się to w zasadzie samo: w postaci zmiennych lokalnych (stos/kolejka) albo parametrów (rekurencja). Nikt specjalnie nie zwraca na ten fakt uwagi. Jednak w momencie próby “wyciągnięcia” z algorytmu operacji Next (w celu stworzenia iteratora) okazuje się nagle, że wymaga to jawnego pamiętania tych wszystkich danych, które pozwalają obliczyć następny element. Przy przeszukiwania katalogów trzeba by na przykład pamiętać jakiś systemowy uchwyt wyszukiwania dla aktualnego katalogu, poziom zagnieżdżenia oraz analogiczne uchwyty… dla każdego takiego poziomu!
Zawracanie głowy, prawda? :) Nic dziwnego, że traktowanie wyszukiwania “per iterator” nie jest popularną praktyką. Z punktu widzenia piszącego algorytm wyliczania nieporównywalnie łatwiej jest po prostu wymusić jakiś callback i wywołać go dla każdego elementu; niech się programista-klient martwi o to, jak ten callback wpasować w swój kod. A że ten byłby o wiele czytelniejszy, gdyby w grę wchodziły iteratory? No cóż, iteratorów tak łatwo pisać się nie da…

…chyba że programujemy w Pythonie. Tam bowiem “iteratory” (zwane generatorami) piszemy w zupełnie unikalny, łatwy sposób. Weźmy dla przykładu taką oto klasę drzewa binarnego (BST – Binary Search Tree):

  1. class Tree:
  2.     def __init__(self, key, left = None, right = None):
  3.         self.key = key
  4.         self.left = left
  5.         self.right = right
  6.     def __insert(self, k): # wstawianie elementu
  7.         if (k < self.key):
  8.             if (self.left != None): self.left.__insert (k)
  9.             else:                   self.left = Tree(k)
  10.         else:
  11.             if (self.right != None): self.right.__insert (k)
  12.             else:                   self.right = Tree(k)
  13.     def insert(self, *keys): # wst. wielu elementów
  14.         for k in keys: self.__insert(k)&#91;/python]
  15. Żeby dało się je przeglądać zwykła pętlą <code>for</code> w porządku <em>inorder</em> (dającym posortowanie kluczy), piszemy do niego odpowiedni generator:
  16. [python]    def inorder(self):
  17.         if (self.left != None):
  18.             for t in self.left.inorder(): yield t
  19.         yield self.key
  20.         if (self.right != None):
  21.             for t in self.right.inorder(): yield t

I już – to wystarczy, by poniższa pętla:

  1. for i in tree.inorder():

rzeczywiście “chodziła” po krawędziach drzewa “w czasie rzeczywistym” – bez żadnego buforowania elementów na liście.

Tajemnica tkwi tutaj w instrukcji yield – działa ona jak “tymczasowe zwrócenie” elementu, który jest przetwarzany przez ciało pętli for. Gdy konieczny jest następny element, funkcja inorder podejmuje po prostu działanie począwszy od kolejnej instrukcji – i tak do następnego yielda, kolejnego cyklu pętli i dalszej pracy funkcji. yield działa więc jak callback, tyle że w obie strony. Całkiem zmyślne, czyż nie?
Aż chciałoby się zapytać, czy w innych językach – zwłaszcza kompilowanych – nie dałoby się zrobić czegoś podobnego. Teoretycznie odpowiedź jest pozytywna: przy pomocy zmyślnych sztuczek na stosie wywołań funkcji (technika zwana ‘nawijaniem stosu’ – stack winding) można uzyskać efekt “zawieszenia funkcji” po zwróceniu wyniku i mieć możliwość powrotu do niej począwszy od następnej instrukcji. Nie jestem jednak przekonany, jak taki feature mógłby współpracować z innymi elementami współczesnych języków programowania, jak choćby wyjątkami. Trudno powiedzieć, czy jest to w ogóle możliwe.

Ale skoro w Pythonie się da, to może już C++2x będzie to miał? ;-)

Tags: , , , , ,
Author: Xion, posted under Programming » 4 comments

Triki z PowerShellem #11 – Ćwierkamy

2009-10-30 19:41

W ramach wyposażania nowozainstalowanego systemu w niezbędne programy, przypomniałem sobie o istnieniu PowerShella. Kiedy jednak chciałem go ściągnąć, spotkała mnie przyjemna niespodzianka: PSh w Windows 7 jest już od razu zainstalowany, więc można go od razu zacząć go używać. Jak sądzę, przyczyni do zwiększenia jego popularności, co jest z pewnością dobrą rzeczą.

Obrazek z TwitteraFakt sprawił rzecz jasna, że zaraz zachciało mi się wypróbować go w jakimś nowym zastosowaniu. Padło na wysyłanie update‘ów do Twittera, w którym to zresztą niedawno się zarejestrowałem (i wciąż nie wiem, dlaczego ;)). Sprawa na oko nie jest trudna, bo sprowadza się do wykonania jednego żądania HTTP POST. Ale jak wiadomo, diabeł zwykle tkwi w szczegółach. Oto skrypt:

  1. # tweet.ps1
  2. # Wysyłanie nowego statusu do Twittera
  3.  
  4. [Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
  5.  
  6. # Stałe
  7. $LOGIN = "login" # lub e-mail
  8. $PASS = "hasło"
  9.  
  10. # Pobranie statusu od użytkownika
  11. $tweet = Read-Host -Prompt "Status"
  12.  
  13. # Złożenie żądania HTTP POST
  14. $uri = [Uri]"http://twitter.com/statuses/update.xml"
  15. $http = [Net.HttpWebRequest]::Create($uri)
  16. $http.Credentials = New-Object Net.NetworkCredential @($LOGIN, $PASS)
  17. $http.Method = [Net.WebRequestMethods+Http]::Post
  18. $http.ServicePoint.Expect100Continue = $false # (*)
  19.  
  20. # Wysyłanie danych
  21. $data = "status=" + [Web.HttpUtility]::UrlEncode($tweet)
  22. $http.ContentLength = $data.Length
  23. $sw = New-Object IO.StreamWriter @($http.GetRequestStream())
  24.     $sw.Write($data)
  25. $sw.Close()
  26.  
  27. # Wyświetlamy ID nowego statusu
  28. $resp = $http.GetResponse().GetResponseStream()
  29. $sr = New-Object IO.StreamReader @($resp)
  30. $xml = [xml]$sr.ReadToEnd()
  31. "Status updated (ID: " + $xml.status.id + ")" | Out-Host
  32. $sr.Close()
  33.  
  34. # Obsługa błędów
  35. trap    { "Error: " + $_.Exception.Message; return }

Jednym z owych detali było kodowanie statusu algorytmem dla URL-i (zamieniającym spacje na %20 itd.), wykonywane poprzez System.Web.HttpUtility.UrlEncode – stąd konieczność importowania assembly System.Web. Ale to jest w sumie pikuś.
Znacznie większym “trikiem” jest linijka oznaczona gwiazdką (*). Powoduje ona obejście domyślnego zachowania .NET, który do każdego żądania HTTP typu POST dodaje nagłówek:

  1. Expect: 100-continue

Powoduje on wysłanie tak naprawdę dwóch requestów: w pierwszym serwer ma tylko sprawdzić poprawność nagłówków (logowania, na przykład) i zwrócić status 100 (Continue). Dopiero w drugim klient wysyła właściwe dane. Mechanizm ten jest w .NET opakowany przezroczyście i ma zapobiegać niepotrzebnemu przesyłaniu dużych ilości danych w żądaniu, które i tak byłoby odrzucone.
API Twittera jednak tego nie obsługuje i jest to właściwe. Trudno przecież nazwać status, mający maks. 160 znaków, “dużą ilością danych”. Lepiej więc przesyłać go od razu, a domyślne zachowanie .NET-a obejść. To właśnie robi zaznaczony wiersz.

Przypomnę jeszcze tylko – gdy ktoś zechciał powyższego skryptu używać do przesyłania tweetów – że uruchomienie skryptu PSh z poziomu zwykłej linii poleceń wymaga parametru -Command i kropki:

  1. powershell -Command . 'ścieżka\tweet.ps1'

Do takiej komendy można np. utworzyć skrót i przypisać mu kombinację klawiszy w celu szybkiego uruchamiania.

Tags: , ,
Author: Xion, posted under Internet, Programming » 1 comment

Powtórka z DirectX

2009-10-29 20:58

Za sprawą przedmiotu o nazwie Grafika Komputerowa 3D musiałem ostatnio przypomnieć sobie, jak tak naprawdę i w praktyce koduje się w DirectX. Pewnie brzmi to dziwnie, ale w rzeczywistości przez ładnych kilka miesięcy nie pisałem większych ilości kodu, który by z tej biblioteki korzystał.

GK3D - screen
Piękna scena ;-)

Projekt, który musiałem teraz napisać, nie był ani trochę ambitny, bo polegał li tylko na wyświetleniu zupełnie statycznej sceny z kilkoma modelami, oświetleniu jej i zapewnieniu możliwości poruszania się w stylu strzelanek FPP. Oczywiście nie było też mowy o żadnych shaderach.

Niby banalne, ale jednak rzecz zajęła mi w sumie jakieś cztery znormalizowane wieczory (czyli od 3 do 4 godzin każdy). Częściowo było tak pewnie dlatego, że pokusiłem się jeszcze o teksturowanie, możliwość regulacji paru opcji renderowania czy bardzo, bardzo prosty menedżer sceny – czytaj: drzewko obiektów + stos macierzy ;)
Wydaje mi się jednak, że ważniejszą przyczyną był nieszczęsny fixed pipeline, którego byłem zmuszony używać. Jeszcze kilka lat temu nigdy bym nie przypuszczał, że to powiem, ale… shadery są po prostu łatwiejsze w użyciu. Porównując chociażby trywialne mieszanie koloru diffuse wierzchołka z teksturą przy użyciu pixel shadera:

  1. psOut.color = psIn.diffuse * tex2D(tex0, psIn.Tex0)

oraz stanów urządzenia:

  1. device->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_CURRENT);
  2. device->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
  3. device->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE);

nietrudno jest ocenić, w której znacznie lepiej widać, co faktycznie dzieje się z kolorem piksela. No, chyba że dla kogoś multum stałych w rodzaju D3DABC_SOMESTRANGEOPTION jest czytelniejsze niż po prostu mnożenie ;P

Inną sprawą jest też to, że w DirectX napisanie aplikacji od podstaw jest stosunkowo pracochłonne. Brak “złych” funkcji typu glVertex* ma rzecz jasna swoje zalety, lecz jest też jednym z powodów, dla których tak opłaca się posiadanie własnego frameworka, a może nawet i – tfu tfu – silnika ;-)

Pola i akcesory wewnątrz klasy

2009-10-26 23:53

Zgodnie z zasadami programowania obiektowego pola klas nie powinny być bezpośrednio dostępne na zewnątrz. Należy jest zawsze opakowywać w akcesory: właściwości lub krótkie metody typu get i set. Z nich właśnie korzysta potem kod zewnętrzny, dzięki czemu nie może on (w dobrze napisanej klasie) niczego zepsuć poprzez – chociażby – ustawienie jakiegoś pola na nieprzewidzianą wartość.
Taka praktyka jest powszechnie przyjęta i raczej nie budzi wątpliwości. Inaczej jest z używaniem tychże pól lub akcesorów wewnątrz klasy, a więc w jej własnych metodach. Tutaj często mamy wybór: czy odwołać się do “gołego” pola, czy też poprzez odpowiednią właściwość/metodę.

Które podejście jest właściwsze? C#/.NET od wersji 3.0 zdaje się to rozstrzygać, umożliwiając półautomatyczne tworzenie właściwości:

  1. public class SomeClass
  2. {
  3.     public int Foo { get; set; }
  4. }

Nie ma tutaj nie tylko bloków get i set, ale i ukrytą pod tą właściwością pola. Przy korzystaniu z tego feature‘a żadnego dylematu więc nie ma.
Wydaje mi się jednak, że wybór nie jest taki oczywisty i że niekoniecznie należy używać akcesorów wewnątrz klasy. Argumentem przeciw, który od razu przychodzi do głowy, jest troska o wydajność – zwykle jednak przesadzona, bo proste gettery i settery są bez problemu rozwijane w miejscu użycia. Drugim ‘ale’ jest wygląd kodu w językach bez właściwości; zwłaszcza dotyczy to Javy, w której odpowiednik C#-owego:

  1. Foo.Bar.Baz.Qux.Thud = 5;

roiłby się od getów. W końcu można by się jeszcze pokusić o uzasadnienie na wpół merytoryczne: skoro bądź co bądź prywatne pole jest składnikiem klasy do jej wyłącznej dyspozycji, to dlaczego metody miałyby obchodzić je dokoła zamiast odwoływać się doń bezpośrednio? A może jednak lepiej jest skorzystać z tej dodatkowej warstwy pośredniczącej (mogącej np. wykrywać jakieś błędy)?…

Na razie – mimo całkiem przyzwoitego doświadczenia w programowaniu w językach wszelakich – trudno jest mi na te pytania odpowiedzieć. Ostatnio aczkolwiek skłaniam się ku bezpośredniemu dostępowi do pól w metodach klas. Chętnie poznałbym jednak opinie innych koderów na ten temat.

Tags: , , , , ,
Author: Xion, posted under Programming » 3 comments

Lepszy pasek przewijania

2009-10-22 14:41

RockScrollZorientowanie się w dużym pliku z kodem (gdzie przez ‘duży’ rozumiem przynajmniej taki, który przekracza tysiąc linii) może niekiedy przysparzać kłopotów. W IDE są oczywiście narzędzia nawigacyjne, pozwalające na przejście do poszczególnych klas, metod czy deklaracji, o ile tylko znamy chociaż ich nazwy. Nie zawsze jednak tak jest. Jeśli o danej metodzie pamiętamy tylko to, że “była długa i skomplikowana”, a o jakiejś właściwości jedynie tyle, iż “jest gdzieś wśród parunastu innych deklaracji”, to najpewniej oznacza, że w ich poszukiwaniu będziemy musieli przeglądnąć cały plik od początku do końca.
Chyba że… No właśnie – chyba że dałoby się spojrzeć na kod z daleka, by zobaczyć jego ogólną strukturę. Wiadomo bowiem, że metoda “długa i skomplikowana” będzie miała najpewniej sporą ilość wcięć, a długi ciąg deklaracji, jedna pod drugą, też były łatwy do odróżnienia od innych kawałków kodu. W książce o wiele mówiącej nazwie Czytanie kodu znalazłem kiedyś radę, że do uzyskania takiego ogólnego spojrzenia można wykorzystać edytor tekstu typu Word, pozwalający na podgląd wydruku wielu stron naraz.

O wiele wygodniej byłoby jednak mieć podobną możliwość wprost w IDE. NetBeans posiada namiastkę czegoś takiego, jednak za jej pomocą można tylko szybko stwierdzić, gdzie w kodzie znajdują się błędy kompilacji (pisałem zresztą o tym trochę ponad rok temu). Porządną, wielkoskalową, a w dodatku całkiem funkcjonalną “mapę kodu” da się za to znaleźć w… Visual Studio.
Mówię tu o darmowym pluginie o nazwie RockScroll, będącym zresztą początkowo wewnętrznym narzędziem Microsoftu. Tym, co wtyczka ta robi, jest zastąpienie standardowego pionowego paska przewijania przez szerszy pionowy pasek, pokazujący podgląd aktualnie edytowanego w postaci długiej “miniaturki” z kolorowaną składnią. RockScroll działa przy tym podobnie jak zwyczajny pasek przewijania, a więc pozwala na przejście kliknięciem do wybranego miejsca w pliku. Ponadto potrafi też zaznaczać breakpointy oraz koloruje wszystkie wystąpienia wskazanego (dwukrotnie klikniętego) słowa w danym pliku – całkiem przydatne. Jedynym mankamentem jest chyba tylko brak wsparcia dla zwijanych i rozwijanych regionów kodu oraz ewentualnie fakt, że w poziomie plugin zajmuje jakieś cztery razy więcej miejsca niż standardowy pasek przewijania. Na szerokoekranowych monitorach ciężko jednak uznać to za wadę :)

Tags: ,
Author: Xion, posted under Applications, Programming » 10 comments

Ściąga z modyfikatorów dostępu

2009-10-16 16:08

W prawie wszystkich językach obiektowych istnieją tak zwane specyfikatory dostępu, przykładem których jest choćby public czy private. Pozwalają one ustalać, jak szeroko dostępne są poszczególne składniki klas w stosunku do reszty kodu. Modyfikatory te występują m.in. w trzech najpopularniejszych, kompilowalnych językach obiektowych: C++, C# i Javie.

Problem polega na tym, że w każdym z nich działają one inaczej. Mało tego: każdy z tych języków posiada inny zbiór tego rodzaju modyfikatorów. Dla kogoś, komu zdarza się pisać regularnie w przynajmniej dwóch spośród nich, może to być przyczyną pomyłek i nieporozumień.
Dlatego też przygotowałem prostą ściągawkę w postaci tabelki, którą prezentuję poniżej. Symbol języka występujący w komórce oznacza tutaj, że dany modyfikator (z wiersza) powoduje widoczność składnika klasy w określonym miejscu (z kolumny).

Specyfikator Klasa Podklasa Pakiet Reszta
public C++ C# Java C++ C# Java C# Java C++ C# Java
protected C++ C# Java C++ C# Java Java
protected internal C# C# C#
internal C# Java C# Java
private C++ C# Java

Parę uwag gwoli ścisłości:

  • Znaczek C++ nie występuje w kolumnie Pakiet nawet dla modyfikatora public, jako że w tym języku w ogóle nie istnieje pojęcie pakietu.
  • Sam termin ‘pakiet’ jest poza tym właściwy dla Javy. W .NET (C#) jego odpowiednikiem jest assembly, jakby ktoś nie wiedział :)
  • Z kolei internal jest słowem specyficznym dla C#, a jego ekwiwalentem w Javie jest po prostu pominięcie modyfikatora w deklaracji (np. int x; zamiast internal int x;).
  • W końcu, protected internal jest modyfikatorem występującym wyłącznie w C#/.NET.
Tags: , , , , , ,
Author: Xion, posted under Programming » 10 comments

override w C++, i nie tylko

2009-10-13 19:13

O braku słowa kluczowego override w C++ zdarzyło mi się już wspominać. Wskazywałem też, że jego niedobór jest całkiem istotnym brakiem, gdyż przypadki nadpisywania metod wirtualnych nie zawsze są zupełnie oczywiste.
Tym bardziej jest mi miło podzielić się odkryciem, które kiedyś poczyniłem na forum Warsztatu. Dowiedziałem się mianowicie, że słówko to do C++ można… dodać!

Jak to? – słychać pewnie pełne zdziwienia głosy. Ano bardzo prosto; zostało to zresztą wyczerpująco przedstawione w oryginalnym poście Esidara. Tutaj pokrótce tylko nakreślę jego ideę, która sprowadza się do genialnego spostrzeżenia, iż C++ zasadniczo radzi sobie bez override. A skoro tak, to jego wprowadzenie jest banalnie i wymaga zaledwie jednej linijki kodu:

  1. #define override

I już! Składniowo można go teraz używać dokładnie tak samo jak analogicznego słówka z C#… albo właściwie jakkolwiek inaczej – implementacja jest bowiem na tyle elastyczna, że zaakceptuje każde jego wystąpienie :) To aczkolwiek jest też, niestety, jego wadą…

W porządku, żarty na bok. Żeby ta notka nie była jednak zupełnie bezużyteczna, pokażę jeszcze, jak przekonać Visual C++, by nasze nowe słówko było przezeń kolorowane tak, jak inne słowa kluczowe w C++.
Da się to zrobić, tworząc (lub edytujący istniejący) plik UserType.dat, umieszczony w tym samym katalogu co plik wykonywalny Visual Studio (devenv) – czyli gdzieś w czeluściach Program Files, zapewne. W tymże pliku wpisujemy w nowej linijce override. Jeśli przypadkiem chcielibyśmy kolorować też inne, niestandardowe słowa kluczowe (to pewnie jest bardziej prawdopodobne), to każde z nich należy umieścić w osobnej linijce.
Po zapisaniu pliku i restarcie VS wystarczy teraz wejść w opcje programu, do zakładki Environment > Fonts & Colors, a tam na liście Display items zaznaczyć User Keywords i przyporządkować im wybraną czcionkę i/lub kolor. Możliwe zresztą, że w ogóle nie musimy tego robić, jako że domyślnie własne słowa kluczowe kolorowane są tak, jak te “oryginalne”.

 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.