niedziela, 11 marca 2012

Vim Powerline

Czy nie zdarza się ci czasami zapomnieć w jakim trybie się aktualnie znajdujesz? Problem ten dotyczy zwłaszcza początkujących użytkowników ale i starszym się zdarza. Jest na to rada - plugin Powerline. Wygląda to tak, że każdy tryb ma swój własny kolor linii statusu, a do tego twórcy wtyczki zadbali o jej ładny wygląd. Tylko spójrz:
Prawda, że ładny?

To teraz do rzeczy. Instaluje się go nieco trudniej niż pozostałe pluginy ale idea jest ta sama. Możemy go wprost ściągnąć przez Vundle, o którym pisałem wcześniej lub po prostu użyć gita:

$ git clone https://github.com/Lokaltog/vim-powerline.git

Po czym kopiujemy pliki standardowo do katalogu ~/.vim.

Do pluginu jest dołączony skrypt o nazwie fontpatcher. Tworzy on dodatkowe znaki w czcionce, której używamy w gVimie. Uruchomienie tego skryptu na naszej czcionce jest niezbędne jeżeli chcemy mieć "trójkąty" w linii statusu. Bez tego będą to po prostu prostokąty o różnych kolorach (bez zakończenia trójkątnym znaczkiem).

Jeżeli chcemy użyć fontpatchera to robimy to w ten sposób:
1. Przechodzimy do katalogu ~/.fonts, gdzie powinna się znajdować czcionka, której używamy w gVimie. Jeżeli jej tam nie ma to ją tam kopiujemy.
2. Uruchamiamy skrypt:
$ /path/to/fontpatcher MyFontFile.ttf
3. Aktualizujemy cache czcionek:
$ sudo fc-cache -vf
4. Dorzucamy do pliku .vimrc linijkę:
let g:Powerline_symbols = 'fancy'
5. Upewniamy się, że plik cache został usunięty:
$ rm /tmp/Powerline.cache

To prawie wszystko. Pozostaje nam jedynie dodać jeszcze jedną opcję do ustawień Vima. Zatem otwieramy ponownie plik .vimrc i dopisujemy:
set laststatus=2
Bez ustawienia tej opcji, nasz pasek statusu byłby dostępny jedynie w momencie otwarcia co najmniej 2 okien (ang. split).

To tyle!

wtorek, 14 lutego 2012

Vundle

Ostatnio poszukiwałem dobrego managera pluginów do Vima. Głównie z tego względu, że mam ich co raz więcej oraz wiele z nich przestałem używać, a nadal leżą gdzieś w ~/.vim. Szukałem czegoś co pozwoliłoby mi nad nimi zapanować oraz potrafiło w łatwy sposób je aktualizować (nie śledząc w kółko www.vim.org).

Managerów dostępnych jest kilka. Na pierwszy ogień poszedł VAM. Niestety po kilku próbach się poddałem. Za każdym razem ściągał on pluginy bezpośrednio do katalogu domowego co mnie bardzo irytowało. Pewnie da się to skonfigurować ale nie miałem już ochoty na sprawdzanie źródeł, a w dokumentacji nic nie znalazłem.

Szukałem więc dalej. I tak przyszła pora na Vundle. Zainstalowałem zgodnie z instrukcją (dostępną na stronie pluginu) i od razu zabrałem się do instalowania moich ulubionych dodatków. Bardzo pomocna jest w tym momencie komenda :BundleSearch nazwa. Po jej wykonaniu dostajemy listę wtyczek podobnie "brzmiących" jak nazwa, którą wpisaliśmy. Wybierając kursorem linię z interesującym nas pluginem wciskamy klawisz i aby go zainstalować.


Jeżeli chcemy sobie zaktualizować dany plugin to wpisujemy :BundleInstall! nazwa, a jeżeli usunąć to :BundleClean nazwa.

Największą dla mnie zaletą jest to, że wpisując w pliku .vimrc Bundle 'nazwa' sprawiamy, że dany plugin jest wymagany, a co za tym idzie jeżeli go nie ma to zostanie automatycznie zainstalowany. W ten sposób ustawianie Vima na innej maszynie sprowadza się do przekopiowania pliku .vimrc oraz zainstalowania Vundle. Polecam!

wtorek, 10 stycznia 2012

O czystości języków programowania

Dzisiaj przybliżę nieco pojęcie czystości w odniesieniu do języków programowania, a później do samego Haskella. Określenia "czysty" używa się do dwóch typów języków: funkcyjnych oraz obiektowych. Zacznę może od tych drugich, gdyż jest to łatwiej wytłumaczyć.

Otóż czysty język obiektowy to taki, w którym każdy element języka jest albo obiektem, klasą albo metodą (czyli funkcją w klasie) albo atrybutem klasy (czyli zmienną w klasie). Co to oznacza w praktyce? Oznacza to tyle, że nawet instrukcja warunka if jest metodą w jakiejś klasie. Nie będę się nad tym dużo rozpisywał, podam jedynie, że przykładem takiego języka jest SmallTalk.

W językach funkcyjnych natomiast sprawa rozbija się o funkcje. Określenie danego języka językiem czysto funkcyjnym sugeruje, że każdy element języka jest funkcją. Co to oznacza? Oznacza to tyle, że funkcja jako argument przyjmuje funkcje a to sprawia, że nie możemy ich traktować jak zmienne (wyrażenie a :: Int nie jest zmienną tylko funkcją, która nie ma żadnych argumentów a zwraca typ Int). Nie możemy również tworzyć pętli, z uwagi na to, że w świecie funkcyjnym nie mamy kolejności wykonywania obliczeń. W związku z tym nie możemy obliczyć jednej rzeczy, a później przeskoczyć do linijki wyżej (funkcje potrzebne do obliczenia linijki wyżej mogły nie zostać jeszcze obliczone).

Jest jeszcze jeden aspekt najczęściej źle rozumiany. Funkcja nie może mieć efektów ubocznych. Co to są efekty uboczne? Ano są to niespodziewane rezultaty wykonania funkcji. Kluczowe jest słowo "niespodziewane". Na przykład weźmy sobie taką funkcję: toInt :: String -> Int. Zastanówmy się czego możemy się spodziewać po tej funkcji a czego nie. Patrząc po nazwie i jej typie spodziewamy się, że funkcja sparsuje sobie łańcuch znaków i zwróci liczbę jemu odpowiadającą. Nie spodziewamy się natomiast, że funkcja wykona zapytanie w bazie danych i zwróci wynik tego zapytania (np. "SELECT '123'::integer"). Niespodziewane jest również wypisanie czegoś na ekranie bądź narysowanie okienka czy cokolwiek innego. Zatem język czysty funkcyjnie nie powinien na to zezwalać.

Powszechne jest twierdzenie, że problem efektów ubocznych rozwiązano w Haskellu monadami. To nie do końca jest prawda. Za pomocą monad rozwiązano problem z IO, a nie same efekty uboczne jako takie. Przypuśćmy kontynuując przykład przytoczony powyżej, że chcielibyśmy napisać funkcję toIntDirty, która dokonywała by konwersji stringa na liczbę stosując zapytanie do bazy danych pokazane wyżej. Musielibyśmy więc sprawić aby patrząc nad typ tej funkcji spodziewalibyśmy się tej sytuacji. Trzeba więc to jakoś zaznaczyć. W Haskellu stosuje się w tym celu opakowanie danego typu w inny. Robi się to np. w taki sposób:

newtype Dirty a = Dirty a

Zatem nasza funkcja miała by typ toIntDirty :: String -> Dirty Int. Ktoś może zapytać co stoi na przeszkodzie aby stosować tą samą technikę w językach imperatywnych? Nic. Problem jest w tym, że kompilator tego na nas nie wymusi (bo język tego nie wymusza). Zauważmy, że funkcja, która wywołuje naszą funkcję toIntDirty musi również zaznaczyć, że podczas jej wykonania mogą się dziać "brudne" rzeczy. Haskell dlatego właśnie jest czystym językiem, gdyż można to wymusić. Dla jaśniejszej sytuacji weźmy taki kod w C:

typedef struct { int result; } DirtyInt;

DirtyInt toIntDirty (const char* text);

int toInt (const char* text)
{
    return toIntDirty(text).result;
} 

Ten kod jest w pełni legalny w C i nie ma możliwości aby wymusić na programiście, że funkcja toInt powinna zwracać DirtyInt, ponieważ w środku wykonuje "nieczystą" funkcję. W Haskellu natomiast, tak jak już to wspominałem, można wymusić aby programista nie mógł się "pozbyć" typu Dirty jak już go użyje. Robi się to tak:

module Type.Dirty where 
    (
      Dirty
    )

newtype Dirty a = Dirty a

Od teraz aby użyć typu Dirty musimy zaimportować moduł Type.Dirty. Zauważmy, że przy where nie napisałem Dirty(..) lecz Dirty. Zapis z kropkami powoduje wyeksportowanie wszystkich konstruktorów danego typu. Gdybym tak zrobił to legalny byłby zapis:

module Test where

import Type.Dirty

cleanDirty :: Dirty a -> a
cleanDirty (Dirty a) = a

a tego nie chcemy. W momencie jednak gdy nie ma tych kropek to powyższa funkcja się nie skompiluje. Tworzy to jednak kolejne problemy. Otóż pozostawiając to w ten sposób nie będziemy mogli wywołać żadnej "czystej" (mam tu na myśli bez typu Dirty) funkcji. Dla przykładu weźmy wspomnianą wcześniej funkcję toIntDirty, która zwraca typ Dirty Int. Chcielibyśmy teraz przemnożyć tego Inta przez, dajmy na to, liczbę 2. Nie da się. Nie zadziała np. coś takiego:

multiply :: Dirty Int -> Dirty Int
multiply a = a*2

ponieważ funkcja (*) nie działa na typie Dirty Int. Nie zadziała również to:

multiply2 :: Dirty Int -> Int
multiply2 (Dirty a) = a*2

z powodu wspomnianego wyżej (nie wyeksportowany konstruktor). Aby rozwiązać ten problem z pomocą przychodzą nam właśnie monady. Umożliwiają one dwie rzeczy. Po pierwsze pozwalają zachować kolejność obliczeń, a po drugie umożliwiają nam wywoływanie "czystych" funkcji na opakowanych typach. Monady reprezentowane są przez klasę Monad, której instancję sobie napiszemy do naszego typu Dirty:

module Type.Dirty where 
    (
      Dirty
    )

newtype Dirty a = Dirty a

instance Monad (Dirty a) where
    return a = Dirty a
    (Dirty a) >>= f = f a

Klasa Monad zawiera w sumie cztery funkcje ale do utworzenia instancji wystarczy tylko zdefiniować return oraz >>=. Funkcja return opakowuje zwykły typ w Dirty, np. return (10::Int) w naszym przypadku spowoduje utworzenie typu Dirty Int. Ciekawsza jest funkcja >>=. Ma ona typ Monad m => m a -> (a -> m b) -> m b co oznacza, że odpakowuje ona na chwilę z typu Dirty i przekazuje odpakowany typ (np. gdy mamy typ Dirty Int to odpakowany typ to Int) do funkcji f, a wynik tej funkcji musi być typu Dirty (ale nie koniecznie tego samego). W ten sposób możemy teraz napisać zdefiniować nasze mnożenie:

module Test where 

import Type.Dirty

toIntAndMultiplyDirty :: String -> Dirty Int
toIntAndMultiplyDirty text = toIntDirty text >>= return . (* 2)

W toIntAndMultiplyDirty tworzymy z tekstu liczbę i mnożymy ją przez 2 stosując w tym celu "czystą" funkcję (*). No dobrze, a w jaki sposób monady pozwalają osiągnąć zachowanie kolejności obliczeń? Bardzo prosto. Zauważmy, że w ciągu wywołań funkcji (>>=) każda funkcja po prawej stronie wymaga jako argumentu wyniku funkcji stojącej po lewej. Zatem zawsze zostanie wymuszone liczenie wartości wyrażenia zgodnie z kolejnością od lewej do prawej.

Wracając do IO, właśnie kolejność obliczeń była kluczowa. Wyobraźmy sobie, że chcemy wypisać dwie linijki jedna pod drugą. Gdyby typ IO nie był monadą wymuszającą kolejność wykonywania akcji to nie byłoby jasne i zgodne z intuicją, który napis wyświetli się jako pierwszy. Często natrafiałem na tezę, że funkcja nie jest czysta, ponieważ "kontaktuje się" ze światem zewnętrznym (np. wypisuje coś na ekranie). Otóż funkcja nie byłaby czysta, gdyby efekty jej wykonania były niespodziewane. Jeżeli jakaś funkcja zwraca typ IO to spodziewamy się, że kontaktuje się ona ze światem zewnętrznym, dokładnie tak samo spodziewamy się, że jak jakaś funkcja zwraca typ String to wykonuje ona operacje na łańcuchach znaków.

środa, 28 grudnia 2011

Mosty w grafie - Haskell

Zbliża się powrót do szkoły więc trzeba powrócić do implementowania algorytmów. Jako projekt do wykonania dostałem napisanie algorytmu znajdującego mosty w grafie i działającego w czasie O(n+m), gzie n to liczba wierzchołków, a m to liczba krawędzi. Moje upodobania na szczęście się przez święta nie zmieniły więc napisałem to w Haskellu.

Algorytm znajdowania mostów wykorzystuje algorytm DFS. Zmiany są niewielkie, a mianowicie:
  • przeglądając dany wierzchołek nadajemy mu od razu kolejny numer,
  • nadajemy mu także początkową wartość low taką samą jak numer wierzchołka,
  • dalej postępujemy jak w algorytmie DFS, czyli przeglądamy wszystkie sąsiadujące wierzchołki i jeżeli są nieodwiedzone to wrzucamy je na stos po czym wywołujemy procedurę rekurencyjnie z nowym stosem,
  • wracając z rekurencji sprawdzamy wszystkie sąsiadujące wierzchołki, różne od tego, z którego przyszliśmy (czyli różne od ojca) i jeżeli sąsiad był odwiedzony i ma wartość low mniejszą od wierzchołka, w którym jesteśmy to zmieniamy naszą wartość low na wartość tego sąsiada.

Po wykonaniu tej procedury, wierzchołki, które mają numer równy low tworzą mosty.

Algorytm stanie się bardziej jasny jak przeanalizujemy kod:


Linie od 7 do 10 definiują nam typy danych, tak aby się nam lepiej programowało. Wierzchołek (Vertex) ma jak widać jakiś numer, jakąś listę krawędzi oraz jakąś wartość low. Krawędź to po prostu liczba, a most z kolei to para liczb oznaczająca parę wierzchołków tworzących jeden most.

Funkcja findBridges przyjmuje tablicę wierzchołków i zwraca listę mostów. Dalej treść funkcji czytamy od końca. Zatem wywołujemy funkcję dfsBridges dla pierwszego wierzchołka i naszej tablicy, która uzupełni nam podanym wyżej algorytmem wartości low oraz numery wierzchołków i zwróci te dane jako nową tablicę wierzchołków. Funkcja assocs zamieni nam tablicę na listę par typu (indeks, Vertex). Przeglądamy tą listę (foldr) i dla każdej pary sprawdzamy czy wartość low jest równa numerowi wierzchołka (czyli wyszukujemy wierzchołki tworzące mosty). Jeżeli nie to pomijamy ten wierzchołek. Natomiast jeżeli tak to bierzemy z listy sąsiadów tylko te wierzchołki, które mają wartość low różną od wartości low danego wierzchołka (filter). Dalej z tej listy tworzymy pary (mosty) w ten sposób: bierzemy numer naszego wierzchołka jako pierwszą liczbę w parze oraz dany element uzyskanej przed chwilą listy jako drugą liczbę (zip). Na koniec zostaje nam dołączenie tej listy do listy już uzyskanej.

Funkcja dfsBridges przyjmuje "stos" (w naszym przypadku po prostu listę) indeksów wierzchołków, ich tablicę oraz aktualny numer do przydzielenia. Zwraca natomiast uzupełnioną tablicę wierzchołków o numer i wartość low. Tą funkcję zaczynamy czytać od where. Na początku inicjalizujemy wartości naszego wierzchołka czyli przydzielamy mu numer oraz low równy aktualnemu numerowi - wartość d. Odbywa się to w ten sposób, że tworzymy nową tablicę wierzchołków (initArr), która zawiera te same dane za wyjątkiem naszego wierzchołka (//). Dalej obliczamy nowy "stos" przeglądając sąsiadów aktualnego wierzchołka i jeżeli któryś z nich nie był jeszcze odwiedzony (czyli ma numer większy niż -1) to wrzucamy go na koniec listy.

Kolejnym krokiem jest wywołanie naszej funkcji rekurencyjnie z nowym "stosem" i nową tablicą oraz kolejnym numerem. Jak przypadkiem skończy się nam "stos" (czyli lista będzie pusta, czyli wszystkie wierzchołki zostały odwiedzone) to zwracamy uzyskaną tablicę wierzchołków. Wracając z rekurencji przeglądamy sąsiadów naszego wierzchołka ale z nowo uzyskanej tablicy. Dalej jest już jak w algorytmie czyli jak któryś z sąsiadów był odwiedzony i nie jest to wierzchołek, z którego przyszliśmy oraz ma mniejszą wartość low to bierzemy jego wartość low. Na koniec zmieniamy tablicę tak aby nasz wierzchołek uzyskał tą nową wartość low i ją zwracamy.

Jeżeli ktoś jeszcze nie za bardzo rozumie co to jest ta wartość low to w skrócie można powiedzieć, że jeżeli napotkamy cykl w grafie to wszystkim wierzchołkom w tym cyklu nadajemy jednakową wartość low (obrazowo można powiedzieć, że kolorujemy cykle w grafie).

To właściwie wszystko. Czekam na komentarze.

poniedziałek, 26 grudnia 2011

Parsowanie plików pcap w Haskellu

Dzisiaj przykład prostego parsowania w Haskellu. Zadanie to znalazłem na stronie firmy Tsuru Capital jako code sample do napisania, gdybyśmy ubiegali się o stanowisko programisty. Opis zadania znajduje się tutaj. Jest tam dostępny także przykładowy plik z danymi w formacie pcap.

W skrócie to co mamy zrobić to:
  • wyciągnąć z pliku linie, które zaczynają się od znaków B6034,
  • daną linię sparsować,
  • i wypisać ją w ten sposób:
       @ ... @ @ ... @
  • To jak dokładnie wygląda linia danych jest opisane na stronie z opisem zadania. Generalnie wystarczy wypisać tylko część danych z linii oraz w nieco innej kolejności.
  • uruchamiając nasz program z parametrem "-r" powinien on sortować dane po zawartej w linii informacji "accept-time". Dla ułatwienia podana jest informacja, że różnica między czasem wysłania pakietu (czyli pkt-time) a accept-time nigdy nie jest większa niż 3 sekundy.

No to zaczynamy. Na początku warto wspomnieć o pakiecie Network.Pcap, który ułatwi nam czytanie z takich plików. Jedyne co nam pozostaje zrobić to parsowanie linii, ewentualne jej sortowanie oraz wypisanie przeczytanych danych. Zacznijmy od zdefiniowania sobie głównych typów danych:

module Main where

import Network.Pcap
import qualified Data.ByteString.Char8 as BS

data MarketData = MarketData {
      issueCode  :: BS.ByteString
    , bids       :: [(BS.ByteString, BS.ByteString)]
    , asks       :: [(BS.ByteString, BS.ByteString)]
    , acceptTime :: AcceptTime
    }

data AcceptTime = AcceptTime {
      hh :: BS.ByteString
    , mm :: BS.ByteString
    , ss :: BS.ByteString
    , uu :: BS.ByteString
    }

Jako, że pakiet Network.Pcap dostarcza nam danych w ByteStringach to wszędzie będziemy ich używać. Na uwagę zasługują pola bids i asks w MarketData. Otóż są to pary, w których pierwsza wartość to cena a druga to liczba danego towaru. Stworzone wyżej typy danych reprezentują jedną linię w pliku z danymi. Teraz sprawmy aby się automatycznie dobrze wypisywały:

instance Show AcceptTime where
    show (AcceptTime h m s u) = (BS.unpack h) ++ ":"
                                              ++ (BS.unpack m)
                                              ++ ":"
                                              ++ (BS.unpack s)
                                              ++ "."
                                              ++ (BS.unpack u)

instance Show MarketData where
    show (MarketData i b a t) = (show t)
        ++ " " ++ (BS.unpack i)
        ++ " " ++ (drop 1 $ foldr (\(ty, pr) ac ->
            "@" ++ (BS.unpack ty) ++ "@" ++ (BS.unpack pr) ++ ac) [] b)
        ++ " " ++ (drop 1 $ foldr (\(ty, pr) ac ->
            "@" ++ (BS.unpack ty) ++ "@" ++ (BS.unpack pr) ++ ac) [] a)

W ten sposób wystarczy wywołać funkcję show na typie MarketData i otrzymamy linię opisaną w zadaniu.

Przechodzimy zatem do najtrudniejszej części czyli parsowania danych. W pakiecie Network.Pcap dostępna jest funkcja dispatch, która przyjmuje otworzony plik (może to być również gniazdo sieciowe), liczbę pakietów do przeczytania (w naszym przypadku -1 czyli cały plik) oraz funkcję, która będzie przetwarzać dany pakiet o typie PktHdr -> ByteString -> IO (). Zanim jednak przejdziemy do napisania funkcji parsującej przypomnijmy sobie warunek czytania danego pakietu. Otóż musi się on zaczynać od znaków B6034, a to z kolei oznacza, że mogą pojawić się pakiety, które tego warunku spełniać nie będą. W takim wypadku musimy je pominąć. Napiszmy więc funkcję sprawdzającą ten warunek i jeżeli on zachodzi to parsujemy daną linię i ją wyświetlamy, a jeżeli nie to nic nie robimy. Nazwijmy ją showData:

prefix :: BS.ByteString
prefix = (BS.pack "B6034")

showData :: PktHdr
         -> BS.ByteString
         -> IO ()
showData hdr dat = do
    let (_, _d) = BS.breakSubstring prefix dat
        (sec, msec) = (hdrTime hdr) `divMod` 1000000
    if (BS.length _d) > 0
        then putStrLn $ (show $ TOD (toInteger sec) (toInteger (msec*1000000)))
            ++ " " ++ (show $ parseData _d)
        else return ()

To wszystko co występuje przed wywołaniem funkcji parseData to wyświetlenie po prostu pkt-time, który nie jest zawarty w danych ale w nagłówku pakietu. Sprawdzanie odbywa się w ten sposób, że dzielimy linię na dwie części: tam gdzie występuje prefix i to co przed nim. Jeżeli uda się podzielić linię w ten sposób to znaczy, że możemy parsować:

parseData :: BS.ByteString
          -> MarketData
parseData line = MarketData (BS.take 12 l) _bids _asks (AcceptTime h m s u)
  where
    l = BS.drop 5 line
    (_bids, r1) = takeBids $ BS.drop 24 l
    (_asks, r2) = takeAsks $ BS.drop 7 r1
    (h, r3) = BS.splitAt 2 (BS.take 8 $ BS.drop 50 r2)
    (m, r4) = BS.splitAt 2 r3
    (s, u) = BS.splitAt 2 r4
 
takeBids :: BS.ByteString
         -> ([(BS.ByteString, BS.ByteString)], BS.ByteString)
takeBids bs = takeBids' bs (5::Integer)
  where
    takeBids' b n
        | n > 0     = let (_b, _r) = takeBids' (BS.drop 12 b) (n-1)
                      in (_b ++ [(BS.splitAt 5 $ BS.take 12 b)], _r)
        | otherwise = ([], b)
 
takeAsks :: BS.ByteString
         -> ([(BS.ByteString, BS.ByteString)], BS.ByteString)
takeAsks bs = takeAsks' bs (5::Integer)
  where
    takeAsks' b n
        | n > 0     = let (_b, _r) = takeAsks' (BS.drop 12 b) (n-1)
                      in ((BS.splitAt 5 $ BS.take 12 b):_b, _r)
        | otherwise = ([], b)

Funkcja parseData jest bardzo prosta. Pobiera ona kolejną konkretną ilość znaków z linii i wrzuca je odpowiednio do konstruktora MarketData. Np. jako pierwszy parametr konstruktor przyjmuje issueCode, który według opisu zadania ma długość 12 znaków. W tym celu najpierw pomijamy pierwsze 5 znaków warunkowych (czyli B6034) po czym bierzemy kolejne 12 właśnie jako issueCode itd. Na uwagę zasługują jeszcze dwie funkcje takeBids oraz takeAsks. Są one niemal identyczne - różnią się kolejnością dodawania czytanych elementów do listy. Pierwsza odwraca kolejność a druga pozostawia ją bez zmian. Obie natomiast czytają dane rekurencyjnie biorąc 12 znaków i dzieląc je na dwie listy od 1 do 5 znaku oraz od 6 do 12. Powtarzają tą czynność tak długo aż uzyskają 5 par danych (parametr n).

Pozostaje nam tylko dopisanie pozostałych funkcji odpalających cały mechanizm:

readQuote :: Bool
          -> PcapHandle
          -> IO ()
readQuote False h = dispatchBS h (-1) showData >> return ()
readQuote True h = return ()
 
main :: IO ()
main = do
    opt <- liftA parseOptions getArgs
    hdl <- openOffline $ filename opt
    readQuote (quoteOrder opt) hdl
 
data Options = Options { filename :: FilePath, quoteOrder :: Bool }

parseOptions :: [String]
             -> Options
parseOptions [] = Options "" False
parseOptions (x:xs)
    | x == "-r" = Options (head xs) True
    | otherwise = Options x False

Dodałem tu nowy typ danych Options, który odpowiada za parametry programu, takie jak nazwa pliku oraz czy ma być sortowany. Funkcja openOffline zwraca nam uchwyt do otworzonego pliku pcap, a funkcja readQuote przyjmuje jako parametr czy ma sortować dane (jeżeli tak to nic nie robi) i dalej wywołuje funkcję dispatchBS, która przetwarza dane wywołując showData na każdym pakiecie.

To by było na tyle. Sortowanie oczywiście pozostawiam czytelnikom do samodzielnej realizacji :)

Jakby jednak ktoś chciał zobaczyć jak ja wykonałem sortowanie danych to tutaj jest cały plik.

piątek, 16 grudnia 2011

Vim Hoogle

Dzisiaj coś dla programistów Haskella. Ostatnio znalazłem bardzo ciekawy plugin do Vima integrujący ten wspaniały edytor z równie wspaniałą wyszukiwarką Hoogle. Jeżeli ktoś jeszcze o niej nie słyszał to w skrócie jest to wyszukiwarka, która umożliwia nam znalezienie funkcji haskellowych w dostępnych bibliotekach (z hackage.haskell.org) po nazwie oraz po typie. Dodatkowo po kliknięciu na znalezionej funkcji odsyła nas do jej dokumentacji.

Plugin zwie się vim-hoogle i jest dostępny tutaj. Instaluje się go standardowo - ściągamy sobie archiwum z github i wypakujemy zawartość do katalogu ~/.vim/
Aby plugin działał poprawnie wymagane jest jeszcze zainstalowanie programu hoogle. Najłatwiej jest to zrobić wykorzystując program cabal:

$ cabal install hoogle

Po czym należy jeszcze ściągnąć bazę danych w ten sposób:

$ hoogle data

Na koniec możemy uruchomić Vima i np. wywołać :Hoogle foldr. Po więcej informacji odsyłam do :help hoogle.

sobota, 10 grudnia 2011

Cieniowanie oraz poziome klamry w MetaPost-cie

Ostatnio pisząc pewien referat natknąłem się na problem w LaTeXu. Otóż po pierwsze chciałem narysować sobie prostokąt, którego część będzie wykreskowana (wycieniowana) ukośnymi liniami. Po drugie potrzebowałem narysować pod figurą taką poziomą klamrę z podpisem. Generalnie chciałem uzyskać coś takiego:


Na pierwszy ogień poszedł MetaPost i jak się okazało świetnie się do tego nadaje. Wystarczyła w sumie prosta pętelka (właściwie to dwie) for i wszystko ładnie wyszło. Oto kod odpowiedzialny za cieniowanie:

for i=0 upto width/2: 
  draw (x,y-i*2)--(x+i*2,y);
endfor

for i=1 upto width/2: 
  draw (x+i*2,y2)--(x2,y - i*2);
endfor

Koncepcja jest bardzo prosta. Jeżeli mamy kwadrat do wykreskowania to dzielimy go na dwie części wzdłuż przekątnej, która będzie również kreską. Dzielimy go z tego powodu, że długości kresek rosną do połowy kwadratu a później maleją. Stąd potrzebne są również dwie pętle.

Druga rzecz to pozioma klamra. Długo się naszukałem jak to zrobić więc jak już w końcu znalazłem to się tą wiedzą podzielę. Cały trick polega na wyrenderowaniu zwykłej klamry z czcionki, rozciągnięcie jej i obrócenie. Kod:

label.bot(btex $\lbrace$ etex xscaled 1.5 yscaled 15 rotated 90, (x,y));

Na koniec zamieszczam cały kod jak wygenerować obrazek przedstawiony powyżej:
prologues:=3;
verbatimtex
%&latex
\documentclass{minimal}
\begin{document}
etex
beginfig(0);
s:=20;

draw (350,s+25)--(350,s+125)--(500,s+125)--(500,s+25)--cycle;
draw (350,s+125)--(400,s+75)--(400,s+25);
draw (400,s+75)--(450,s+25);
draw (350,s+75)--(500,s+75) dashed evenly scaled 2;
label.lft(btex $m/2$ etex, (350,s+100));
label.lft(btex $m/2$ etex, (350,s+50));
label.top(btex $p$ etex, (425,s+125));
label(btex $H$ etex, (450,s+100));
label(btex $U_2$ etex, (475,s+50));

for i=0 upto 25: 
  draw (350+i,s+125-i)--(350,s+125-i*2);
endfor

for i=1 upto 25: 
  draw (375+i,s+100-i)--(350+i*2,s+75);
endfor

for i=0 upto 25: 
  draw (350,s+75-i*2)--(350+i*2,s+75);
endfor

for i=1 upto 25: 
  draw (350+i*2,s+25)--(400,s+75 - i*2);
endfor

for i=0 upto 25: 
  draw (400+i,s+75-i)--(400,s+75-i*2);
endfor

for i=1 upto 25: 
  draw (425+i,s+50-i)--(400+i*2,s+25);
endfor

s:= 40;

label.bot(btex $\lbrace$ etex xscaled 1.5 yscaled 15 rotated 90, (425,s));

labeloffset:=5.5mm;
label.bot(btex $U$ etex, (425,s));


endfig;
end