26 stycznia 2012

Zapytany o początki z Clojure

0 komentarzy
Nie długo trwało, aby kilka maili między mną a rozmówcą zmieniło temat główny na (nieznacznie) poboczny - zalążek projektu librarian-clojure.

W ten sposób poniosło mnie (dosłownie i w przenośni) na Heroku, gdzie uruchomiłem webową wersję HelloWorld z Clojure'owym Ring.

Pewnie dla niejednego jest to o wiele za wiele, ale czego nie robi się dla zauważalnego rozwoju informatycznego, w którym nie ma miejsca dla już poznanego, a jedynie nieznacznie odświeżonego, a raczej próbuje się czegoś zupełnie nowego, np. zmiana języka obiektowego (Java, Scala) na funkcyjny (Clojure), albo zmiana języka statycznie typowanego (Java, Scala) na dynamiczny (Clojure, JRuby). Człowiek ponownie czuje się niedoświadczony i w ogóle (przesadnie) zagubiony. Podobno tylko tak można się czegoś nowego nauczyć w naszej branży.

Pożyjemy, zobaczymy. A teraz ciii...Clojure się zbliża!

Kiedy projekt trafił w ręce mojego rozmówcy, padło kilka pytań, które pomyślałem, że zadaje więcej osób, więc postanowiłem odpowiedzieć na nie w formie wpisu na blogu (a nóż widelec kogoś skusi i wejdzie w temat - byłoby cudnie - niechby chociaż komentował, jak to beznadziejnie nam idzie!) Marzą mi się regularne sesje, w których z kimś zasiadałbym przy IRC i jakoś współdzieląc ekran programował w parze zdalnie. Podobno się daje i jedynie wystarczy trochę chęci. Znajdzie się ktoś na ochotnika do librarian-clojure? Regularne sesje - stała pora, stała długość sesji i stałe miejsce mogłyby przyciągnąć innych, choćby do pooglądania. Cudo pomysł!

A teraz do pytań.

Jako IDE Eclipse?

"Jedynie" Clojure REPL (powłoka Clojure).

Jeśli jednak potrzebujesz wskazówek, to możesz skorzystać z Eclipse z wtyczką CounterClockWise (CCW) lub IntelliJ IDEA i La Clojure. Możesz spróbować Emacs.

Ja pracuję nad sobą, aby skorzystać z dobrodziejstw Eclipse CCW.

Czy project.clj to manifest?

Zależy co rozumiesz przez manifest? Ten javowy, to nie, ale manifest jako główny plik konfiguracyjny projektu, to jak najbardziej.

Innymi słowy, jest to serce projektu zarządzanego przez leiningen (odpowiednik Mavena w świecie Clojure).

Dlaczego nadałeś rozszerzenie *.md dla pliku README?

Chcę korzystać ze wspacia dla markdown przez GitHub, który rozpoznaje takie pliki jako pisane w tym metajęzyku. Daje to formatowanie tekstu bez większych udziwnień (niż te, których wymaga sam markdown).

Czy ns (namespace) to analogia dla pakietów w Javie?

Prawie. To właściwie javowy odpowiednik FQCN (ang. fully-qualified class name), czyli pakiet + nazwa klasy.

Każdy plik w Clojure to zwykle zestaw funkcji (nic jednak nie stoi na przeszkodzie, abyś potworzył przestrzenie nazw, a w nich funkcje itd.) Zachowując jednak zdrowy rozsądek, rozsądnie jest potraktować pojedynczy plik clj z ns jako klasę, w której na początku znajduje się deklaracja przestrzeni nazewniczej, w której "siedzi" również nazwa pliku (coś ala javowa klasa).

Czy :use to analogia do import w Javie?

Tak.

Czy deftest to słowo kluczowe definiujące funkcję 'testową' analogicznie do anotacji @org.junit.Test?

Tak.

Jakie wykorzystanie gita preferujesz - z poziomu powłoki systemowej, czy IDE, np. Eclipse EGit?

Powłoka. Javowe IDE wspierają gita, więc kiedy się już zdecydujesz, znajdziesz i wsparcie dla gita. Przekonuję się do Eclipse EGit.

20 stycznia 2012

Clojure finansowo z makrem -> (wplatanie na drugą pozycję)

4 komentarzy
Pisałem w poprzednim wpisie o moich ponownych poczynaniach wokół Clojure, aby w końcu zrozumieć jego istotę i w ogóle istotę programowania funkcyjnego.

Kontynuując moje "naukowe" aktywności zakończyłem lekturę "The Joy of Clojure: Thinking the Clojure Way", którą podsumowałem recenzją The "Why" of Clojure - mind-bending for enlightenment with idiomatic Clojure code w serwisie Amazon.com (nakłaniam do oddaniu głosu) i zabrałem się za kolejną książkę Clojure – Grundlagen, Concurrent Programming, Java. Tym razem będzie po niemiecku, a przez to zdecydowanie trudniej (dawno to było, kiedy władałem tym językiem w stopniu zadowalającym).

Jakby celem utrudnienia i tak już niełatwego zadania nauki Clojure po niemiecku, dostałem od Manning Clojure in Action, co przyprawiło mnie o niemały ból głowy - brnąć w przypominanie sobie niemieckiego (jedno z postanowień noworocznych), czy odłożyć to i zabrać się za drugą książkę, która pisana była z myślą o tym "Jak to zrobić w Clojure". Na razie Clojure z niemieckim przeważa.

Przeglądając reakcję czytelników po ostatnim wpisie Clojure finansowo uzasadniłem moje wewnętrzne przekonanie, że należy kontynuować temat programowania funkcyjnego z Clojure.


I właśnie w tym momencie zorientowałem się, że w prezentowanej aplikacji najbardziej zabrakło idiomatycznego Clojure, o którym tak wiele pisano w "The Joy of Clojure".

Było tak:
; zakładam 366 dni w roku 2012 i kapitalizację w dni wolne (niesprawdzone)
(defn interest-rate-per-day [interest-rate-per-year]
     (/ (* interest-rate-per-year 0.01) 366))

; funkcja pomocnicza do wyliczenia zysku (niewykorzystywana)
(defn interest [present-value interest-rate]
     (- (* present-value (inc interest-rate)) present-value))

(defn future-value [present-value interest-rate periods]
     (* present-value (Math/pow (inc (interest-rate-per-day interest-rate)) periods)))

(defn max-present-value [limit interest-rate periods]
     (/ limit (Math/pow (inc (interest-rate-per-day interest-rate)) periods)))

(max-present-value 10000 8.1 31)
Powinno być jednak tak (miejsca oznaczone przez ",,," w pierwszej funkcji służą *jedynie* wskazaniu, gdzie argument zostanie przekazany przez makro -> - usuń je, a zobaczysz zmianę, a raczej jej brak!):
; zakładam 366 dni w roku 2012 i kapitalizację w dni wolne (niesprawdzone)
(defn interest-rate-per-day [interest-rate-per-year]
  (-> interest-rate-per-year
      (* ,,, 0.01)
      (/ ,,, 366)))

(defn future-value [present-value interest-rate periods]
  (-> interest-rate 
      interest-rate-per-day 
      inc
      (Math/pow periods)
      (* present-value)))

; teraz dopiero zauważyłem, że future-value jest skopiowane w części do max-present-value :(
(defn max-present-value [limit interest-rate periods]
  (let [v (-> interest-rate interest-rate-per-day inc (Math/pow periods))]
    (/ limit v)))

(max-present-value 10000 8.1 31)
Od razu ładniej, nieprawdaż?

Mówią, że po ilości idiomatyczych konstrukcji poznaje się poziom zaawansowania w danej dziedzinie i nie inaczej jest w programowaniu - czy to w Javie, czy Clojure, czy innym języku, nawet takim jak angielski, niemiecki, itp.

A jak to jest u Ciebie z idiomatycznymi konstrukcjami? Znasz jakieś w Javie? Ciekawym również Scali (pewnie _ jest swego rodzaju idiomem) czy JRuby.

04 stycznia 2012

Clojure finansowo

9 komentarzy
Maksym to straszny brojek się robi. Kiedykolwiek przyglądam mu się bacznie, bez względu na jego minkę, widzę w nim gościa, którego będzie nosiło. Aż boimy się z Agatą pomyśleć, co będzie, kiedy zacznie raczkować, a później chodzić. Brrr...Będzie jazda bez trzymanki (jak u Owsiaka na WOŚPie)!


Gość właśnie skończył 3 miesiące i rośnie jak na drożdżach. Zdjęcie z miarką jest z 18 grudnia i teraz jest jeszcze dłuższy! Niech rośnie w siłę, aby tatusiowi na emeryturę zarobić :)

Clojure

Jeszcze w zeszłym roku zabrałem się ponownie za Clojure i w ramach postanowień noworocznych zdecydowałem się rozpoznać możliwość wykorzystania tego języka do tworzenia aplikacji biznesowych. Wiem, że ludzie w tym coś robią i powstają systemy korporacyjne, ale ja jakoś tego nie czuję.

Jeszcze.

Siedzę od lat w Javie, głównie w jej wydaniu korporacyjnym (Java EE), i kiedykolwiek pytany o możliwości Clojure, moim ochom i echom nie ma końca. Szybko się jednak kończy, kiedy pada "A jaką aplikację możnaby w tym napisać, aby widać było zysk w porównaniu, chociażby, z Javą?". I tu mnie pot zalewa, zaczynam czkać i kończy się na mizernym "Nie wiem". Właśnie na tym chciałbym popracować w tym roku. Muszę to wiedzieć, albo pora zarzucić Clojure jako język, któremu poświęcam czas.

Czytam sobie pomalutku "The Joy of Clojure: Thinking the Clojure Way" i poszukuję odpowiedzi na nurtujące mnie odpowiedzi sensowności stosowania języka przy tworzeniu aplikacji korporacyjnych (niechby to były wyłącznie aplikacje webowe). Wiem o istnieniu Compojure, Ring i podobnych rozwiązań, ale jakoś do mnie nie przemawiają, bo...tak naprawdę w ogóle się z nimi nie zmierzyłem na dłużej niż kilka chwil. To uważam za głównego winowajcę moich trudów mentalnych wokół Clojure.

I właśnie w tym roku postanowiłem to zmienić. Albo teraz, albo nigdy.

...finansowo

Ostatnimi czasy wzięło mnie na porównywanie ofert różnych banków dotyczących lokat (głównie "antybelkowych") i rachunków oszczędnościowych (z dniowym naliczaniem odsetek). Na bankier.pl trafiłem na ofertę Deutsche Banku z kontem oszczędnościowym z kapitalizacją 8,1% (bez podatku od zysków kapitałowych przy założeniu, że nie przekroczymy 10k PLN).

To górne ograniczenie mnie zaintrygowało. Moje skromne pokłady wiedzy finansowej zostały lekko nadwyrężone, kiedy miałem policzyć, ile należy wpłacić, aby nie przekroczyć 10k w ciągu miesiąca, w którym mam do dyspozycji jeden przelew bezpłatny. Chodziło o znalezienie tej magicznej kwoty, z którą mogłem spokojnie przespać 29 nocy, aby przed 30. przelać różnicę niezbędną do "przeczekania" kolejnego miesiąca (i tak wyłącznie do 28 lutego).

Wystarczyło otworzyć Google Docs i policzyć. Można było również skorzystać z kalkulatorów różnej maści w Sieci, albo LibreOffice, ale mnie zachciało się...Clojure (później dopiero doszło do mnie, że to był przerost formy nad treścią, ale co wiem, to moje).

Chcesz spróbować samodzielnie? Nic trudnego. Przekonaj się sam(a)!

Zainstaluj lein zgodnie z dokumentacją na stronie domowej projektu. Sprowadza się to do pobrania skryptu lein i kilku drobnych systemowych ustawień.

Teraz już tylko kilka chwil i siedzisz w Clojure po pachy.
jacek:~/sandbox
$ lein new clojure-finansowo
Created new project in: /Users/jacek/sandbox/clojure-finansowo
Look over project.clj and start coding in clojure_finansowo/core.clj
jacek:~/sandbox
$ cd clojure-finansowo/
jacek:~/sandbox/clojure-finansowo
$ lein repl
Copying 1 file to /Users/jacek/sandbox/clojure-finansowo/lib
REPL started; server listening on localhost port 25483
user=> 
I jesteś w REPL - interaktywnej powłoce Clojure. Skorzystaj z poniższego skryptu, aby wyliczyć ILE trzeba włożyć, aby nie przekroczyć magicznego 10k w miesiącu, w którym masz jeden przelew darmowy (wliczając przelew na rachunek spoza banku).
; zakładam 366 dni w roku 2012 i kapitalizację w dni wolne (niesprawdzone)
(defn interest-rate-per-day [interest-rate-per-year]
     (/ (* interest-rate-per-year 0.01) 366))

; funkcja pomocnicza do wyliczenia zysku (niewykorzystywana)
(defn interest [present-value interest-rate]
     (- (* present-value (inc interest-rate)) present-value))

(defn future-value [present-value interest-rate periods]
     (* present-value (Math/pow (inc (interest-rate-per-day interest-rate)) periods)))

(defn max-present-value [limit interest-rate periods]
     (/ limit (Math/pow (inc (interest-rate-per-day interest-rate)) periods)))

(max-present-value 10000 8.1 31)
Nie jest to cudo programowania w Clojure, ale nie miało takim być. Służyło jedynie odświeżeniu moich znajomości z Clojure i uważam, że ten cel został w pełni zrealizowany. Przede wszystkim, zrealizowałem swój własny cel biznesowy (a właściwie finansowy, ale przecież to to samo).

Czy nazwy funkcji odpowiadają zamierzeniom autora (patrząc oczyma czytelnika)? Czy zrobił(a)byś to inaczej? Daj się namówić na podzielenie się kilkoma usprawnieniami, aby biedną duszyczkę jackową uchować od ogni piekielnych :)

03 stycznia 2012

O operatorach przesunięć w Javie...prawie wszystko

0 komentarzy
Na zakończenie 2011 zasiadłem do rozpoznania wewnętrznej reprezentacji liczb całkowitych w Javie i w ten sposób powstały dwa wpisy:
Wszystko jednak zaczęło się od operatorów przesunięcia, a szczególnie jednego - prawego, bez znaku >>>. Zaczął mnie intrygować, skąd w ogóle pomysł na przesunięcia w Javie? Jakiej klasy problemy są rozwiązywane nimi? I jakie znaczenie ma przesunięcie z i bez znaku?

Dla przypomnienia:

& (AND) zwraca 1, wyłącznie jeśli oba bity są 1.
^ (XOR) zwraca 0, jeśli oba bity są jednocześnie 0 lub 1.
| (OR) zwraca 0, wyłącznie jeśli oba bity są 0.

Kolejność działań: &, ^, |

Wstępniak

Java definiuje 3 operatory przesunięcia - w lewo ze znakiem <<, w prawo ze znakiem >> i w prawo bez znaku >>>. Wszystkie działają wyłącznie na typach całkowitych long (64 bity) lub int (32 bity) i typ lewego argumentu wyznacza typ prawego. W przypadku węższych typów - short, byte, char - są one promowane do int i to po obu stronach niezależnie.

Jeśli typem lewego argumentu jest int, jedynie 5 najmłodszych bitów jest branych pod uwagę. Dlaczego? Taka jest długość bitowej reprezentacji int, tzn. 5 bitów pozwala na określenie zakresu przesunięcia od 0 do 31 włącznie, a tylko takie ma sens w przypadku 32 bitowej reprezentacji liczby całkowitej. W myśl tej zasady prawy argument jest zawsze pomniejszany przez zastosowanie operatora logicznego AND z maską 0x1F (0b11111), aby nie wyjść poza zakres [0, 31].

Jeśli jednak typem lewego argumentu jest long, wtedy jedynie 6 najmłodszych bitów jest branych pod uwagę. Ponownie, tylko tyle bitów - 2^6 - służy do reprezentacji bitowej dowolnej liczby long. Tym razem stosujemy logiczny AND z maską 0x3F (= 0b111111) i przesunięcie jest wykonane w zakresie 0 do 63 włącznie.

Lewy argument typu long nie wymusza promocji argumentu prawego do long, gdyż i tak int jest wystarczający, aby określić zakres przesunięcia [0-63] (przez zastosowanie maski 0x3F).
35            00000000 00000000 00000000 00100011
31 -> 0x1f    00000000 00000000 00000000 00011111
&             -----------------------------------
              00000000 00000000 00000000 00000011   -> 3
-29           11111111 11111111 11111111 11100011
31 -> 0x1f    00000000 00000000 00000000 00011111
&             -----------------------------------
              00000000 00000000 00000000 00000011   -> 3
Interesujący wydał mi się ten drugi przykład, gdzie prawy operand -29 daje ostatecznie przesunięcie 3 bitów (!) Z takim pytaniem na egzaminie znajomości Javy można łatwo polec.

Operator lewego przesunięcia ze znakiem <<

Działanie n << s to przesunięcie s bitów w n z uwzględnieniem bitu znaku, czyli zakres działania to 31 bitów dla lewego operandu typu int lub 63 dla long (odpowiednio maskowane jak opisałem wyżej). Nowe bity po prawej, które pojawią się przy przesunięciu, są wypełniane zerami.

Przesunięcie w lewo ze znakiem jest równoważne iloczynowi lewego operandu przez 2 do potęgi s, tj. n * 2^s.

Przykład: 11 << 3 = 11 * 2^3 = 11 * 8 = 88
11 (binarnie)       0000 0000 0000 0000 0000 0000 0000 1011
przesunięcie o 3    0000 0000 0000 0000 0000 0000 0101 1000
wynik:              88 (dziesiętnie)
Podobnie z liczbami ujemnymi.

Operator prawego przesunięcia ze znakiem >>

Działanie n >> s to przesunięcie s bitów w n z uwzględnieniem bitu znaku, czyli zakres działania to 31 bitów dla lewego operandu typu int lub 63 dla long (odpowiednio maskowane). Przy przesunięciu w prawo ze znakiem "wypełniaczem" miejsc pustych jest najważniejszy bit - bit znaku, tj. 0 dla dodatnich i 1 dla ujemnych.

Dla liczb nieujemnych, przesunięcie w prawo ze znakiem jest równoważne z obliczeniem ilorazu (bez reszty) lewego operandu przez 2 do potęgi s, tj. n / 2^s.

Przykład: 11 >> 3 = 11 / 2^3 = 11 / 8 = 1
11 (binarnie)       0000 0000 0000 0000 0000 0000 0000 1011
przesunięcie o 3    0000 0000 0000 0000 0000 0000 0000 0001
wynik:              1 (dziesiętnie)
Przykład: -17 >> 3 = -3
-17 (binarnie)      1111 1111 1111 1111 1111 1111 1110 1111
przesunięcie o 3    1111 1111 1111 1111 1111 1111 1111 1101
wynik:              -3 (dziesiętnie)

Operator prawego przesunięcia bez znaku >>>

Działanie n >>> s to przesunięcie s bitów w n wliczając bit znaku (bit znaku traci swoją wyjątkowość i jest traktowany jak każdy inny bit).

Z przesunięciem bez znaku "wypełniaczem" staje się 0, co może zamazać bit znaku i zmienić znak wyniku, który zawsze będzie dodatni.

Próbując wyrazić działanie >>> wzorem stosuje się następującą zasadę - jeśli lewy operand jest dodatni, wtedy wynik jest identyczny z prawym przesunięciem ze znakiem. Jeśli jednak lewy operand jest ujemy, wtedy wynik równa się (n >> s) + (2 << ~s). I właśnie ten wzór mnie zmroził najbardziej. Aż mnie do tej pory ciarki przechodzą :)

Przykład: 11 >>> 3 = 11 >> 3 = 11 / 2^3 = 1
11 (binarnie)       0000 0000 0000 0000 0000 0000 0000 1011
przesunięcie o 3    0000 0000 0000 0000 0000 0000 0000 0001
wynik:              1 (dziesiętnie)
Przykład: -17 >>> 3 = 536870909
-17 (binarnie)      1111 1111 1111 1111 1111 1111 1110 1111
przesunięcie o 3    0001 1111 1111 1111 1111 1111 1111 1101
wynik:              536870909 (dziesiętnie)

Zastosowanie przesunięć

Operatory ze znakiem to odpowiednio mnożenie i dzielenie lewego operanda przez s-tą potęgę dwójki.

Tylko skąd pomysł na >>>?! Na pewno nie zamierzam zapamiętać wzoru na wynik (chociaż przy przeglądaniu Sieci i spisywaniu wiedzy już zagnieździło się w głowie). Musi być tego jakieś sensowne uzasadnienie.

Dopiero w The Unsigned Right Shift olśniło mnie, kiedy trafiłem na możliwe zastosowanie operatora prawego przesunięcia bez znaku "shifting bits that does not represent a numeric value, e.g. pixel-based values and graphics."

I uzupełniając, w Bit twiddling in Java: The Unsigned Right Shift Operator:

Mimo, że każdy ciąg bitów o długości mniejszej niż 64 (najdłuższy typ w Javie - long) reprezentuje pewną liczbę całkowitą, to jedynie przesunięcia ze znakiem szanują reprezentację ciągu w postaci liczby - zachowują znak, a on ma znaczenie przy liczbach. W przypadku przesunięcia w prawo bez znaku >>> reprezentacja liczbowa nie ma znaczenia, a jedynie ciąg bitów - właśnie przez brak dbałości o znak liczby.

Innymi słowy, główne zastosowanie prawego przesunięcia bez znaku to sterowniki urządzeń, niskopoziomowa obsługa grafiki i formatów graficznych, pakiety komunikacyjne, kryptografia. W mojej karierze z Javą nigdy jednak nie spotkałem się z takimi zadaniami, ani chociażby minimalnego użycia operatorów przesunięcia. Zdają się być zbyt magiczne, aby miały zastosowanie nad chociażby java.lang.Math.pow(double, double). Mówi się również, że maksymalna liczba możliwych pytań z operatorów przesunięć na egzaminie z SCJP nie przekracza...jednego (!)

Zagadka: Jaki będzie wynik dowolnego przesunięcia liczby 0xFFFFFFFF (8 znaków F, tj. 32 jedynki) dla operatorów prawego przesunięcia ze znakiem?

W artykule Bitwise operation znalazłem bardzo intrygujące zachowanie przesunięć.

Zacznijmy od zagadki: Jaki będzie wynik odpowiednich przesunięć - (-12 >> 2) << 2, (-12 << 2) >> 2, (-12 >>> 2) << 2, (-12 << 2) >>> 2?

UWAGA: Przesunięcia typów mniej pojemnych, które będą rozszerzane do int - do 32 bitów, np. 8-bitowy byte rozszerzany jest do 32 bitowego int, więc przesunięcie >>> może nie mieć żadnego wpływu i należy maskować liczbę przed przesunięciem, np. (b & 0xFF) >>> 2.

Przesunięcie w prawo zawsze skutkuje "wypadaniem" liczb poza koniec (jakby wpadały w przepaść i były tracone na zawsze).

W The SCJP Tip Line Bit Shifting by Corey McGlone trafiłem na ciekawą zagadkę, która wynika bezpośrednio z powyższych reguł przesunięcia liczb całkowitych (szczególnie maskowaniem prawego operandu).

Zagadka: Jaki będzie wynik 3 >>> 32?

Na odpowiedź masz jedynie sekundę :)

Pamiętasz maskowanie prawego operandu przed wykonaniem przesunięcia? Dla int będzie to maska 0x1F, czyli 32. Czy teraz łatwiej odpowiedzieć na powyższe pytanie? To jaki będzie wynik?

Wystarczy zastosować mod 32 i masz gotową odpowiedź.

3 >>> 32 = 3 >>> (32 % 32) = 3 >>> 0 = 3

Fajne, co?! Właśnie takie cuda w Javie i w ogóle w językach programowania nakręcają mnie na dalsze ich zgłębianie. Czego i Tobie życzę w Nowym Roku 2012! :)