Implementacja identikon w PLT Scheme

Synthroid Without Prescription Inderal No Prescription Nexium For Sale Prevacid Generic Buy Elimite Online Prevacid Without Prescription Ultram No Prescription Prevacid For Sale Ultram Generic Buy Prednisone Online

Gdy w sieci przeczytałem o identikonach o wiele bardziej urzekła mnie estetyka ich wyglądu niż ich techniczne zalety. Identikony, w formie zaimplementowanej przez Dona Parka, służyć mają jako graficzna reprezentacja tożsamości osoby w sieci. Założenie jest takie, że kolorowe wzory są łatwiejsze do porównania i zapamiętania przez człowieka, niż numer IP. Dodatkowo, przepuszczając IP użytkownika przez funkcję haszującą z sekretnym ziarnem możemy zachować właściwości identyfikujące bez potrzeby ujawniania samego numeru IP.

Będąc zainteresowany samymi wzorami, szybko odnalazłem opis sposobu ich generowania i zacząłem zastanawiać się nad implementacją. Pomyślałem bowiem, że to świetna okazja na wypróbowanie możliwości Scheme i bliższe poznanie wybranego środowiska. Padło na PLT, głównie ze względu na moją wcześniejszą z nim styczność i dobre doświadczenia wyniesione z eksperymentów w DrScheme.

Jak się później dowiedziałem, bloczki, z których zbudowane są identikony, zostały początkowo opisane przez Jareda Tarbella na podstawie mającej długą historię sztuki quiltu. A to z kolei pamiętam, że gdzieś już widziałem.

Przede wszystkim zachęcam do ściągnięcia i wypróbowania kodu. Do prawidłowego działania wymaga on PLT z serii 3.99 (lub 4.0, który ma wyjść całkiem niedługo). Poniżej zaś kilka komentarzy i wniosków, które nasunęły mi się podczas implementacji.

Bibliotekę wrzuciłem na Planetę PLT, by jej użyć wystarczy więc jedna linijka kodu:
(require (planet “identicons.ss” (“mk” “identicons.plt”)))
Implementacja identikon zostanie automatycznie ściągnięta i zainstalowana.

System obiektowy

System obiektowy PLT Scheme funkcjonalnie nie różni się bardzo od tych zawartych w językach takich jak Java, Python, czy Ruby. Filozofia jest jednak trochę inna, głównie z tej przyczyny, że system obiektowy nie jest podstawą języka, ale jednym z wielu sposobów organizacji programu. Po załadowaniu modułu scheme/class nie doświadczymy więc wielkich zmian. Podstawowe typy danych, takie jak liczby, symbole, czy ciągi znaków nie zostają wciągnięte w hierarchię klas. Prawdę mówiąc, poza klasą object% i interfejsem externalizable<%> hierarchia ta jest całkiem pusta i zadanie wypełnienia jej leży w rękach programisty. Po bibliotekach standardowych pękających od podstawowych klas i obiektów jest to całkiem odświeżające doświadczenie, muszę przyznać. W założeniu twórców PLT obiekty mają służyć do sprawnego zamodelowania dziedziny problemu, przed jakim staje programista. A do tego nie potrzeba bagażu historycznego jaki często niosą ze sobą hierarchie starszych języków obiektowych.

Podstawowe konstrukcje są dość dobrze opisane w dokumentacji, a pomiędzy liniami można też poznać kilka konwencji. Nazwy klas kończą się %, a nazwy interfejsów <%>. W obrębie wyrażenia definiującego klasę dostępnych jest kilka form specjalnych służących do definiowania atrybutów i metod. Do komfortowej pracy wystarczy znajomość czterech z nich:

  • init-field do definiowania atrybutów, które może ustawić użytkownik podczas tworzenia obiektu
  • field do definiowania pozostałych atrybutów
  • define/public do definiowania publicznych metod
  • define do definiowania metod prywatnych

Dla przykładu przyjrzyjmy się implementacji identikony:

(define identicon%
  (class object%
    (init-field seed)
 
    (field (32-bits            (make-bit-stream seed))
           (center-patch-shape (list-ref center-patch-shapes (32-bits 2)))
           (side-patch-shape   (list-ref patch-shapes (32-bits 4))))
 
    (define (get-patch-size dc))
 
    (define/public (draw dc))
 
    (super-new)))

Kod zdaje się mówić sam za siebie. seed jest atrybutem ustawianym podczas konstrukcji obiektu. 32-bits, center-patch-shape i side-patch-shape są ukrytymi atrybutami przyjmującymi zadane wartości. Warte zauważenia jest to, że 32-bits zależy od atrybutu seed, podczas gdy samo 32-bits jest używane w następujących po nim definicjach. get-patch-size jest metodą dostępną tylko dla pozostałych metod tej klasy (czyli jest metodę prywatną), a draw jest metodą publiczną. Wywołanie (super-new) kończące definicje klasy jest potrzebne do właściwej inicjalizacji obiektu przez nadklasę object%.

Konstrukcja obiektów jest bezbolesna. Przykładowo, by utworzyć nową identikonę wystarczy napisać:

(make-object identicon% 1234567890)

i dostaniemy z powrotem identikonę pokazaną poniżej. By zapisać identikonę do pliku, wystarczy wywołać na niej metodę save-to-file z podaniem nazwy pliku i rozmiarem identikony w pikselach:

(send identicon save-to-file “1234567890.png” 200)

O ile do tego momentu nie miałem większych zastrzeżeń do systemu obiektowego PLT to do sposobu wywoływania metod muszę się już przyczepić. :-) System obiektowy z przekazywaniem komunikatów (ang. message passing) moim zdaniem idzie trochę na przekór lispowej filozofii. Common Lispowe procedury ogólne (ang. generic procedures) nie dość, że potężniejsze, to moim zdaniem bardziej idą z duchem Lispa. Pisanie kodu podobnego do:

(send identicon draw 30)

wydaje się nienaturalne, umieszczając nazwę wykonywanej operacji dopiero na trzeciej pozycji wyrażenia. Naturalną składnią dla wołania metody draw jest następująca postać powyższego wyrażenia:

(draw identicon 30)

Co prawda, można by zacząć dodawać dla wszystkich metod publicznych odpowiadającą im procedurę, np.:

(define (draw object . args)
  (send/apply object draw args))

i może nawet powiązać ten efekt z define/public, ale wtedy mielibyśmy już nowy system obiektowy, łamiący świętą w świecie Scheme makrową higienę. Oczywiście świat nie jest czarno-biały i istnieją zalety podejścia używanego przez PLT. Jednym z niewątpliwych plusów jest fakt, że jedynym symbolem, jaki trzeba eksportować z modułu jest nazwa klasy - nazwy metod bowiem rozwijane są jedynie w kontekście konkretnego obiektu. Metody “podążają za” obiektami, co w systemie z przekazywaniem komunikatów ma sens.

W PLTowym pakiecie Swindle jest dostępny system obiektowy bliższy Common Lispowemu CLOSowi, będzie więc jeszcze okazja przyjrzeć się innemu podejściu.

Inną ciekawostką związaną ze standardowym PLTowym systemem obiektowym jest to, że klasy same nie są obiektami:

(object? identicon%) ;; => #f 
 

Nie możemy więc na nich wywoływać metod i definiować atrybutów. Nie jest to jednak potrzebne, w PLT istnieją bowiem moduły, w których możemy zawrzeć zarówno lokalny stan, jak i grupę procedur. O zabawach z metaklasami możemy jednak zapomnieć (co prawdopodobnie językowi wychodzi na zdrowie ;-).

Moduły i kontrakty

Wraz z nadejściem R6RS doczekaliśmy się standardowego wsparcia dla modułów (bibliotek) w Scheme, zanim jednak ten czas nastąpił każda z implementacji musiała rozwiązać ten problem po swojemu. Moduły w PLT, w ograniczonym zakresie jakim miałem okazję ich używać, spełniają swoją funkcję doskonale. By obudować całą zawartość pliku w moduł wystarczy dodać na jego początku linijkę z określeniem używanego podzbioru języka przy pomocy #lang. Przykładowo, by użyć języka rozszerzonego o możliwości operowania oknami i grafiką należy użyć:

#lang scheme/gui

Osiągamy w ten sposób efekt podobny do modułów pythonowych - każdy plik stanowi moduł, którego nazwa wywodzi się bezpośrednio z nazwy pliku, z pominięciem rozszerzenia. Plik identicons.scm zawiera więc moduł o nazwie identicons.

Prócz tej jednej linijki moduł prawdopodobnie nie będzie wymagał żadnych zmian. require wciąż działa, a wszystkie definicje są szczelnie zamknięte dla zewnętrznego świata. Udostępnianie symboli na zewnątrz odbywa się przy pomocy formy specjalnej provide, ja jednak od pierwszego wejrzenia zakochałem się w formie provide/contract. Przy jej pomocy bowiem możemy nie tylko określić eksportowane symbole, ale również określić kontrakt, który będzie obowiązywał pomiędzy modułem, a jego użytkownikiem. Przykładowo, kontrakt funkcji degrees->radians zdefiniowanej w module util wygląda następująco:

[degrees->radians (number? . -> . number?)]

Oznacza on, że degrees->radians jest funkcją, która oczekuje liczbę jako argument i obiecuje zwrócić liczbę. Proste. Jak widać, kontrakty mogą służyć do dynamicznego sprawdzania typów, ale ich możliwości sięgają daleko poza. Kontrakt można zbudować z dowolnego predykatu, a te mogą sprawdzać nie tylko typ, ale i wartości i stan obiektów przekraczających granice kontraktu. Świetne w kontraktach jest to, że są całkowicie opcjonalne. Nie ma więc żadnego problemu w tym, by dojrzalsze części budowanego przez nas systemu posiadały kontrakty i jednocześnie współpracowały z nowszymi modułami, których projekt wciąż jest niejasny - a przez to - pozbawiony kontraktów. Poza oczywistymi względami technicznymi kontrakty posiadają również niewątpliwą wartość dokumentacyjną. Odpowiednio dobrane nazwy predykatów mogą wiele powiedzieć o oczekiwaniach, jakie kładziemy na argumenty funkcji i o obietnicach jakie składamy co do wartości przez te funkcje zwracanych.

Co ciekawe, kontrakty można stosować nie tylko w kontekście modułów, ale również bezpośrednio do definicji, jak i do metod i atrybutów obiektów.

Składnia infixowa

Jak wspomina przypis na stronie dokumentacji kontraktów PLT Scheme udostępnia specjalną składnię z dwoma kropkami. Jej działanie jest bardzo proste. Każde wyrażenie o postaci:

(a b . x . c d)

przekształcane jest na:

(x a b c d)

jeszcze przed interpretacją. Oprócz tego, że składnia ta jest bardzo przydatna przy definiowaniu kontraktów, można ją (nad)użyć chociażby do pisania wyrażeń algebraicznych. Przykładowo:

(a . * . (b . + . c))

jest tym samym co:

(* a (+ b c))

Osobiście jednak odradzałbym posuwanie się z użyciem tego składniowego lukru zbyt daleko. ;-)

Narzędzia graficzne

Moje zainteresowanie grafiką w PLT zaczęło się od tego wspaniałego tutoriala wstępnego. Polecam go tym, którzy chcą zacząć przygodę z Lispem, ale nudzi ich operowanie wyłącznie na liczbach. A w zasadzie to polecam go każdemu, tak dobry jest. :-)

Niestety do implementacji identikon biblioteka prezentacyjna jest niewystarczająca. Głównym jej minusem jest brak możliwości obracania obiektów graficznych. Indeks szybko jednak zaprowadził mnie do metody rotate klasy dc-path%, która to stanowi część większej biblioteki graficznej dostępnej wraz z PLT. Klasa dc-path% umożliwia narysowanie, wypełnienie i manipulacje dowolnym kształtem, złożonym z odcinków, czy łuków. Konstrukcja takiej ścieżki jest bardzo prosta. Przykładowo, by narysować trójkąt najpierw tworzymy nowy obiekt dc-path%:

Cały poniższy kod musi być uruchamiany z linii poleceń mred lub w środowisku drscheme; funkcjonalność graficzna nie jest dostępna z poziomu mzscheme.

(define path (make-object dc-path%))

Po tym określamy punkt startowy (”punkt przyłożenia ołówka do kartki”):

(send path move-to 0 0)

i zaczynamy rysować jedną z dostępnych metod, przykładowo line-to:

(send path line-to 100 100)
(send path line-to 0 100)
(send path line-to 0 0)

Żeby zobaczyć efekt musimy przygotować odpowiedni kontekst do rysowania (ang. drawing context), czyli w praktyce cokolwiek, co implementuje interfejs dc<%>. Skorzystajmy z kodu identikon i użyjmy do tego celu zwykłej ramki (czy też “okna”):

(define (make-frame-dc)
  (let* ((frame (new frame% (label “New frame”) (width 200) (height 200)))
         (canvas (new canvas% (parent frame))))
    (send frame show #t)
    (sleep/yield 1)
    (send canvas get-dc)))

Utwórzmy więc właściwy obiekt i narysujmy na nim nasz trójkąt:

(define dc (make-frame-dc))
(send dc draw-path path)

W tym momencie na ekranie powinniśmy zobaczyć zarys trójkąta. Pokolorowanie go to tylko kwestia użycia odpowiedniego pędzla. Wybierzmy dla niego jakiś pozytywny kolor i przerysujmy nasz trójkąt ponownie (oczywiście wszystko bez restartowania interpretera, czy otwierania nowej ramki):

(send dc set-brush “green” ’solid)
(send dc draw-path path)

Gotowe, nasz trójkąt jest teraz zielony. A stąd już niedaleko do generowania kolorowych identikon. Po szczegóły zapraszam do źródeł.

Wnioski

Podsumowując, PLT jest dojrzałą i bogatą w rozszerzenia implementacją języka Scheme. Dzięki obszernej i dokładnej dokumentacji łatwo się jej nauczyć i korzystać z jej możliwości. Dla tych, którzy wolą zacząć od łagodniejszej niż strony dokumentacji lektury jest blisko z PLT związana książka “How to Design Programs”. Twórcy PLT nie boją się eksperymentować - dystrybucja PLT pełna jest ciekawych minijęzyków i odważnych pomysłów, jest więc prawdziwą kopalnią wiedzy dla zainteresowanych możliwościami Scheme i implementacją języków programowania ogólnie.Viagra versus levivia Feldene Women using viagra Accutane Xanax and pregnancy Information phentermine Indomethacin Adipex phentermine xenical Cefamandole Adenosine Lanoxin Tramadol cod Oxprenolol Methylergonovine Ibuprofen Nicorette Pulmonary hypertension and viagra Probucol Cialis drug impotence Cialis impotence drug eli lilly co Lortab and xanax without a prescription Levivia viagra compared Meropenem Viagra herbal alternative Saccharin Flexeril Colace Buy phentermine online same day delivery Mecamylamine Phentermine dangers Info on meridia Aldara Cheap price on phentermine Reglan Purchase xanax Hydrocodone ap ap Cialis soft Levoxyl Buy phentermine with no prescription Pyridoxine Cialis drug for impotence Order buy phentermine online Metronidazole Pharmacy phentermine sister Ipratropium Fioricet Clomocycline Cheap pharmacy viagra Pentaerythritol Buy hydrocodone overnight Xanax dosage Amiodarone 10 min viagra Voltaren Viagra drug Low natural resources for the drug phentermine Phentermine side effects Order cheap phentermine Paxil Lowest price on phentermine Lexapro interaction with phentermine Cialis price Zithromax Lotrel Arava Decamethonium Arthrotec Approval cialis Cope Levivia and viagra Xanax for sale Adipex diet phentermine pill prescription Potassium Buy cialis viagra Pain medication tramadol Diet ingredient phentermine pill Dangers of phentermine heart Terbutaline Saturday delivery phentermine Killer pain tramadol Ambien rx Cardizem Viagra sample pack Cheap tramadol prescriptions online Meridia weight loss pill Isoniazid Cod online pharmacy phentermine sell Phentermine in florida Generic viagra overnight delivery Dexbrompheniramine Hydrocodone picture Canadian cialis Cialis sales uk Lopressor Xanax no rx Xanax death About phentermine Cialis immunity Viagra kaufen Buy generic viagra online Phentermine hc Podophyllum Ipodate Octreotide Real phentermine Xanax pictures Kaopectate Lamivudine Herbal alternative to viagra Phentermine blue 30 mg Valsartan Anafranil Norgestrel Lansoprazole Tramadol online pharmacy Budesonide Phentermine blogging Mephenytoin Ditropan Order viagra now Keflex Phentermine with no prescription Difference between cialis and viagra Ambien overdose Tramadol hydrochloride tablet Cialis review Phentermine 37.5 mg free shipping Online pharmacies with doctor consultation for viagra Busulfan Phentermine prescribed online Buying tramadol online Levivia viagra online Phentermine worldwide shipment Phentermine ephedrine Estrone Phentermine 37.5 mg sale Viagra commercial Nasalcrom Shipping overnight phentermine Phentermine without doctor’s approval Actonel Phentermine credit card or cod _cialis et levitra How long does xanax stay in your body Ethotoin Where can i buy phentermine Buy Atarax Zidovudine Order soma carisoprodol Macrodantin

Komentuj wpis