10 listopada 2009

Środowisko programistyczne dla Clojure i poprzedni przykład funkcjonalniej

Nasze praktyczne przygody z Clojure możemy zacząć od instalacji wymaganego oprogramowania i/lub uruchomienia Clojure REPL lokalnie, albo skorzystać z Lord of the REPLs. Interesująca alternatywa, nie tylko pomocna w nauce Clojure, ale i Groovy, Ruby czy Scala.

Kiedy jednak nasze doświadczenie w programowaniu funkcyjnym w Clojure zacznie wykraczać poza proste aplikacje będące złożeniem kilku funkcji i spróbujemy wyjść poza ramy REPL możemy skorzystać ze wsparcia udostępnianego przez zintegrowane środowiska programistyczne, np. NetBeans IDE, Eclipse IDE czy IntelliJ IDEA. Wystarczy doszukać się właściwej wtyczki (rozszerzenia) i możemy czerpać garściami z pomocy IDE - możemy chociażby znieść z siebie trud dbania o nawiasy. Dla NetBeans IDE mamy Enclojure. Ostatnia wersja z 3.11, więc dostatecznie świeża, aby uwierzyć w aktywny jej rozwój. Dla środowiska Eclipse IDE mamy Clojure plug-in for Eclipse (clojure-dev), o której dowiedziałem się w artykule The Clojure programming language - Take advantage of the Clojure plug-in for Eclipse. Stawiam na NetBeans IDE i Enclojure.

Dla bloggerów istnieje podświetlanie składni dla Clojure w ramach Syntax Highlighter 2.0 - All Syntax Highlighter 2.0 brushes collected, described and downloadable. Można zobaczyć go w działaniu w moich wpisach dotyczących Clojure, chociażby ten. Jeśli więc przyjdzie zaprezentować swoje cudo w Clojure szerokiej publiczności na własnym blogu warto podeprzeć się stylem clojure. Znacznie uatrakcyjnia prezentację kodu.

A skoro mamy środowisko możemy powrócić do naszego przykładu z surowcami, której wersja bardziej przypominająca wersję funkcjonalną (dosłownie i w przenośni) wygląda teraz następująco:
 (ns pl.jaceklaskowski.clojure)

(defn czas-zwiekszenia-produkcji
"Wylicza potrzebny czas (w minutach) do osiagniecia stanu magazynowego na uruchomienie rozbudowy do poziomu przy danej produkcji"
[docelowo, magazyn, produkcja]
(let [teraz (java.util.Calendar/getInstance)]
(. teraz (add (java.util.Calendar/MINUTE)
(* 60 (reduce max (map / (map - (map #(.getValue %) docelowo) (map #(.getValue %) magazyn)) (map #(.getValue %) produkcja))))))
(. teraz getTime)))
Widać wyraźne zwiększenie konstrukcji PF z reduce i map. Poza samym wyliczeniem, za ile nastąpi zwiększenie (w linii 8.) mamy również wyliczoną dokładną godzinę, kiedy to nastąpi (linie 6., 7. i 9.)

Zamiast dłubania w REPL zapisujemy cały program w pliku czas-zwiekszenia-produkcji.clj. Zwyczajowo skrypty w Clojure zapisywane są z rozszerzeniem .clj.

Tworzymy kolejny skrypt testowy skrypt-testowy.clj, który zweryfikuje poprawność działania.
 (load-file "czas-zwiekszenia-produkcji.clj")

(ns pl.jaceklaskowski.clojure)

(def magazyn {:drewno 10 :glina 10 :zelazo 10 :zboze 10})
(def docelowo {:drewno 20 :glina 20 :zelazo 20 :zboze 20})
(def produkcja {:drewno 5 :glina 5 :zelazo 5 :zboze 5})

(println (czas-zwiekszenia-produkcji docelowo magazyn produkcja))
Uruchomienie to po prostu wykonanie:
 $ java -jar clojure-1.1.0-alpha-SNAPSHOT.jar skrypt-testowy.clj
#<Date Tue Nov 10 23:36:07 CET 2009>
I tak kończy się moje poszukiwanie ładniejszej (=bardziej funkcyjnej) wersji czas-zwiekszenia-produkcji. Oczywiście nie kończy to mojej przygody z Clojure i jeśli kiedykolwiek pomyślałem, aby sobie odpuścić, bo i po co mi PF, to wizyta na blogu BEST IN CLASS jest faktycznie najlepszym w swojej klasie i nie pozwala zapomnieć o możliwościach Clojure. Warto tam zajrzeć w wolnej chwili, szczególnie w chwilach zwątpienia. Mnie zachwycił i upewnił w postanowieniu poznania go dokładniej.

W kolejnym podejściu postaram się zrozumieć o co chodzi w makro -> oraz funkcji line-seq z core.clj jak zasugerował Michael w komentarzu do ostatniego mojego wpisu o Clojure - Programowanie w Clojure - część 2 - pierwszy przykład z wyliczaniem surowców.