"The Grid. A digital frontier. I tried to picture clusters of information as they move through the computer. What did they look like? Ships? Motorcycles? Were the circuits like freeways? I kept dreaming of a world I thought I’d never see. And then, one day, I got in." — Tron: Legacy

2012-12-20

Clojure z Emacs i Leiningen 2


"Leiningen is for automating Clojure projects without setting your hair on fire."
- czyli jak krok po kroku przygotować Emacsa do współpracy z Clojure i Leiningen. Podam też sposób jad dołączać własne biblioteki do projektu przy użyciu Maven.


Instalacja będzie prowadzona na systemie Ubuntu 12.10 x64. Jeżeli ktoś zauważy jakieś niezgodności, to proszę o komentarz.

Leinngen jest menedżerem pakietów dla projektów Clojure.  Ma on ułatwić pracę z zależnościami. Jednak nie ma tak pięknie jak można by się spodziewać na pierwszy rzut oka. Jeżeli pakietów nie ma w repozytorium Clojure, to trzeba stworzyć sobie własne. O tym na końcu.

Krok 1: Instalacja Emacs 24.x  i Leiningen 2

Najprościej jest to zrobić z terminala tymi poleceniami:

sudo apt-get install emacs24
wget https://raw.github.com/technomancy/leiningen/preview/bin/lein
chmod 755 lein
sudo mv lein /usr/bin
lein self-install


Krok 2: Konfiguracja Emacs 

Poleceniem z konsoli uruchmić:

emacs ~/.emacs.d/init.el

Wkleić to i zapisać:

(require 'package)
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)

Potem (M-x to klawisz Alt+x, po wciśnięciu wpisać resztę polecenia):

M-x eval-buffer

Krok 3: Instalacja wtyczki clojure-mode

Teraz trzeba załadować listę pakietów. Nie wiem dlaczego tak, ale sposób podany na Github u mnie nie zadziałał, więc metodą prób i błędów tak mi się udało.

Trzeba wyświetlić listę pakietów poleceniem:

M-x package-list-packages 

Znaleźć pakiet clojure-mode. Kliknąć [Install] i zamknąć Emacs.

Krok 4: Przykładowy projekt

Utworzyć projekt poleceniem w terminalu:

lein new clojure-projekt1

Następnie wejść do katalogu projektu i edytować plik konfiguracji project.clj. Emacs by widzieć projekt musi być uruchamiany z katalogu głównego projektu:

cd clojure-projekt1
emacs project.clj

Wkleić tę linijkę:

:plugins [[lein-swank "1.4.4"]]

nad linijkę z :dependencies

W Emacs uruchomić serwer Swank poleceniem:

M-x clojure-jack-in

I voila ;). Powinien się pokazać REPL.

Przykładowe polecenia przydatne przy współpracy z Emacs i SLIME (C - Ctrl, M - Alt):



  • M-. : Skok do definicji var-a
  • M-TAB lub C-c TAB: Autouzupełnianie symbolu w miejscu kursora
  • C-x C-e: Wykonaj wyrażenie pod kursorem
  • C-c C-k: Skompiluj bieżący burfor
  • C-c C-l: Załaduj bieżący bufor i wymuś przeładowanie wymaganych przestrzeni nazw
  • C-M-x: Skompiluj najbardziej zewnętrzne wyrażenie pod kursorem
  • C-c S-i: Podejrzyj wartość
  • C-c C-m: Wykonaj macroexpand dla wyrażenia pod kursorem
  • C-c C-d C-d: Przeszukaj dokumentacje dla var-a
  • C-c C-z: Przełącz się pomiędzy buforem Clojure a REPL
  • C-c M-p: Zmien przestrzeń nazw w REPL dla odpowiedniej z bieżącego bufora
  • C-c C-w c: Wyświetl funkcje wywołujące bieżącą funkcję



  • Dodawanie własnych bibliotek do projektu

    Najpierw trzeba zainstalować Maven2:

    sudo apt-get install maven2

    Następnie trzeba mieć jakąś bibliotekę w bieżącym katalogu, np.  ormlite-core-4.42.jar.
    Poleceniem poniżej dodaje się taką bibliotekę do lokalnego repozytorium mavena:



    mvn install:install-file \
     -Dfile=ormlite-core-4.42.jar \
     -DgroupId=self \
     -DartifactId=ormlite-core \
     -Dversion=4.42 \
     -Dpackaging=jar \
     -DgeneratePom=true



    Po pozytywnym przetworzeniu powinien pojawić się napis BUILD SUCCESSFUL. Następnie do pliku projektu w sekcji :dependencies trzeba dodać linijkę i zapisać:

    [self/ormlite-core "4.42"]

    Następnie w katalogu projektu w terminalu dać polecenie:

    lein deps

    I to wszystko. ;)




    2012-12-17

    Light Table : Factorial fun

    Niedawno na kickstarter.com wystartował projekt IDE dla języka Clojure przez człowieka o nazwisku Chris Granger. Dzisiaj zaprezentuję działanie części edytora: Instarepl. Jest to moduł, w którym można testować kod. Ma tę przewagę nad innymi REPL-ami, że widać wyniki działania poszczególnych poleceń. Nie tylko po wykonaniu polecenia, ale też aktualizację innych w historii jeżeli wcześniejszy kod coś pozmienia. Jak dla mnie bajka. Dawno czegoś takiego nie widziałem. Ekran Instarepl jest podzielony na dwa obszary. Z lewej strony mamy edytor kodu, z prawej kod, i wyniki działanaia. W wynikach przez ciała funkcji zamiast symboli przewijają się dane i widać jak przechodzą przez kolejne polecenia. W przypadku pętli zauważyłem, że widać tylko ostatnią iterację.

    Light table można pobrać z tej strony. Wymagane jest JDK Javy. Przy pierwszym uruchomieniu program pobiera niezbędne biblioteki. Proces ten trwa dość długo, więc nie należy się przerażać, że aplikacja "wisi". Najlepiej za pierwszym razem uruchomić w konsoli poleceniem "java -jar launcher.jar",  bo będzie widać przebieg instalacji. Program także doinstalowuje biblioteki przy przejściu do części projektowej : Table.

    Widok główny Light Table


    Widok Instarepl

    Jak widać na zrzucie ekranu Instarepl wyjście funkcji, które piszą do standardowego wyjścia (terminal/konsola) umieszczane jest na końcu bloku wyników pod linijką "Output:".

    Część Table wygląda tak:


    Z lewej pokazują się bloki funkcji, które można edytować. Polecam zapoznać się ze skrótami klawiszowymi, bo w widoku nie ma żadnych przycisków, czy menu z komendami.

    Z prawej strony ekranu na górze w lewym okienku jest widok przestrzeni nazw. Każda przestrzeń nazw zawiera funkcje, które wyświetlają się w prawym okienku. Niżej na czerwono jest brudnopis do testowania kodu, a jeszcze niżej w zielonym polu pojawiają się wyniki działania funkcji z brudnopisu lub programu. Widać, że autor wzorował się na browserze kodu Smalltalka. Według mnie to dobre rozwiązanie, a przydałaby się jeszcze możliwość podziału funkcji na własne kategorie.

    Dodam, że to IDE jest w wersji alpha, więc zawiera bugi i nie jest tak rozbudowane jak można by sobie życzyć (np. brak refaktoringu).  Jednak w porównaniu do wtyczek Clojure do Eclipse czy Intellij IDEA program ten to ogromny krok naprzód i z przyjemnością się z nim pracuje.

    Na koniec załączam gwóźdź programu, czyli film z testowania Instarepl, na przykładzie realizacji funkcji liczącej silnię z testami ze strony 4Clojure.


    Pod koniec filmu mam małą przywieszkę z tego względu, że nie zauważyłem błędu w funkcji, a środowisko czasem lubi się przywiesić. Np. w wyniku edycji funkcji, program  wpadnie w nieskończoną pętlę. Ten fakt można poznać po zwiększonym zużyciu procesora, lagowaniu interfejsu i braku reakcji na zmiany kodu (factorial3 zawsze pokazywał 1). To mnie wprowadziło w błąd.

    Projekt Light Table jest bardzo ciekawy, więc mam nadzieję, że nie tylko mnie przypadnie do gustu. :)

    2012-12-11

    Smalltalk : Seaside z użyciem Pharo, cz.3

    (c) Robert Tinney

    Mam już schemat jak wyciągnąć komentarze z dowolnego bloga, to teraz czas na część wizualną. Tak jak poprzednio, najpierw z grubsza opiszę poszczególne kroki. Film z podsumowaniem dam na końcu. Jako, że film byłby dość długi gdyby pokazywać tworzenie kodu to pokażę w nim tylko jak wygląda struktura pakietu i kod. Z poprzedniej części mam już w zasadzie napisaną podstawę pojedynczej funkcji, która odczyta komentarze z podanego adresu URL i zwróci tablicę komentarzy. Najpierw jednak opiszę jak utworzyć komponent w Seaside.
    Otwieram System Browser i zaczynam od utworzenia kategorii (paczki obiektów). Prawy myszki na liście kategorii i klikam "Add category...". Jako, że tworzę pod portal dobreprogramy.pl, utworzę kategorię DobreprogramyAPI.

    Tworzenie komponentu głównego

    Ten komponent będzie odpowiadać za zawartość strony głównej. To wszystko o czym będę pisać jest także dostępne dostępne w dokumentacji Seaside.. Kasą główną komponentów jest klasa WAComponent. Po niej odziedziczę funkcjonalność na potrzeby mojej nowej klasy "DPRoot":

    WAComponent subclass: #DPRoot
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'DobreprogramyAPI'

    Pierwsze co trzeba zrobić, to utworzyć metodę "initialize":

    initialize
       super initialize.

    Metoda "initialize" jest metodą wywoływaną zaraz po utworzeniu obiektu, m.in. po wywołaniu metody "new". Dobrą praktyką jest wywołać metodę "initialize" obiektu nadrzędnego za pomocą metody "super". Zielona strzałka do góry, która się pokaże obok nazwy metody mówi, że moja metoda nadpisuje metodę z klasy nadrzędnej.
    Kolejnym krokiem będzie utworzenie metody, która wyświetli coś na stronie. Na chwilę obecną dam jakiś prosty tekst byle zobaczyć, czy działa. Metoda ta posiada określona nazwę, bo Seaside przeszukując obiekty i wyświetlając zawartość szuka właśnie jej:

    renderContentOn: html
       html paragraph: 'Alibaba'.

    Jako, że obiekt DPRoot jest obiektem głównym, trzeba zaznaczyć to tworząc metodę, która poinformuje framework Seaside. W części klasowej trzeba utworzyć metodę:

    canBeRoot
       ^ true.

    Dzięki niej Seaside będzie widzieć obiekt w przeglądarce aplikacji.

    Rejestracja komponentu jako aplikacji

    W przeglądarce przechodzę pod adres http://localhost:8080/config . Klikam w Add na pasku menu. W pole wpisuję 'dp' i wybieram typ jako Application. OK. W sekcji General na kolejnej stronie jako 'Root class' wybieram DPRoot. Klik w Apply. Teraz przechodząc pod adres localhost:8080/dp powinien się wyświetlić Alibaba... Jest. ;)
    To samo można zrobić wywołując w Workspace polecenie:

    WAAdmin register: DPRoot asApplicationAt: 'dp'.

    2012-12-08

    Clojure Easy 2


    "The computing scientist’s main challenge is not to get confused by the complexities of his own making."
     --- E. W. Dijkstra

    Czas na kolejną część zagadek ze strony 4clojure.com.


    Problem 9: Fibonacci Sequence


    Zadanie polega na napisaniu funkcji, która zwróci n pierwszych elementów ciągu Fibonacci.
    Funkcja w E1 bierze za parametr szukaną liczbę elementów ciągu. Dla pierwszego i drugiego elementu zwracane są wartości bezpośrednio. Dla dalszych obliczane są kolejne elementy na podstawie dwóch pierwszych elementów listy acc. W pętli zwiększany jest licznik c i dodawana do początku listy acc suma dwóch poprzednich elementów. Trzeba pamiętać, że funkcja conj dodaje elementy do list od lewej strony, dlatego aby uzyskać poprawną odpowiedź trzeba acc odwrócić przed zwrotem. Odpowiedź E2 jest z wykorzystaniem wektora.

    T1: (= (__ 3) '(1 1 2))
    T2: (= (__ 6) '(1 1 2 3 5 8))
    T3: (= (__ 8) '(1 1 2 3 5 8 13 21))

    E1: (fn fiblist [n]
          (let [a '(1) b '(1 1)]
              (cond (= n 1) a
                   (= n 2) b
                   :else (loop [c 2 acc b]
                        (if (>= c n)
                            (reverse acc)
                            (recur (inc c)
                                   (conj acc (+ (first acc) (second acc)))))))))

    E2 : (fn fiblist [n]
            (let [a [1] b [1 1]]
                (cond (= n 1) a
                      (= n 2) b
                      :else (loop [c 2 acc b]
                                 (if (>= c n)
                                  acc
                                 (recur (inc c) 
                                        (conj acc (+ (acc (- c 1)) (acc (- c 2))))))))))


    Problem 10: Maximum value


    Zadanie polega na napisaniu funkcji, która zwróci parametr o największej wartości.
    Haczyk: nie można użyć funkcji max i max-key.

    Zanim podam wynik wyjaśnię na przykładzie jak to jest z przekazywaniem argumentów do funkcji:

    => ((fn f [a b & more] more) 1 2 3 4)
    (3 4)
    => ((fn f [a & more] more) 1 2 3 4)
    (2 3 4)
    => ((fn f [& more] more) 1 2 3 4)
    (1 2 3 4)

    Przed znakiem & argumenty przypisywane są jak leci 1 do a, 2 do b, itd. Po znaku & reszta argumentów traktowana jest jako lista. W przypadku braku identyfikatorów przed & wszystkie argumenty traktowane są jako lista. Oczywiście a, b, more i args mogą mieć dowolne nazwy.

    T1: (= (__ 1 8 3 4) 8)
    T2: (= (__ 30 20) 30)
    T3: (= (__ 45 67 11) 67)

    E1:(fn maxx [& args]
                (loop [e (first args)
                       re (rest args)
                       mx e]
                      (if (empty? re)
                           mx
                          (recur (first re) (rest re) (if (> e mx) e mx)))))

    Problem 11: Get the Caps


    Zadanie: Znaleźć funkcję, która z podanego łańcucha znaków zwróci łańcuch zawierający tylko wielkie litery. Łańcuchy tekstowe w Clojure to zwykłe String z Javy, więc będą na nich działać funkcje z Javy. Aby dostać listę wielkich liter trzeba przefiltrować tekst za pomocą funkcji filter, którą następnie trzeba złączyć w napis przy użyciu funkcji str i apply.


    T1: (= (__ "HeLlO, WoRlD!") "HLOWRD")
    T2: (empty? (__ "nothing"))
    T3: (= (__ "$#A(*&987Zf") "AZ")

    E1: #(apply str (filter (fn [c] (Character/isUpperCase c)) %))

    Problem 12: Intro to some


    Należy podać wynik działania funkcji some. Funkcja ta bierze za pierwszy parametr funkcję, która zwraca logiczną prawdę lub fałsz , oraz kolekcję, której elementy będą przekazywane do tej funkcji. Jeżeli wynikiem działania funkcji będzie true to zostanie zwrócony pierwszy element dla którego warunek został spełniony.


    T1: (= __ (some #{2 7 6} [5 6 7 8]))
    T2: (= __ (some #(when (even? %) %) [5 6 7 8]))

    E1: 6

    Problem 13: Duplicate a sequence


    Zadanie polega na napisaniu funkcji, która powtórzy elementy sekwencji dwukrotnie.

    T1: (= (__ [1 2 3]) '(1 1 2 2 3 3))
    T2: (= (__ [:a :a :b :b]) '(:a :a :a :a :b :b :b :b))
    T3: (= (__ [[1 2] [3 4]]) '([1 2] [1 2] [3 4] [3 4]))

    E1: #(if (empty? %1) []
             (loop [a (first %1) r (rest %1) ac [a a]]
                  (if (empty? r) ac
                      (let [s (first r)]
                           (recur s (rest r) (conj ac s s))))))

    E2: #(interleave %1 %1)


    Problem 14: Implement range


    Zadanie polega na napisaniu odpowiednika funkcji range .

    T1: (= (__ 1 4) '(1 2 3))
    T2: (= (__ -2 2) '(-2 -1 0 1))
    T3: (= (__ 5 8) '(5 6 7))

    E1: (fn [s e]
            (loop [c s acc []]
                 (if (>= c e) 
                  acc
                 (recur (inc c) (conj acc c)))))