<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5313652849827589697</id><updated>2012-02-20T00:12:32.321-08:00</updated><category term='algorytmy'/><category term='linux'/><category term='vundle'/><category term='pisanie'/><category term='saa7134'/><category term='cvs'/><category term='tsuru'/><category term='xmobar'/><category term='jakość'/><category term='smalltalk'/><category term='latex'/><category term='godność'/><category term='klamra'/><category term='porównanie'/><category term='vimgolf'/><category term='efektywność'/><category term='manager'/><category term='open source'/><category term='monitory'/><category term='xmonad'/><category term='programowanie'/><category term='intelektualna'/><category term='repozytorium'/><category term='edytor'/><category term='makra'/><category term='metapost'/><category term='git'/><category term='plugin'/><category term='pcap'/><category term='powody'/><category term='haskell'/><category term='wolność'/><category term='tuner'/><category term='oprogramowanie'/><category term='języki'/><category term='tv'/><category term='vim'/><category term='projektowanie'/><category term='hoogle'/><category term='graf'/><category term='cieniowanie'/><category term='czystość'/><category term='svn'/><category term='mosty'/><title type='text'>Ubi dubium, ibi libertas</title><subtitle type='html'>Blog programisty na tematy programistyczne a także sporadycznie traktujący o wolności.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-802487083450881512</id><published>2012-02-14T00:36:00.000-08:00</published><updated>2012-02-14T00:36:01.754-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='vundle'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='manager'/><title type='text'>Vundle</title><content type='html'>Ostatnio poszukiwałem dobrego &lt;i&gt;managera pluginów&lt;/i&gt; 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 &lt;i&gt;~/.vim&lt;/i&gt;. 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).&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Managerów&lt;/i&gt; dostępnych jest kilka. Na pierwszy ogień poszedł &lt;a href="http://www.vim.org/scripts/script.php?script_id=2905"&gt;VAM&lt;/a&gt;. Niestety po kilku próbach się poddałem. Za każdym razem ściągał on &lt;i&gt;pluginy&lt;/i&gt; 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. &lt;br /&gt;&lt;br /&gt;Szukałem więc dalej. I tak przyszła pora na &lt;a href="https://github.com/gmarik/vundle"&gt;Vundle&lt;/a&gt;. Zainstalowałem zgodnie z instrukcją (dostępną na stronie &lt;i&gt;pluginu&lt;/i&gt;) i od razu zabrałem się do instalowania moich ulubionych dodatków. Bardzo pomocna jest w tym momencie komenda &lt;i&gt;&lt;b&gt;:BundleSearch nazwa&lt;/b&gt;&lt;/i&gt;. Po jej wykonaniu dostajemy listę wtyczek podobnie "brzmiących" jak nazwa, którą wpisaliśmy. Wybierając kursorem linię z interesującym nas &lt;i&gt;pluginem&lt;/i&gt; wciskamy klawisz &lt;b&gt;i&lt;/b&gt; aby go zainstalować. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://lh3.googleusercontent.com/-4EnLqLpEZlk/TlqXWpgWxOI/AAAAAAAAHRw/oBAl6s1hj7U/vundle-install2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="350" src="https://lh3.googleusercontent.com/-4EnLqLpEZlk/TlqXWpgWxOI/AAAAAAAAHRw/oBAl6s1hj7U/vundle-install2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Jeżeli chcemy sobie zaktualizować dany &lt;i&gt;plugin&lt;/i&gt; to wpisujemy &lt;i&gt;&lt;b&gt;:BundleInstall! nazwa&lt;/b&gt;&lt;/i&gt;, a jeżeli usunąć to &lt;i&gt;&lt;b&gt;:BundleClean nazwa&lt;/b&gt;&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Największą dla mnie zaletą jest to, że wpisując w pliku &lt;i&gt;.vimrc&lt;/i&gt; &lt;b&gt;Bundle 'nazwa'&lt;/b&gt; sprawiamy, że dany &lt;i&gt;plugin&lt;/i&gt; 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 &lt;i&gt;.vimrc&lt;/i&gt; oraz zainstalowania Vundle. Polecam!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-802487083450881512?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/802487083450881512/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2012/02/vundle.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/802487083450881512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/802487083450881512'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2012/02/vundle.html' title='Vundle'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/-4EnLqLpEZlk/TlqXWpgWxOI/AAAAAAAAHRw/oBAl6s1hj7U/s72-c/vundle-install2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-5562443458814549438</id><published>2012-01-10T03:00:00.000-08:00</published><updated>2012-01-11T00:04:18.150-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='języki'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='czystość'/><title type='text'>O czystości języków programowania</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/1/1c/Haskell-Logo.svg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" width="50" src="http://upload.wikimedia.org/wikipedia/commons/1/1c/Haskell-Logo.svg" /&gt;&lt;/a&gt;&lt;/div&gt;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ć. &lt;br /&gt;&lt;br /&gt;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 &lt;i&gt;if&lt;/i&gt; jest metodą w jakiejś klasie. Nie będę się nad tym dużo rozpisywał, podam jedynie, że przykładem takiego języka jest &lt;b&gt;SmallTalk&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;Jest jeszcze jeden aspekt najczęściej źle rozumiany. Funkcja nie może mieć efektów ubocznych. Co to są efekty uboczne? Ano są to &lt;b&gt;niespodziewane&lt;/b&gt; rezultaty wykonania funkcji. Kluczowe jest słowo "niespodziewane". Na przykład weźmy sobie taką funkcję: &lt;b&gt;toInt :: String -&gt; Int&lt;/b&gt;. 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ć.&lt;br /&gt;&lt;br /&gt;Powszechne jest twierdzenie, że problem efektów ubocznych rozwiązano w Haskellu &lt;b&gt;monadami&lt;/b&gt;. 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 &lt;b&gt;opakowanie&lt;/b&gt; danego typu w inny. Robi się to np. w taki sposób:&lt;br /&gt;&lt;br /&gt;&lt;pre class:"brush: bash"&gt;newtype Dirty a = Dirty a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Zatem nasza funkcja miała by typ &lt;b&gt;toIntDirty :: String -&gt; Dirty Int&lt;/b&gt;. 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 &lt;b&gt;wymusić&lt;/b&gt;. Dla jaśniejszej sytuacji weźmy taki kod w C:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: c"&gt;typedef struct { int result; } DirtyInt;&lt;br /&gt;&lt;br /&gt;DirtyInt toIntDirty (const char* text);&lt;br /&gt;&lt;br /&gt;int toInt (const char* text)&lt;br /&gt;{&lt;br /&gt;    return toIntDirty(text).result;&lt;br /&gt;} &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;module Type.Dirty where &lt;br /&gt;    (&lt;br /&gt;      Dirty&lt;br /&gt;    )&lt;br /&gt;&lt;br /&gt;newtype Dirty a = Dirty a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Od teraz aby użyć typu Dirty musimy zaimportować moduł Type.Dirty. Zauważmy, że przy &lt;b&gt;where&lt;/b&gt; nie napisałem Dirty(..) lecz Dirty. Zapis z kropkami powoduje wyeksportowanie wszystkich konstruktorów danego typu. Gdybym tak zrobił to legalny byłby zapis:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;module Test where&lt;br /&gt;&lt;br /&gt;import Type.Dirty&lt;br /&gt;&lt;br /&gt;cleanDirty :: Dirty a -&gt; a&lt;br /&gt;cleanDirty (Dirty a) = a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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ę &lt;b&gt;toIntDirty&lt;/b&gt;, 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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;multiply :: Dirty Int -&gt; Dirty Int&lt;br /&gt;multiply a = a*2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ponieważ funkcja (*) nie działa na typie Dirty Int. Nie zadziała również to:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;multiply2 :: Dirty Int -&gt; Int&lt;br /&gt;multiply2 (Dirty a) = a*2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;z powodu wspomnianego wyżej (nie wyeksportowany konstruktor). Aby rozwiązać ten problem z pomocą przychodzą nam właśnie &lt;b&gt;monady&lt;/b&gt;. 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ę &lt;b&gt;Monad&lt;/b&gt;, której instancję sobie napiszemy do naszego typu Dirty:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;module Type.Dirty where &lt;br /&gt;    (&lt;br /&gt;      Dirty&lt;br /&gt;    )&lt;br /&gt;&lt;br /&gt;newtype Dirty a = Dirty a&lt;br /&gt;&lt;br /&gt;instance Monad (Dirty a) where&lt;br /&gt;    return a = Dirty a&lt;br /&gt;    (Dirty a) &gt;&gt;= f = f a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Klasa Monad zawiera w sumie cztery funkcje ale do utworzenia instancji wystarczy tylko zdefiniować return oraz &gt;&gt;=. Funkcja return opakowuje zwykły typ w Dirty, np. return (10::Int) w naszym przypadku spowoduje utworzenie typu Dirty Int. Ciekawsza jest funkcja &gt;&gt;=. Ma ona typ Monad m =&gt; m a -&gt; (a -&gt; m b) -&gt; 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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;module Test where &lt;br /&gt;&lt;br /&gt;import Type.Dirty&lt;br /&gt;&lt;br /&gt;toIntAndMultiplyDirty :: String -&gt; Dirty Int&lt;br /&gt;toIntAndMultiplyDirty text = toIntDirty text &gt;&gt;= return . (* 2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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 (&gt;&gt;=) 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.&lt;br /&gt;&lt;br /&gt;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 &lt;b&gt;efekty jej wykonania były niespodziewane&lt;/b&gt;. 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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-5562443458814549438?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/5562443458814549438/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2012/01/o-czystosci-jezykow-programowania.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5562443458814549438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5562443458814549438'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2012/01/o-czystosci-jezykow-programowania.html' title='O czystości języków programowania'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-5139619362520643084</id><published>2011-12-28T01:48:00.000-08:00</published><updated>2011-12-28T01:48:31.310-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='graf'/><category scheme='http://www.blogger.com/atom/ns#' term='programowanie'/><category scheme='http://www.blogger.com/atom/ns#' term='mosty'/><title type='text'>Mosty w grafie - Haskell</title><content type='html'>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. &lt;br /&gt;&lt;br /&gt;Algorytm znajdowania mostów wykorzystuje algorytm DFS. Zmiany są niewielkie, a mianowicie:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; przeglądając dany wierzchołek nadajemy mu od razu kolejny numer,&lt;/li&gt;&lt;li&gt; nadajemy mu także początkową wartość &lt;a href="http://pl.wikipedia.org/wiki/Funkcja_low"&gt;&lt;i&gt;low&lt;/i&gt;&lt;/a&gt; taką samą jak numer wierzchołka, &lt;/li&gt;&lt;li&gt; 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,&lt;/li&gt;&lt;li&gt; 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ść &lt;i&gt;low&lt;/i&gt; mniejszą od wierzchołka, w którym jesteśmy to zmieniamy naszą wartość &lt;i&gt;low&lt;/i&gt; na wartość tego sąsiada. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Po wykonaniu tej procedury, wierzchołki, które mają numer równy &lt;i&gt;low&lt;/i&gt; tworzą mosty.&lt;br /&gt;&lt;br /&gt;Algorytm stanie się bardziej jasny jak przeanalizujemy kod:&lt;br /&gt;&lt;iframe src="http://pastebin.com/embed_iframe.php?i=nKK5uUkP" style="border:none;width:100%;height:100%"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;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ść &lt;i&gt;low&lt;/i&gt;. Krawędź to po prostu liczba, a most z kolei to para liczb oznaczająca parę wierzchołków tworzących jeden most.&lt;br /&gt;&lt;br /&gt;Funkcja &lt;i&gt;findBridges&lt;/i&gt; przyjmuje tablicę wierzchołków i zwraca listę mostów. Dalej treść funkcji czytamy od końca. Zatem wywołujemy funkcję &lt;i&gt;dfsBridges&lt;/i&gt; dla pierwszego wierzchołka i naszej tablicy, która uzupełni nam podanym wyżej algorytmem wartości &lt;i&gt;low&lt;/i&gt; oraz numery wierzchołków i zwróci te dane jako nową tablicę wierzchołków. Funkcja &lt;a href="http://hackage.haskell.org/packages/archive/array/latest/doc/html/Data-Array.html#v:assocs"&gt;&lt;i&gt;assocs&lt;/i&gt;&lt;/a&gt; zamieni nam tablicę na listę par typu (indeks, Vertex). Przeglądamy tą listę (&lt;a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:foldr"&gt;&lt;i&gt;foldr&lt;/i&gt;&lt;/a&gt;) i dla każdej pary sprawdzamy czy wartość &lt;i&gt;low&lt;/i&gt; 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ść &lt;i&gt;low&lt;/i&gt; różną od wartości &lt;i&gt;low&lt;/i&gt; danego wierzchołka (&lt;a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:filter"&gt;&lt;i&gt;filter&lt;/i&gt;&lt;/a&gt;). 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ę (&lt;a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:zip"&gt;&lt;i&gt;zip&lt;/i&gt;&lt;/a&gt;). Na koniec zostaje nam dołączenie tej listy do listy już uzyskanej.&lt;br /&gt;&lt;br /&gt;Funkcja &lt;i&gt;dfsBridges&lt;/i&gt; 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ść &lt;i&gt;low&lt;/i&gt;. Tą funkcję zaczynamy czytać od &lt;i&gt;where&lt;/i&gt;. Na początku inicjalizujemy wartości naszego wierzchołka czyli przydzielamy mu numer oraz &lt;i&gt;low&lt;/i&gt; równy aktualnemu numerowi - wartość &lt;i&gt;d&lt;/i&gt;. Odbywa się to w ten sposób, że tworzymy nową tablicę wierzchołków (&lt;i&gt;initArr&lt;/i&gt;), która zawiera te same dane za wyjątkiem naszego wierzchołka (&lt;a href="http://hackage.haskell.org/packages/archive/array/latest/doc/html/Data-Array-IArray.html#v:-47--47-"&gt;&lt;i&gt;//&lt;/i&gt;&lt;/a&gt;). 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. &lt;br /&gt;&lt;br /&gt;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ść &lt;i&gt;low&lt;/i&gt; to bierzemy jego wartość &lt;i&gt;low&lt;/i&gt;. Na koniec zmieniamy tablicę tak aby nasz wierzchołek uzyskał tą nową wartość &lt;i&gt;low&lt;/i&gt; i ją zwracamy.&lt;br /&gt;&lt;br /&gt;Jeżeli ktoś jeszcze nie za bardzo rozumie co to jest ta wartość &lt;i&gt;low&lt;/i&gt; to w skrócie można powiedzieć, że jeżeli napotkamy cykl w grafie to wszystkim wierzchołkom w tym cyklu nadajemy jednakową wartość &lt;i&gt;low&lt;/i&gt; (obrazowo można powiedzieć, że kolorujemy cykle w grafie). &lt;br /&gt;&lt;br /&gt;To właściwie wszystko. Czekam na komentarze.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-5139619362520643084?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/5139619362520643084/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/mosty-w-grafie-haskell.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5139619362520643084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5139619362520643084'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/mosty-w-grafie-haskell.html' title='Mosty w grafie - Haskell'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-6562973332915524392</id><published>2011-12-26T12:51:00.000-08:00</published><updated>2011-12-26T12:51:09.312-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='tsuru'/><category scheme='http://www.blogger.com/atom/ns#' term='pcap'/><title type='text'>Parsowanie plików pcap w Haskellu</title><content type='html'>Dzisiaj przykład prostego parsowania w Haskellu. Zadanie to znalazłem na stronie firmy Tsuru Capital jako &lt;i&gt;code sample&lt;/i&gt; do napisania, gdybyśmy ubiegali się o stanowisko programisty. Opis zadania znajduje się &lt;a href="http://www.tsurucapital.com/en/code-sample.html"&gt;tutaj&lt;/a&gt;. Jest tam dostępny także przykładowy plik z danymi w formacie &lt;i&gt;pcap&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;W skrócie to co mamy zrobić to:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; wyciągnąć z pliku linie, które zaczynają się od znaków B6034,&lt;/li&gt;&lt;li&gt; daną linię sparsować,&lt;/li&gt;&lt;li&gt; i wypisać ją w ten sposób:&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;pkt-time&gt; &lt;accept-time&gt; &lt;issue-code&gt; &lt;bqty5&gt;@&lt;bprice5&gt; ... &lt;bqty1&gt;@&lt;bprice1&gt; &lt;aqty1&gt;@&lt;aprice1&gt; ... &lt;aqty5&gt;@&lt;aprice5&gt;&lt;/pre&gt;&lt;/li&gt;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.&lt;li&gt; uruchamiając nasz program z parametrem &lt;i&gt;"-r"&lt;/i&gt; 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 &lt;i&gt;pkt-time&lt;/i&gt;) a &lt;i&gt;accept-time&lt;/i&gt; nigdy nie jest większa niż 3 sekundy.&lt;/li&gt; &lt;/ul&gt;&lt;br /&gt;No to zaczynamy. Na początku warto wspomnieć o pakiecie &lt;a href="http://hackage.haskell.org/package/pcap-0.4.5.1"&gt;Network.Pcap&lt;/a&gt;, 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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;module Main where&lt;br /&gt;&lt;br /&gt;import Network.Pcap&lt;br /&gt;import qualified Data.ByteString.Char8 as BS&lt;br /&gt;&lt;br /&gt;data MarketData = MarketData {&lt;br /&gt;      issueCode  :: BS.ByteString&lt;br /&gt;    , bids       :: [(BS.ByteString, BS.ByteString)]&lt;br /&gt;    , asks       :: [(BS.ByteString, BS.ByteString)]&lt;br /&gt;    , acceptTime :: AcceptTime&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;data AcceptTime = AcceptTime {&lt;br /&gt;      hh :: BS.ByteString&lt;br /&gt;    , mm :: BS.ByteString&lt;br /&gt;    , ss :: BS.ByteString&lt;br /&gt;    , uu :: BS.ByteString&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Jako, że pakiet &lt;i&gt;Network.Pcap&lt;/i&gt; dostarcza nam danych w &lt;i&gt;ByteStringach&lt;/i&gt; to wszędzie będziemy ich używać. Na uwagę zasługują pola &lt;i&gt;bids&lt;/i&gt; i &lt;i&gt;asks&lt;/i&gt; w &lt;i&gt;MarketData&lt;/i&gt;. 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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;instance Show AcceptTime where&lt;br /&gt;    show (AcceptTime h m s u) = (BS.unpack h) ++ ":"&lt;br /&gt;                                              ++ (BS.unpack m)&lt;br /&gt;                                              ++ ":"&lt;br /&gt;                                              ++ (BS.unpack s)&lt;br /&gt;                                              ++ "."&lt;br /&gt;                                              ++ (BS.unpack u)&lt;br /&gt;&lt;br /&gt;instance Show MarketData where&lt;br /&gt;    show (MarketData i b a t) = (show t)&lt;br /&gt;        ++ " " ++ (BS.unpack i)&lt;br /&gt;        ++ " " ++ (drop 1 $ foldr (\(ty, pr) ac -&gt;&lt;br /&gt;            "@" ++ (BS.unpack ty) ++ "@" ++ (BS.unpack pr) ++ ac) [] b)&lt;br /&gt;        ++ " " ++ (drop 1 $ foldr (\(ty, pr) ac -&gt;&lt;br /&gt;            "@" ++ (BS.unpack ty) ++ "@" ++ (BS.unpack pr) ++ ac) [] a)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;W ten sposób wystarczy wywołać funkcję &lt;i&gt;show&lt;/i&gt; na typie &lt;i&gt;MarketData&lt;/i&gt; i otrzymamy linię opisaną w zadaniu. &lt;br /&gt;&lt;br /&gt;Przechodzimy zatem do najtrudniejszej części czyli parsowania danych. W pakiecie &lt;i&gt;Network.Pcap&lt;/i&gt; dostępna jest funkcja &lt;i&gt;dispatch&lt;/i&gt;, 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 &lt;i&gt;PktHdr -&gt; ByteString -&gt; IO ()&lt;/i&gt;. 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ą &lt;i&gt;showData&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;prefix :: BS.ByteString&lt;br /&gt;prefix = (BS.pack "B6034")&lt;br /&gt;&lt;br /&gt;showData :: PktHdr&lt;br /&gt;         -&gt; BS.ByteString&lt;br /&gt;         -&gt; IO ()&lt;br /&gt;showData hdr dat = do&lt;br /&gt;    let (_, _d) = BS.breakSubstring prefix dat&lt;br /&gt;        (sec, msec) = (hdrTime hdr) `divMod` 1000000&lt;br /&gt;    if (BS.length _d) &gt; 0&lt;br /&gt;        then putStrLn $ (show $ TOD (toInteger sec) (toInteger (msec*1000000)))&lt;br /&gt;            ++ " " ++ (show $ parseData _d)&lt;br /&gt;        else return ()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To wszystko co występuje przed wywołaniem funkcji &lt;i&gt;parseData&lt;/i&gt; to wyświetlenie po prostu &lt;i&gt;pkt-time&lt;/i&gt;, 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 &lt;i&gt;prefix&lt;/i&gt; i to co przed nim. Jeżeli uda się podzielić linię w ten sposób to znaczy, że możemy parsować:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;parseData :: BS.ByteString&lt;br /&gt;          -&gt; MarketData&lt;br /&gt;parseData line = MarketData (BS.take 12 l) _bids _asks (AcceptTime h m s u)&lt;br /&gt;  where&lt;br /&gt;    l = BS.drop 5 line&lt;br /&gt;    (_bids, r1) = takeBids $ BS.drop 24 l&lt;br /&gt;    (_asks, r2) = takeAsks $ BS.drop 7 r1&lt;br /&gt;    (h, r3) = BS.splitAt 2 (BS.take 8 $ BS.drop 50 r2)&lt;br /&gt;    (m, r4) = BS.splitAt 2 r3&lt;br /&gt;    (s, u) = BS.splitAt 2 r4&lt;br /&gt; &lt;br /&gt;takeBids :: BS.ByteString&lt;br /&gt;         -&gt; ([(BS.ByteString, BS.ByteString)], BS.ByteString)&lt;br /&gt;takeBids bs = takeBids' bs (5::Integer)&lt;br /&gt;  where&lt;br /&gt;    takeBids' b n&lt;br /&gt;        | n &gt; 0     = let (_b, _r) = takeBids' (BS.drop 12 b) (n-1)&lt;br /&gt;                      in (_b ++ [(BS.splitAt 5 $ BS.take 12 b)], _r)&lt;br /&gt;        | otherwise = ([], b)&lt;br /&gt; &lt;br /&gt;takeAsks :: BS.ByteString&lt;br /&gt;         -&gt; ([(BS.ByteString, BS.ByteString)], BS.ByteString)&lt;br /&gt;takeAsks bs = takeAsks' bs (5::Integer)&lt;br /&gt;  where&lt;br /&gt;    takeAsks' b n&lt;br /&gt;        | n &gt; 0     = let (_b, _r) = takeAsks' (BS.drop 12 b) (n-1)&lt;br /&gt;                      in ((BS.splitAt 5 $ BS.take 12 b):_b, _r)&lt;br /&gt;        | otherwise = ([], b)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Funkcja &lt;i&gt;parseData&lt;/i&gt; jest bardzo prosta. Pobiera ona kolejną konkretną ilość znaków z linii i wrzuca je odpowiednio do konstruktora &lt;i&gt;MarketData&lt;/i&gt;. Np. jako pierwszy parametr konstruktor przyjmuje &lt;i&gt;issueCode&lt;/i&gt;, 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 &lt;i&gt;issueCode&lt;/i&gt; itd. Na uwagę zasługują jeszcze dwie funkcje &lt;i&gt;takeBids&lt;/i&gt; oraz &lt;i&gt;takeAsks&lt;/i&gt;. 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).&lt;br /&gt;&lt;br /&gt;Pozostaje nam tylko dopisanie pozostałych funkcji odpalających cały mechanizm:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;readQuote :: Bool&lt;br /&gt;          -&gt; PcapHandle&lt;br /&gt;          -&gt; IO ()&lt;br /&gt;readQuote False h = dispatchBS h (-1) showData &gt;&gt; return ()&lt;br /&gt;readQuote True h = return ()&lt;br /&gt; &lt;br /&gt;main :: IO ()&lt;br /&gt;main = do&lt;br /&gt;    opt &lt;- liftA parseOptions getArgs&lt;br /&gt;    hdl &lt;- openOffline $ filename opt&lt;br /&gt;    readQuote (quoteOrder opt) hdl&lt;br /&gt; &lt;br /&gt;data Options = Options { filename :: FilePath, quoteOrder :: Bool }&lt;br /&gt;&lt;br /&gt;parseOptions :: [String]&lt;br /&gt;             -&gt; Options&lt;br /&gt;parseOptions [] = Options "" False&lt;br /&gt;parseOptions (x:xs)&lt;br /&gt;    | x == "-r" = Options (head xs) True&lt;br /&gt;    | otherwise = Options x False&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Dodałem tu nowy typ danych &lt;i&gt;Options&lt;/i&gt;, który odpowiada za parametry programu, takie jak nazwa pliku oraz czy ma być sortowany. Funkcja &lt;i&gt;openOffline&lt;/i&gt; zwraca nam uchwyt do otworzonego pliku &lt;i&gt;pcap&lt;/i&gt;, a funkcja &lt;i&gt;readQuote&lt;/i&gt; przyjmuje jako parametr czy ma sortować dane (jeżeli tak to nic nie robi) i dalej wywołuje funkcję &lt;i&gt;dispatchBS&lt;/i&gt;, która przetwarza dane wywołując &lt;i&gt;showData&lt;/i&gt; na każdym pakiecie. &lt;br /&gt;&lt;br /&gt;To by było na tyle. Sortowanie oczywiście pozostawiam czytelnikom do samodzielnej realizacji :)&lt;br /&gt;&lt;br /&gt;Jakby jednak ktoś chciał zobaczyć jak ja wykonałem sortowanie danych to &lt;a href="http://pastebin.com/jLreEVVk"&gt;tutaj&lt;/a&gt; jest cały plik.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-6562973332915524392?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/6562973332915524392/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/parsowanie-plikow-pcap-w-haskellu.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6562973332915524392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6562973332915524392'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/parsowanie-plikow-pcap-w-haskellu.html' title='Parsowanie plików pcap w Haskellu'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-4955506762088851411</id><published>2011-12-16T04:05:00.000-08:00</published><updated>2011-12-16T04:06:03.088-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='hoogle'/><title type='text'>Vim Hoogle</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.haskell.org/hoogle/res/hoogle.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="58" width="160" src="http://www.haskell.org/hoogle/res/hoogle.png" /&gt;&lt;/a&gt;&lt;/div&gt;Dzisiaj coś dla programistów &lt;i&gt;Haskella&lt;/i&gt;. Ostatnio znalazłem bardzo ciekawy plugin do &lt;i&gt;Vima&lt;/i&gt; integrujący ten wspaniały edytor z równie wspaniałą wyszukiwarką &lt;i&gt;Hoogle&lt;/i&gt;. Jeżeli ktoś jeszcze o niej nie słyszał to w skrócie jest to wyszukiwarka, która umożliwia nam znalezienie funkcji &lt;i&gt;haskellowych&lt;/i&gt; w dostępnych bibliotekach (z &lt;a href="hackage.haskell.org"&gt;hackage.haskell.org&lt;/a&gt;) po nazwie oraz po typie. Dodatkowo po kliknięciu na znalezionej funkcji odsyła nas do jej dokumentacji.&lt;br /&gt;&lt;br /&gt;Plugin zwie się &lt;i&gt;vim-hoogle&lt;/i&gt; i jest dostępny &lt;a href="https://github.com/Twinside/vim-hoogle"&gt;tutaj&lt;/a&gt;. Instaluje się go standardowo - ściągamy sobie archiwum z &lt;b&gt;github&lt;/b&gt; i wypakujemy zawartość do katalogu ~/.vim/&lt;br /&gt;Aby plugin działał poprawnie wymagane jest jeszcze zainstalowanie programu &lt;i&gt;hoogle&lt;/i&gt;. Najłatwiej jest to zrobić wykorzystując program &lt;i&gt;cabal&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;$ &lt;/b&gt; &lt;i&gt;cabal install hoogle&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Po czym należy jeszcze ściągnąć bazę danych w ten sposób:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;$ &lt;/b&gt; &lt;i&gt;hoogle data&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Na koniec możemy uruchomić &lt;i&gt;Vima&lt;/i&gt; i np. wywołać &lt;i&gt;:Hoogle foldr&lt;/i&gt;. Po więcej informacji odsyłam do &lt;i&gt;:help hoogle&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://blog-mno2.csie.org/images/2011/11/vim-hoogle01.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="350" src="http://blog-mno2.csie.org/images/2011/11/vim-hoogle01.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-4955506762088851411?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/4955506762088851411/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/vim-hoogle.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/4955506762088851411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/4955506762088851411'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/vim-hoogle.html' title='Vim Hoogle'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-7108670397680523410</id><published>2011-12-10T11:21:00.000-08:00</published><updated>2011-12-10T11:21:58.154-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><category scheme='http://www.blogger.com/atom/ns#' term='metapost'/><category scheme='http://www.blogger.com/atom/ns#' term='klamra'/><category scheme='http://www.blogger.com/atom/ns#' term='cieniowanie'/><title type='text'>Cieniowanie oraz poziome klamry w MetaPost-cie</title><content type='html'>Ostatnio pisząc pewien referat natchnął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:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-fhhmjOi8di4/TuOr_p6q-OI/AAAAAAAAABw/bi2yhrLEEII/s1600/main.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="236" width="320" src="http://2.bp.blogspot.com/-fhhmjOi8di4/TuOr_p6q-OI/AAAAAAAAABw/bi2yhrLEEII/s320/main.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;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) &lt;i&gt;for&lt;/i&gt; i wszystko ładnie wyszło. Oto kod odpowiedzialny za cieniowanie:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;for i=0 upto width/2: &lt;br /&gt;  draw (x,y-i*2)--(x+i*2,y);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=1 upto width/2: &lt;br /&gt;  draw (x+i*2,y2)--(x2,y - i*2);&lt;br /&gt;endfor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;label.bot(btex $\lbrace$ etex xscaled 1.5 yscaled 15 rotated 90, (x,y));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Na koniec zamieszczam cały kod jak wygenerować obrazek przedstawiony powyżej:&lt;br /&gt;&lt;pre class="brush: js"&gt;prologues:=3;&lt;br /&gt;verbatimtex&lt;br /&gt;%&amp;latex&lt;br /&gt;\documentclass{minimal}&lt;br /&gt;\begin{document}&lt;br /&gt;etex&lt;br /&gt;beginfig(0);&lt;br /&gt;s:=20;&lt;br /&gt;&lt;br /&gt;draw (350,s+25)--(350,s+125)--(500,s+125)--(500,s+25)--cycle;&lt;br /&gt;draw (350,s+125)--(400,s+75)--(400,s+25);&lt;br /&gt;draw (400,s+75)--(450,s+25);&lt;br /&gt;draw (350,s+75)--(500,s+75) dashed evenly scaled 2;&lt;br /&gt;label.lft(btex $m/2$ etex, (350,s+100));&lt;br /&gt;label.lft(btex $m/2$ etex, (350,s+50));&lt;br /&gt;label.top(btex $p$ etex, (425,s+125));&lt;br /&gt;label(btex $H$ etex, (450,s+100));&lt;br /&gt;label(btex $U_2$ etex, (475,s+50));&lt;br /&gt;&lt;br /&gt;for i=0 upto 25: &lt;br /&gt;  draw (350+i,s+125-i)--(350,s+125-i*2);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=1 upto 25: &lt;br /&gt;  draw (375+i,s+100-i)--(350+i*2,s+75);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=0 upto 25: &lt;br /&gt;  draw (350,s+75-i*2)--(350+i*2,s+75);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=1 upto 25: &lt;br /&gt;  draw (350+i*2,s+25)--(400,s+75 - i*2);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=0 upto 25: &lt;br /&gt;  draw (400+i,s+75-i)--(400,s+75-i*2);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;for i=1 upto 25: &lt;br /&gt;  draw (425+i,s+50-i)--(400+i*2,s+25);&lt;br /&gt;endfor&lt;br /&gt;&lt;br /&gt;s:= 40;&lt;br /&gt;&lt;br /&gt;label.bot(btex $\lbrace$ etex xscaled 1.5 yscaled 15 rotated 90, (425,s));&lt;br /&gt;&lt;br /&gt;labeloffset:=5.5mm;&lt;br /&gt;label.bot(btex $U$ etex, (425,s));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;endfig;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-7108670397680523410?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/7108670397680523410/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/cieniowanie-oraz-poziome-klamry-w.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/7108670397680523410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/7108670397680523410'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/12/cieniowanie-oraz-poziome-klamry-w.html' title='Cieniowanie oraz poziome klamry w MetaPost-cie'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-fhhmjOi8di4/TuOr_p6q-OI/AAAAAAAAABw/bi2yhrLEEII/s72-c/main.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-1259708082241005333</id><published>2011-11-28T00:26:00.000-08:00</published><updated>2011-11-28T00:26:13.408-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='języki'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='porównanie'/><title type='text'>O wyższości języków</title><content type='html'>Zazwyczaj unikam rozmów na temat wyższości jednego języka (programowania) nad drugim. Głównie ze względu na to, że każdy programista ma jakiś swój ulubiony język i przy takiej dyskusji stosuje bardziej emocjonalne argumenty niż rzeczowe. Niestety ostatnio zostałem w taką dyskusję wciągnięty. &lt;br /&gt;&lt;br /&gt;Człowiek, z którym rozmawiałem jest ewidentnym zwolennikiem programowania w C# pod platformą Windows. I tak jak się obawiałem pytania, które padały były emocjonalne (tzn. nie za bardzo dotyczyły tematu rozmowy). Na przykład ów człowiek zapytał mnie - niby całkiem rzeczowo - dlaczego warto programować w Haskellu. Dodam jeszcze, że on o tym języku w ogóle nie słyszał. Pytanie byłoby jak najbardziej na miejscu, gdyby nie dalsza część, którą dopowiedział chwilę później, a mianowicie jak została rozwiązana komunikacja z maszyną wirtualną od C# i czy komunikacja z Windowsem odbywa się za pomocą obiektów COM+. &lt;br /&gt;&lt;br /&gt;Widać zatem, że pytanie w całości nie dotyczyło w ogóle cech języka programowania tylko raczej sposobu w jakim zostały napisane biblioteki. Ewidentnie oczekiwał on odpowiedzi, która wyraziłaby wyższość C# nad Haskellem, ponieważ komunikacja z Windowsem jest bardziej efektywna w C#. To przypomina mi bardzo pytanie o to co da się w danym języku napisać. Odpowiedź brzmi wszystko, w tym również da się napisać efektywną komunikację z Windowsem ale to nigdy nie powinno dotyczyć porównywania języków programowania (co najwyżej środowisk programistycznych lub wsparcia). Na marginesie dodam, że jeśli nawet w Haskellu nie da się napisać szybko działającej jakiejś krytycznej części programu to zawsze można to napisać chociażby w C, z którym Haskell świetnie się integruję (za pomocą modułu FFI).&lt;br /&gt;&lt;br /&gt;Moim skromnym zdaniem takie rozmowy powinny dotyczyć zalet elementów składniowych języka a nie możliwości jego bibliotek. Wiem, że większość programistów uważa, że dzisiaj język bez dobrego zaplecza w postaci ogromu bibliotek i najlepiej jakiejś firmy (korporacji), która za tym stoi jest nic nie wart. Ja jednak na takie zarzuty odpowiadam, że w przypadku gdy w jednym języku nie ma napisanej danej funkcjonalności a w drugim jest to faktycznie ten drugi wypada lepiej. Jednak jeśli danej funkcjonalności nie ma ani tu ani tu to wygrywa język, w którym piszę się szybciej, krócej i poprawniej. &lt;br /&gt;&lt;br /&gt;Na dłuższą metę koszt poniesiony na początku w postaci napisania funkcjonalności bardzo szybko się zwróci w przypadku takich właśnie języków. To tak jak z algorytmami działającymi lepiej gdy dane są posortowane - sortowanie danych faktycznie zajmuje trochę czasu ale gdy dane są odpowiednio duże (czyli w naszym przypadku program) to nie ma on żadnego znaczenia a czas działania algorytmu znacząco się skraca.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-1259708082241005333?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/1259708082241005333/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/o-wyzszosci-jezykow.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1259708082241005333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1259708082241005333'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/o-wyzszosci-jezykow.html' title='O wyższości języków'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-2113627355403079999</id><published>2011-11-21T03:13:00.000-08:00</published><updated>2011-11-21T03:13:21.076-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='projektowanie'/><title type='text'>UML a Haskell</title><content type='html'>Dzisiaj chciałbym poruszyć temat projektowania aplikacji &lt;i&gt;haskellowych&lt;/i&gt; przy użyciu UMLa. Zajmę się tylko diagramem klas, gdyż akurat ostatnio próbowałem się z nim zmierzyć (w kontekście aplikacji haskellowych). Aby jednak zacząć w ogóle mówić o diagramie klas musiałbym pokrótce omówić typy danych jakie występują w Haskellu oraz ich podobieństwa do typów używanych w językach &lt;i&gt;imperatywnych orientowanych obiektowo&lt;/i&gt; jak np. C++ czy Java. &lt;br /&gt;&lt;br /&gt;W Haskellu rozróżniamy głównie 2 słowa kluczowe służące do tworzenia typów: &lt;b&gt;class&lt;/b&gt; oraz &lt;b&gt;data&lt;/b&gt; (istnieje również &lt;b&gt;type&lt;/b&gt;, który tworzy tylko alias do typu oraz &lt;b&gt;newtype&lt;/b&gt;, który jest raczej zabiegiem optymalizacyjnym). Oczywiście w językach funkcyjnych również funkcja jest typem danych. Omówię teraz z grubsza wyżej wymienione słowa kluczowe. Zapisy tłumaczył będę imperatywnie, tzn. używając słów takich jak chociażby interfejs, które oczywiście nie występują w Haskellu.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;data&lt;/h2&gt;Zacznę o słówka &lt;b&gt;data&lt;/b&gt;, ponieważ jest ono mniej abstrakcyjne od &lt;b&gt;class&lt;/b&gt;. Typy tworzone przez właśnie to słowo odpowiadają klasą znanym z języków obiektowych. Wygląda to mniej więcej tak:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;data Foo = Foo { fooname :: String, foofunction :: Int -&gt; String }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tłumacząc to na język imperatywny powiedzielibyśmy, że Foo jest klasą, która zawiera atrybut o nazwie &lt;i&gt;fooname&lt;/i&gt; typu String oraz metodę o nazwie &lt;i&gt;foofunction&lt;/i&gt;, która przyjmuje obiekt typu Int jako argument i zwraca obiekt typu String. Jak zatem tworzymy instancję klasy? Ano tak:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;fooInstance :: Foo&lt;br /&gt;fooInstance = Foo "foo" (\a -&gt; "foo: " ++ (show a))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pierwsza różnica jaką widać w stosunku do języków imperatywnych to fakt, że możemy przyjąć funkcję jako argument konstruktora. Oczywiście w językach typu C-podobnych moglibyśmy wykorzystać w tym celu wskaźnik do funkcji bądź delegaty. Rozważmy zatem nieco bardziej wyrafinowany typ:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;data Foo2 = Foo2 { fooname :: String, foofunction :: a -&gt; String }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Zmieniła się definicja &lt;i&gt;foofunction&lt;/i&gt;. Moglibyśmy przeczytać ją następująco: &lt;i&gt;foofunction&lt;/i&gt; jest funkcją, która przyjmuje &lt;b&gt;argument dowolnego typu&lt;/b&gt; i zwraca obiekt typu String. Aby wyrazić taką funkcję w imperatywnych językach potrzebny jest jakiś mechanizm &lt;i&gt;template'ów&lt;/i&gt;. OK, a co powiedzie na taki typ:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;data Foo3 = Foo3 { fooname :: String, foofunction :: (Show a) =&gt; a -&gt; String }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Teraz &lt;i&gt;foofunction&lt;/i&gt; jest funkcją, która przyjmuje &lt;b&gt;argument dowolnego typu, który implementuje interfejs Show&lt;/b&gt; i zwraca obiekt typu String. W Javie po prostu funkcja ta przyjmowałaby jako argument ten interfejs a raczej &lt;i&gt;"obiekt interfejsu"&lt;/i&gt;. No dobrze, a co w takim przypadku:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;data Foo3 = Foo3 { fooname :: String, foofunction :: (Show a, Ord a) =&gt; a -&gt; String }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;W tym przypadku &lt;i&gt;foofunction&lt;/i&gt; jest funkcją, która przyjmuje &lt;b&gt;argument dowolnego typu, który implementuje interfejs Show oraz interfejs Ord&lt;/b&gt; i zwraca obiekt typu String. Teraz jest znacznie ciężej wyrazić to w Javie. Trzeba by utworzyć osobny typ (klasę), który implementuje te interfejsy i użyć go jako argument funkcji.&lt;br /&gt;&lt;br /&gt;Typy tworzone przez &lt;b&gt;data&lt;/b&gt; mogą być również parametryzowane:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;data Foo3 b = FooB { fooid :: (Show b) =&gt; b, foofunction :: (Show a, Ord a) =&gt; a -&gt; String }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Obiekt tworzylibyśmy w ten sposób:&lt;br /&gt;&lt;pre class="brush: bash"&gt;fooInstance :: Foo3 Int&lt;br /&gt;fooInstance = FooB 3 (\a -&gt; "foo: " ++ (show a))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Natomiast obiekt takiej klasy przyjmowalibyśmy w funkcji np. w taki sposób:&lt;br /&gt;&lt;pre class="brush: bash"&gt;fooFun :: Foo3 b -&gt; b&lt;br /&gt;fooFun foo = fooid foo&lt;br /&gt;&lt;br /&gt;fooFunSpec :: Foo3 Int -&gt; Int&lt;br /&gt;fooFunSpec = fooid foo&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pierwsza funkcja jest polimorficzna czyli zadziała dla każdego typu Foo3, a druga tylko dla typu Foo3 Int.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;class&lt;/h2&gt;Klasy w Haskellu nie są tym samym co klasy w Javie czy w C++. Przypominają one raczej interfejsy znane z Javy tylko znacznie lepsze. Prosty przykład:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;class Show a where&lt;br /&gt;    show :: a -&gt; String&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Jak widać jest to prosty interfejs z metodą &lt;i&gt;show&lt;/i&gt;, która przyjmuje argument typu &lt;b&gt;a&lt;/b&gt; i zwraca obiekt typu String. Jest to tylko deklaracja typu funkcji bez jej ciała. Jak widać również tutaj w przypadku języków imperatywnych trzeba by zastosować mechanizm &lt;i&gt;template'ów&lt;/i&gt;. Jak zatem implementuje się ten &lt;i&gt;interfejs&lt;/i&gt;? A tak:&lt;br /&gt;&lt;pre class="brush: bash"&gt;instance Show Foo where&lt;br /&gt;    show _ = "Foo"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;W tym momencie na dowolnym obiekcie klasy &lt;i&gt;Foo&lt;/i&gt; możemy wywołać funkcję &lt;i&gt;show&lt;/i&gt; i zawsze w wyniku otrzymamy napis Foo. Pominę może aspekt, że metody/atrybuty w interfejsach mogą przyjmować domyślne definicje co różni je od interfejsów w Javie.&lt;br /&gt;&lt;br /&gt;Podstawy za nami więc pora na bardziej skomplikowany typ:&lt;br /&gt;&lt;pre class="brush: bash"&gt;class (Ord a) =&gt; Show a where&lt;br /&gt;    show :: a -&gt; String&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ten zapis powoduje, że implementować interfejs Show mogą klasy, które implementują już interfejs Ord. Czy da się coś takiego wyrazić w językach imperatywnych? Ja nie znam żadnej konstrukcji ale też żadnym mistrzem w C++ czy Javie nie jestem więc jak ktoś wie jak można to zrobić to proszę niech napisze w komentarzu.&lt;br /&gt;&lt;br /&gt;To nie jedyne różnice. Pokażmy zatem coś znacznie ciekawszego:&lt;br /&gt;&lt;pre class="brush: bash"&gt;class Eq e =&gt; Collection c e | c -&gt; e where&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Zapis po kresce oznacza, że typ e jest &lt;b&gt;jednoznacznie określony przez typ c&lt;/b&gt;. Co to oznacza? Oznacza to tyle, że jak istniała by instancja powiedzmy Collection Array Int to nie może istnieć inna klasa implementująca ten interfejs taka, że c będzie typu Array, a &lt;b&gt;e NIE będzie typu Int&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;Nie dodałem tu jeszcze, że interfejsy mogą implementować typy polimorficzne takie jak chociażby lista elementów dowolnego typu: [a]. Czyli taki zapis:&lt;br /&gt;&lt;pre class="brush: bash"&gt;instance Collection [a] a where&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;w tym przypadku oznacza, że "&lt;i&gt;[a] a&lt;/i&gt;" jest kolekcją i żadna inna kolekcja "zaczynająca się" od &lt;i&gt;[a]&lt;/i&gt; istnieć nie może. Oznacza on również, że "&lt;i&gt;[Int] Int&lt;/i&gt;" jest kolekcją oraz że "&lt;i&gt;[Char] Char&lt;/i&gt;" jest kolekcją itd. Tak na marginesie to widać tu jeszcze jedną rzecz, której nie da się (chyba) wyrazić w wymienionych językach imperatywnych, tzn. "&lt;i&gt;[a] a&lt;/i&gt;" - jak pierwszy typ będzie powiedzmy [Int] to drugi musi być Int (ponieważ a jest typu Int).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;UML&lt;/h2&gt;Powróćmy zatem do UMLa. O ile typy tworzone za pomocą słówka &lt;b&gt;data&lt;/b&gt; i relacje między nimi da się jakoś na siłę wyrazić na diagramie klas o tyle już w przypadku słówka &lt;b&gt;class&lt;/b&gt; nie za bardzo. No bo jak ma z diagramu wynikać, że np. &lt;i&gt;klasa (słówko data) "Article a" ma być taka, że a implementuje interfejs (słówko class) Foo&lt;/i&gt;, a do tego interfejs Foo mogą implementować tylko te klasy, które implementują jeszcze interfejs Show oraz interfejs Ord? Nie mówiąc już o funkcjach istniejących poza interfejsami/klasami. &lt;br /&gt;&lt;br /&gt;Czego zatem użyć zamiast diagramu klas skoro się nie nadaje? W UMLu nie ma moim zdaniem odpowiedniego diagramu. Pewien człowiek napisał propozycję diagramów do projektowania funkcyjnego w swoim doktoracie: &lt;a href="http://www.cs.kent.ac.uk/pubs/2001/1152/index.html"&gt;link&lt;/a&gt;. Większość ludzi jednak uważa, że w przypadku nie zbyt dużych programów wystarczy pisać program w odpowiedni sposób, który Haskell akurat wymusza. Jaki to konkretnie sposób? Odsyłam do książki "Real World Haskell". W przypadku większych aplikacji polecam użyć jakiegoś prostego programu do tworzenia diagramów chociażby jak CmapTools. Tyle, że sami musimy ustalić sobie jak ma być prezentowana klasa, jak interfejs a jak funkcja itd. Niestety kodu nam to nie wygeneruje ale aplikację uda się z powodzeniem zaprojektować.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-2113627355403079999?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/2113627355403079999/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/uml-haskell.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2113627355403079999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2113627355403079999'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/uml-haskell.html' title='UML a Haskell'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-4061547155521349866</id><published>2011-11-15T01:56:00.000-08:00</published><updated>2011-11-15T01:56:48.715-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='algorytmy'/><title type='text'>Multikolorowanie grafu w Haskellu</title><content type='html'>Ostatnio na studiach dostałem takie zadanie do zaimplementowania:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Część I:&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Wygenerować plik w formacie GXL zawierający graf składający się ze stacji, które są losowe rozmieszczone wewnątrz miasta, w którym stacje są połączone krawędzią wtedy, gdy ich zasięg się pokrywa.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Wejście:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Współrzędne środka koła (miasta)&lt;/li&gt;&lt;li&gt;Promień koła (miasta)&lt;/li&gt;&lt;li&gt;Liczba stacji&lt;/li&gt;&lt;li&gt;Zasięg (promień) stacji&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Wyjście:&lt;br /&gt;Uzyskany graf w formacie GXL.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Część II&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Należy wczytać wygenerowany wcześniej graf oraz zapotrzebowanie na częstotliwości dla każdej stacji. Stacje, których zasięg się pokrywa nie mogą uzyskać tych samych częstotliwości. Program powinien znaleźć jak najmniejszą liczbę różnych częstotliwości potrzebnych aby stacje mogły funkcjonować.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Przykład:&lt;br /&gt;Mając prosty graf [1]-&gt;[2]-&gt;[3] i wiedząc, że stacja 1 potrzebuje 3 częstotliwości, stacja 2 potrzebuje 4 a stacja ostatnia 5. Wówczas powinniśmy przydzielić częstotliwości np. tak: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stacja nr 1: 4,5,6&lt;/li&gt;&lt;li&gt;Stacja nr 2: 0,1,2,3&lt;/li&gt;&lt;li&gt;Stacja nr 3: 4,5,6,7,8&lt;/li&gt;&lt;/ul&gt;i wynikiem działania programu powinna być liczba 9. Jak widać stacja pierwsza i ostatnia może korzystać z tych samych częstotliwości, ponieważ ich zasięg się nie pokrywa. Stacja druga natomiast musi korzystać z innych częstotliwości niż stacja 1 i 3.&lt;br /&gt;&lt;br /&gt;Wejście:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Plik GXL&lt;/li&gt;&lt;li&gt;Zapotrzebowanie na częstotliwości dla każdej stacji&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Wyjście:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Liczba przydzielonych różnych częstotliwości&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Problem postanowiłem rozwiązać w Haskellu z uwagi na możliwość operacji na listach nieskończonych co w tym przypadku bardzo ułatwia znajdowanie częstotliwości. W części II wykorzystałem algorytm &lt;a href="http://pl.wikipedia.org/wiki/Kolorowanie_grafu#Algorytm_SLF_.28saturated_largest_first.29"&gt;SLF&lt;/a&gt;. Oczywiście problem jest NP zupełny więc nie ma idealnego algorytmu, który by przydzielił zawsze najmniejszą możliwą do uzyskania liczbę częstotliwości dla każdego przypadku. &lt;br /&gt;&lt;br /&gt;Część I: &lt;a href="http://pastebin.com/dgpH4VX5"&gt;gxl.hs&lt;/a&gt;&lt;br /&gt;Część II: &lt;a href="http://pastebin.com/qMzWPris"&gt;multicolor.hs&lt;/a&gt;&lt;br /&gt;Plik do obsługi formatu GXL (wymagany aby skompilować poprzednie 2 pliki): &lt;a href="http://pastebin.com/LzPfrGYy"&gt;gxl_dtd.hs&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-4061547155521349866?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/4061547155521349866/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/multikolorowanie-grafu-w-haskellu.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/4061547155521349866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/4061547155521349866'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/multikolorowanie-grafu-w-haskellu.html' title='Multikolorowanie grafu w Haskellu'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-1448486934009553362</id><published>2011-11-13T14:14:00.000-08:00</published><updated>2011-11-13T14:14:23.492-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='programowanie'/><title type='text'>Leniwa potęga</title><content type='html'>Dla odmiany dzisiaj napiszę o Haskellu. Poznałem ten język stosunkowo niedawno (jakieś 1,5 roku temu) więc nie jestem żadnym ekspertem w temacie. Jednakże jedna rzecz nadzwyczaj mi się w nim spodobała i miałem nawet okazję ją ostatnio wykorzystać. Jest to jedna z wyróżniających cech tego języka czyli leniwa ewaluacja. Brzmi dość tajemniczo ale koncepcja jest bardzo prosta. Rozważmy prostą funkcję w C:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;int check(int a, int b) {&lt;br /&gt;    if (a &gt; 100)&lt;br /&gt;        return 100;&lt;br /&gt;    if (a &gt; b) &lt;br /&gt;        return b;&lt;br /&gt;&lt;br /&gt;    return a;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Leniwa ewaluacja spowoduje, że jeżeli akurat zmienna &lt;b&gt;a&lt;/b&gt; będzie większa niż 100 to zmienna &lt;b&gt;b&lt;/b&gt; nie zostanie w ogóle wyliczona. Zauważmy, że pod zmienną &lt;b&gt;b&lt;/b&gt; może kryć się szereg wywołań funkcji ciężkich obliczeniowo. Wówczas nasz program będzie działał znacznie szybciej w Haskellu niż w C (napisany w ten sposób). &lt;br /&gt;&lt;br /&gt;Niestety jak ktoś kiedyś napisał, leniwa ewaluacja to nie święty graal programowania, to coś z czym trzeba nauczyć się żyć. Powoduje ona masę problemów związanych z przepełnieniem stosu. Chodzi o to, że żadna zmienna nie jest wyliczana od razu lecz dopiero w momencie gdy zostaje użyta. Zamiast jej wartości odkładany na stos jest adres do instrukcji, które pozwalają ją wyliczyć. Jeśli więc np. funkcja działa rekurencyjnie to może takich adresów sporo odłożyć i dochodzi wówczas do przepełnienia stosu.&lt;br /&gt;&lt;br /&gt;Leniwa ewaluacja pozwala jeszcze na jedną rzecz. Mianowicie na definiowanie w programie typów nieskończonych oraz operacji na nich. W Haskellu zatem możliwe jest zapisanie listy wszystkich liczb naturalnych:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;[0..] &lt;/pre&gt;Odbywa się to w ten sam sposób jak powyżej. Dopóki nie żaden element z listy nie jest potrzebny to pamiętany jest tylko adres jak wyliczyć kolejny element listy. Oczywiście próba wypisania wszystkich elementów tej listy skończy się w końcu brakiem pamięci. Podam teraz przykład kiedy się to może przydać. Załóżmy, że mamy jakąś (skończoną) listę liczb i chcielibyśmy poznać najmniejszą liczbę naturalną, która nie należy do tej listy (ten problem występuje np. w zagadnieniach kolorowania grafu). W imperatywnym języku musielibyśmy pisać pętle z szeregiem instrukcji warunkowych, które strasznie zaciemniają istotę tego co robimy. Jak zatem wygląda to w Haskellu? A tak:&lt;br /&gt;&lt;pre class="brush: bash"&gt;head $ [0..] // lista &lt;/pre&gt;Te dwa ukośniki (&lt;b&gt;//&lt;/b&gt;) to nie żaden komentarz tylko operator usuwający elementy z pierwszej listy (po lewej), które występują w drugiej (po prawej). Przeczytajmy więc to wyrażenie od lewej strony: &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Weź pierwszy element (head) z listy powstałej z wyrażenia ($) różnicy (//) liczb naturalnych ([0..]) i listy (lista).&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Wygląda to bardzo zwięźle i czytelnie a do tego nie musimy się martwić o warunki do sprawdzenia ani o zakres liczb do przeszukania. Podsumowując warto się zainteresować językami z leniwą ewaluacją lecz trzeba jej używać z głową :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-1448486934009553362?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/1448486934009553362/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/leniwa-potega.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1448486934009553362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1448486934009553362'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/leniwa-potega.html' title='Leniwa potęga'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-6499787278984624389</id><published>2011-11-08T11:47:00.000-08:00</published><updated>2011-11-08T11:47:45.423-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='vimgolf'/><title type='text'>Zagrajmy w golfa!</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://vimgolf.com/images/vim_golf_logo.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" width="50" src="http://vimgolf.com/images/vim_golf_logo.png" /&gt;&lt;/a&gt;&lt;/div&gt;Dość ważnym etapem w mojej nauce &lt;i&gt;Vima&lt;/i&gt; był serwis &lt;a href="http://vimgolf.com"&gt;vimgolf.com&lt;/a&gt;. Niebywały pomysł aby zmobilizować ludzi do głębszego poznania tego wspaniałego edytora. Wydaje Ci się, że znasz &lt;i&gt;Vima&lt;/i&gt;? Wejdź na stronę i sprawdź!&lt;br /&gt;&lt;br /&gt;Serwis zawiera kilkadziesiąt zadań do wykonania. Zadania polegają na przekształceniu pliku wejściowego tak aby przy użyciu najmniejszej liczby naciśnięć klawiszy uzyskać plik wyjściowy. Jednakże by w ogóle móc się z kimś porównać należałoby mieć tą samą wersję &lt;i&gt;Vima&lt;/i&gt;, te same &lt;i&gt;pluginy&lt;/i&gt; i te same ustawienia. Nie musisz się o to martwić, autor strony o wszystkim pomyślał. &lt;br /&gt;&lt;br /&gt;Aby w ogóle móc brać udział w zabawie należy zarejestrować się na stronie przy użyciu &lt;i&gt;Twittera&lt;/i&gt;. Jeżeli więc nie masz konta to musisz je sobie założyć. Gdy się zarejestrujemy uzyskamy klucz (&lt;i&gt;VimGolf key&lt;/i&gt;). Później należy zainstalować sobie skrypt napisany przez autora w &lt;i&gt;Rubym&lt;/i&gt; za pomocą polecenia:&lt;br /&gt;&lt;i&gt;&lt;b&gt;$&lt;/b&gt; gem install vimgolf&lt;/i&gt;&lt;br /&gt;Jeżeli wszystko przejdzie pomyślnie to w dalszym ciągu uruchamiamy polecenie:&lt;br /&gt;&lt;i&gt;&lt;b&gt;$&lt;/b&gt; vimgolf setup&lt;/i&gt;&lt;br /&gt;Program zapyta nas o klucz, który uzyskaliśmy rejestrując się na stronie.&lt;br /&gt;Na koniec pozostaje nam wybrać sobie zadanie i wykonać polecenie:&lt;br /&gt;&lt;i&gt;&lt;b&gt;$&lt;/b&gt; vimgolf put [challenge ID]&lt;/i&gt;&lt;br /&gt;gdzie [challenge ID] jest podane na stronie z zadaniem. Po wykonaniu powyższego polecenia uruchomi się nam &lt;i&gt;Vim&lt;/i&gt; z załadowanym plikiem wejściowym i spełniający wszystkie kryteria, które opisałem w poprzednim akapicie. Dokonujemy w nim niezbędnych zmian i wychodzimy z &lt;i&gt;Vima&lt;/i&gt; uprzednio zapisując plik. Skrypt zliczy nasze wciśnięcia klawiszy i zapyta się czy wysłać wynik na serwer.&lt;br /&gt;&lt;br /&gt;To wszystko. Zatem do dzieła!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-6499787278984624389?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/6499787278984624389/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/zagrajmy-w-golfa.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6499787278984624389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6499787278984624389'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/zagrajmy-w-golfa.html' title='Zagrajmy w golfa!'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-7441188657576356240</id><published>2011-11-04T14:04:00.000-07:00</published><updated>2011-11-04T14:05:36.965-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='makra'/><title type='text'>Makra w Vimie</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.clipartheaven.com/clipart/tools_%26_hardware/hammer_%26_nails_2.gif" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0"  width="50" src="http://www.clipartheaven.com/clipart/tools_%26_hardware/hammer_%26_nails_2.gif" /&gt;&lt;/a&gt;&lt;/div&gt;Tym razem chciałbym rozwinąć wątek słynnej już zasady wśród &lt;i&gt;vimowców&lt;/i&gt; czyli &lt;b&gt;"jak musisz coś zrobić więcej niż dwa razy powinieneś się zastanowić jak lepiej można to zrobić"&lt;/b&gt;. Zakładam, że osoba czytająca tego posta przeszła chociaż przez &lt;i&gt;vimtutor&lt;/i&gt;, bez tego raczej nie warto czytać dalej. Jeżeli jednak masz to już za sobą to przejdę od razu do rzeczy. Chyba nie wiele osób zdaje sobie tak naprawdę sprawę z potęgi drzemiącej w makrach. W tym poście postaram się omówić ich podstawy. Makra to po prostu nagrana sekwencja wykonywanych przez nas czynności. W przypadku &lt;i&gt;Vima&lt;/i&gt; są to po prostu wciskane kolejno klawisze. Poniżej omówię na konkretnym przykładzie jak utworzyć makro, przypisać do niego skrót klawiszowy oraz sprawić by nie zniknął po ponownym uruchomieniu &lt;i&gt;Vima&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tworzenie makr&lt;/b&gt;&lt;br /&gt;Nagrywanie makra rozpoczynamy wciskając &lt;b&gt;q&lt;/b&gt; oraz literę (a-z). Oznacza ona nazwę rejestru, do którego będą zapisywane wciskane klawisze. Ta informacja przyda nam się później do przypisywania skrótu klawiszowego. Gdy już rozpoczniemy nagrywanie to wykonujemy kolejno czynności, które chcielibyśmy powtarzać, a później kończymy nagrywanie wciskając klawisz &lt;b&gt;q&lt;/b&gt;. W tym momencie możemy wykonać makro wciskając &lt;b&gt;@&lt;/b&gt; oraz literę, którą wybraliśmy przy nagrywaniu makra.&lt;br /&gt;&lt;br /&gt;Podam teraz przykład bardzo prostego makra. Będzie ono wstawiać zaznaczony tekst w cudzysłowy, czyli jak zaznaczymy tekst &lt;i&gt;Ala ma kota.&lt;/i&gt; i wykonamy makro to w efekcie dostaniemy &lt;i&gt;"Ala ma kota."&lt;/i&gt;.&lt;br /&gt;Cały trik polega na tym, że wycinamy zaznaczony tekst, wstawiamy cudzysłów, wklejamy tekst i wstawiamy cudzysłów. Wykonujemy więc kolejno:&lt;br /&gt;1. zaznaczamy kawałek tekstu&lt;br /&gt;2. rozpoczynamy nagrywanie makra "a": qa&lt;br /&gt;3. wycinamy tekst: c&lt;br /&gt;4. wstawiamy cudzysłów: "&lt;br /&gt;5. wychodzimy z trybu wstawiania: &lt;esc&gt;&lt;br /&gt;6. wklejamy wycięty tekst: p&lt;br /&gt;7. przechodzimy do trybu wstawiania: a&lt;br /&gt;8. wstawiamy cudzysłów: "&lt;br /&gt;9. wychodzimy z trybu wstawiania: &lt;esc&gt;&lt;br /&gt;10. kończymy nagrywanie makra: q&lt;br /&gt;&lt;br /&gt;Teraz możemy przetestować działanie naszego makra poprzez zaznaczenie dowolnego tekstu i wciśnięcie &lt;b&gt;@a&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Przypisanie skrótu klawiszowego&lt;/b&gt;&lt;br /&gt;Skróty tworzy się poleceniem &lt;b&gt;map&lt;/b&gt;. Jego składnia z grubsza wygląda tak:&lt;br /&gt;:map &amp;lt;klawisz&amp;gt; &amp;lt;sekwencja&amp;gt;&lt;br /&gt;Aby więc &lt;i&gt;zmapować&lt;/i&gt; jakieś makro potrzebujemy nagranej sekwencji. Jak już wspominałem wcześniej makra zostają zapisane do wybranego rejestru, tzn. jeżeli na początku wcisnęliśmy &lt;b&gt;qa&lt;/b&gt; to nasza sekwencja znajduje się w rejestrze &lt;b&gt;a&lt;/b&gt;. Możemy więc w bardzo prosty sposób ją wyciągnąć. Mianowicie jak już wpiszemy np. &lt;i&gt;:map &amp;lt;C-t&amp;gt;&lt;/i&gt; to wciskamy &amp;lt;C-r&amp;gt; po czym naciskamy literę odpowiedniego rejestru czyli w naszym przypadku jest to &lt;b&gt;a&lt;/b&gt;. Sekwencja w ten sposób zostanie wklejona po czym pozostaje nam tylko zatwierdzić wciskając enter.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Zapisanie skrótu na stałe&lt;/b&gt;&lt;br /&gt;Każde wykonane polecenie &lt;i&gt;map&lt;/i&gt; jest pamiętane jedynie dopóki nie zamkniemy &lt;i&gt;Vima&lt;/i&gt;. Aby temu zapobiec musimy dopisać wywołanie tego polecenia w pliku &lt;i&gt;.vimrc&lt;/i&gt;. W tym celu otwieramy ów plik i dopisujemy:&lt;br /&gt;&lt;i&gt;map &amp;lt;skrót&amp;gt; &amp;lt;sekwencja&amp;gt;&lt;/i&gt;&lt;br /&gt;Jeżeli chcemy wkleić zawartość rejestru &lt;b&gt;a&lt;/b&gt; do treści pliku to wystarczy poprzedzić komendę &lt;b&gt;p&lt;/b&gt; kombinacją &lt;b&gt;"&amp;lt;litera_rejestru&amp;gt;&lt;/b&gt; czyli w naszym wypadku wciskamy &lt;b&gt;"ap&lt;/b&gt;. &lt;br /&gt;Nasza linijka powinna wyglądać mniej więcej tak:&lt;br /&gt;&lt;b&gt;map &amp;lt;C-t&amp;gt; c" ^]pa"^]&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;Na koniec kilka uwag:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Skrót &amp;lt;C-t&amp;gt; oznacza przytrzymanie przycisku &lt;i&gt;Ctrl&lt;/i&gt; oraz wciśnięcie klawisza &lt;i&gt;t&lt;/i&gt;,&lt;/li&gt;&lt;li&gt;Aby podejrzeć zawartość rejestrów bez wklejania można wywołać polecenie &lt;i&gt;:registers&lt;/i&gt;,&lt;/li&gt;&lt;li&gt;Skrót &amp;lt;C-r&amp;gt; służący do wklejania zawartości rejestrów (i nie tylko ale o tym może innym razem) można wywołać także w trybie wprowadzania,&lt;/li&gt;&lt;li&gt;Do powtarzania tylko ostatniej czynność możemy użyć klawisza &lt;b&gt;.&lt;/b&gt; (kropka) zamiast nagrywania makra, np. gdy chcemy po prostu wstawić jakiś tekst parę razy to wstawiamy go raz a potem &lt;b&gt;.&lt;/b&gt; (kropka) będzie robić to za nas.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Zachęcam wszystkich do radosnego tworzenia własnych makr!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-7441188657576356240?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/7441188657576356240/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/makra-w-vimie.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/7441188657576356240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/7441188657576356240'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/makra-w-vimie.html' title='Makra w Vimie'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-5420030014785847441</id><published>2011-11-02T03:39:00.000-07:00</published><updated>2011-11-02T03:39:29.958-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><title type='text'>Vim - ciekawsze pluginy</title><content type='html'>Jako, że na co dzień używam &lt;i&gt;Vima&lt;/i&gt; w pracy i w domu to zdążyłem już wyrobić sobie opinie na temat różnych &lt;i&gt;pluginów&lt;/i&gt;. Najciekawsze i jednocześnie najczęściej przeze mnie korzystane omówię poniżej:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2771"&gt;&lt;b&gt;Conque Shell&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Wtyczka, która czyni z &lt;i&gt;Vima&lt;/i&gt; terminal. Najlepiej omówić to na przykładzie. Ja używałem bardzo często tego &lt;i&gt;pluginu&lt;/i&gt; do integracji z klientem &lt;i&gt;MySQLa&lt;/i&gt;, tzn. dzięki &lt;b&gt;Conque Shell&lt;/b&gt; uruchamiamy z &lt;i&gt;Vima&lt;/i&gt; wybrany program a jego wyjście pojawi się nam w oknie. Na tym co wyświetli program będziemy mogli operować tak jak na każdym pliku tekstowym. Co więcej, w trybie wprowadzania komunikujemy się z programem. W moim przypadku bardzo często przeklejałem całe &lt;i&gt;SQLe&lt;/i&gt; i wyświetlał mi się wynik w postaci ładnej tabelki wewnątrz &lt;i&gt;Vima&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Tutaj widać 3 uruchomione procesy wewnątrz &lt;i&gt;Vima&lt;/i&gt; (bash,top,ipython): &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://conque.googlecode.com/svn/wiki/screenshot/unix.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="450" src="http://conque.googlecode.com/svn/wiki/screenshot/unix.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;:ConqueTermSplit &lt;nazwa_procesu_i_argumenty&gt;&lt;br /&gt;Powoduje uruchomienie procesu w oknie poniżej.&lt;/li&gt;&lt;li&gt;:ConqueTermVSplit &lt;nazwa_procesu_i_argumenty&gt;&lt;br /&gt;Powoduje uruchomienie procesu w oknie z lewej.&lt;/li&gt;&lt;li&gt;&amp;lt;F9&amp;gt;&lt;br /&gt;Przekleja zaznaczony tekst do procesu ostatnio uruchomionego.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Przykład: &lt;b&gt;:ConqueTermSplit bash&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=90"&gt;&lt;b&gt;vcscommand&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Bardzo fajny skrypt do integracji z wieloma systemami kontroli wersji (konkretnie CVS, SVN, SVK, git, bzr, hg). Sprawia on, że edytując dany plik nie musimy się zastanawiać czy aby zobaczyć jego historię musimy wywołać &lt;i&gt;svn log&lt;/i&gt; czy &lt;i&gt;git log&lt;/i&gt;, gdyż ten skrypt sam to sprawdzi i wywoła za nas. Oczywiście posiada on znacznie więcej opcji, chociażby wyświetlanie zmian za pomocą mechanizmu &lt;i&gt;Vima&lt;/i&gt; (&lt;b&gt;:VCSVimDiff &amp;lt;commit&amp;gt;&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;Obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;:VCSUpdate&lt;br /&gt;Odpowiada komendzie &lt;i&gt;svn update&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;:VCSCommit&lt;br /&gt;Otwiera okno na wpisanie komentarza. Po zamknięciu okna następuje &lt;i&gt;commit&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;:VCSDiff &amp;lt;commit&amp;gt;&lt;br /&gt;Wypisuje zmiany aktualnej wersji w stosunku do podanej w argumencie w taki sam sposób jak &lt;i&gt;svn diff&lt;/i&gt; (czy tam &lt;i&gt;git diff&lt;/i&gt; itp.).&lt;/li&gt;&lt;li&gt;:VCSVimDiff &amp;lt;commit&amp;gt;&lt;br /&gt;Tak samo jak powyżej tyle, że zmiany są wyświetlana za pomocą wewnętrznego mechanizmu &lt;i&gt;Vima&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;inne analogiczne polecenia takie jak: VCSLog, VCSBlame itd.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1658"&gt;&lt;b&gt;The NERD tree&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Nieco ciekawszy plugin do poruszania się po katalogach i plikach. Od razu dodam, bo może nie każdy wie, że próbując otworzyć katalog do edycji w &lt;i&gt;Vimie&lt;/i&gt; wyświetla się nam jego skład i możemy się po nim poruszać, np. &lt;b&gt;:e .&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Ten plugin oferuje nam dodatkowe możliwości jak np. zakładki, tworzenie i usuwanie plików oraz katalogów, filtrowanie zawartości katalogów, sortowanie itd.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://farm4.static.flickr.com/3001/2862367534_898b010158.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="450" src="http://farm4.static.flickr.com/3001/2862367534_898b010158.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;O obsłudze nie będę pisał, ponieważ ten &lt;i&gt;plugin&lt;/i&gt; podmienia standardową funkcjonalność listowania katalogu. O reszcie można poczytać w pomocy.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=311"&gt;&lt;b&gt;grep&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Prosty skrypt służący do &lt;i&gt;grepowania&lt;/i&gt; (wyszukiwania) po plikach. Wywołuje on program &lt;i&gt;grep&lt;/i&gt; z różnymi opcjami rozszerzając jego funkcjonalność o filtry. Tym właśnie różni się on od wbudowanego polecenia &lt;i&gt;vimgrep&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Podstawowa obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;:Grep &amp;lt;ciąg_do_wyszukania&amp;gt; &amp;lt;pliki&amp;gt;&lt;br /&gt;Wyszukuje w danym katalogu. Jako &amp;lt;pliki&amp;gt; można wstawić *&lt;br /&gt;&lt;/li&gt;&lt;li&gt;:RGrep &amp;lt;ciąg_do_wyszukania&amp;gt; &amp;lt;pliki&amp;gt;&lt;br /&gt;Tak samo tylko, że wyszukuje również w podkatalogach.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Filtry czyli w jakich plikach nie ma wyszukiwać można ustawić w pliku &lt;i&gt;.vimrc&lt;/i&gt;:&lt;br /&gt;let Grep_Skip_Dirs = '.svn CVS' &lt;br /&gt;let Grep_Skip_Files = '*.bak *~ *.swp tags *.vim' &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=492"&gt;&lt;b&gt;SQLUtilities&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Fajna wtyczka do formatowania zapytań. Oczywiście potrafi ona znacznie więcej ale w gruncie rzeczy formatowanie jest najciekawsze i najbardziej przydatne.&lt;br /&gt;&lt;br /&gt;Podstawowa obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;\sfs&lt;br /&gt;Powoduje, że zaznaczony kod &lt;i&gt;SQL&lt;/i&gt; zostaje ładnie sformatowany.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1528"&gt;&lt;b&gt;comments&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Również bardzo prosty skrypt do komentowania kodu. Rozpoznaje on automatycznie typ pliku i komentuje używając odpowiednich dla pliku symboli.&lt;br /&gt;&lt;br /&gt;Obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;ltCtrl-C&amp;gt;&lt;br /&gt;Komentuje linię bądź zaznaczenie.&lt;/li&gt;&lt;li&gt;&amp;lt;Ctrl-X&amp;gt;&lt;br /&gt;Odkomentowuje linię bądź zaznaczenie.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=3465"&gt;&lt;b&gt;TagBar&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Fantastyczny &lt;i&gt;plugin&lt;/i&gt; wyświetlający nam listę funkcji, klas, typów itp. w bocznym panelu.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://majutsushi.github.com/tagbar/tagbar1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="450" src="http://majutsushi.github.com/tagbar/tagbar1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Obsługa:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;lt;F9&amp;gt;&lt;br /&gt;Wyświetla bądź chowa panel boczny.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2620"&gt;&lt;b&gt;neocomplcache&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Kolejny świetny skrypt. Umożliwia on podpowiadania "w locie" przy pisaniu jak obserwujemy w różnych edytorach. Ten &lt;i&gt;plugin&lt;/i&gt; naprawdę polecam!&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_ci2yBnqzJgM/TD1PTolkTBI/AAAAAAAAADU/knJ3eniuHWI/s1600/omni_complete.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="450" src="http://2.bp.blogspot.com/_ci2yBnqzJgM/TD1PTolkTBI/AAAAAAAAADU/knJ3eniuHWI/s1600/omni_complete.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Co do obsługi to nie będę się również rozpisywał, najlepiej ze strony skryptu przekopiować domyślne ustawienia do pliku &lt;i&gt;.vimrc&lt;/i&gt; i potestować.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-5420030014785847441?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/5420030014785847441/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/vim-ciekawsze-pluginy.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5420030014785847441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5420030014785847441'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/11/vim-ciekawsze-pluginy.html' title='Vim - ciekawsze pluginy'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3001/2862367534_898b010158_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-1060299365242071904</id><published>2011-10-21T00:40:00.000-07:00</published><updated>2011-10-21T00:40:14.546-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='cvs'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><category scheme='http://www.blogger.com/atom/ns#' term='repozytorium'/><title type='text'>Git i (współ)praca z innymi systemami kontroli wersji</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://alien.slackbook.org/blog/wp-content/uploads/2010/02/gitlogo.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" width="50" src="http://alien.slackbook.org/blog/wp-content/uploads/2010/02/gitlogo.png" /&gt;&lt;/a&gt;&lt;/div&gt;Dzisiaj będzie bardziej konkretny post. Jako, że sam nie tak dawno zmieniałem pracę, w której niestety programiści używali &lt;i&gt;cvsa&lt;/i&gt; i nie mieli ochoty tego zmieniać (zgodnie z zasadą po co coś zmieniać skoro działa) to postanowiłem opisać jak sobie poradzić w takiej sytuacji.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Po co?&lt;/b&gt;&lt;br /&gt;Pytanie jak najbardziej zasadne. Odpowiedź jest prosta, po to aby móc korzystać z wszystkich zalet &lt;i&gt;gita&lt;/i&gt; nie robiąc rewolucji wśród reszty zespołu. Jakie to zalety? Jeżeli ktoś nie wie do odsyłam do &lt;a href="http://blog.matthew.org.pl/2009/06/29/dlaczego-git-jest-lepszy"&gt;link&lt;/a&gt; lub do &lt;a href="http://www.slideshare.net/xsolve/git4xsolve-5663557"&gt;link&lt;/a&gt;. Mnie cieszy najbardziej to, że mam całe sklonowane repo tylko dla siebie i mogę sobie &lt;i&gt;commitować&lt;/i&gt; ile mi się podoba. Nie bez znaczenia jest też łatwość tworzenia branchy i ich niemal automatyczne mergowanie. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 1 - tworzenie centralnego repozytorium &lt;i&gt;gita&lt;/i&gt; tylko do odczytu&lt;/b&gt;&lt;br /&gt;Na początku musimy stworzyć repozytorium, które będzie klonem tego głównego (czy to &lt;i&gt;cvs&lt;/i&gt; czy &lt;i&gt;svn&lt;/i&gt;). Najlepiej postawić je na maszynie, do której wszyscy będą mieli łatwy dostęp. Natomiast, co ważne to repozytorium będzie tylko ściągało zmiany z oryginalnego, nie będziemy żadnych zmian bezpośrednio do niego wrzucać! &lt;br /&gt;&lt;br /&gt;W przypadku &lt;i&gt;cvsa&lt;/i&gt; robimy to tak:&lt;br /&gt;Przed importem należy ustawić zmienną &lt;i&gt;CVSROOT&lt;/i&gt; w ten sposób:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; CVSROOT=:method:user@host:/path/to/CVS/root cvs login&lt;/i&gt;&lt;br /&gt;Później tworzymy sobie jakiś katalog, w którym będzie znajdowało się repozytorium i przechodzimy do niego. I wreszcie importujemy wszystko z &lt;i&gt;cvsa&lt;/i&gt;:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-cvsimport -p x -v -d :method:user@host:/path/to/CVS/root/someproject&lt;/i&gt;&lt;br /&gt;&lt;i&gt;method&lt;/i&gt; oznacza typ serwera, np. &lt;i&gt;pserver&lt;/i&gt;. Możemy wprowadzić kilka drobnych udogodnień:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git config cvsimport.module someproject&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git config cvsimport.r cvs&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git config cvsimport.d $CVSROOT&lt;/i&gt;&lt;br /&gt;W ten sposób przy aktualizowaniu repozytorium nie będziemy musieli prawie nic podawać.&lt;br /&gt;&lt;br /&gt;A w &lt;i&gt;svnie&lt;/i&gt; tak: &lt;br /&gt;Na początku tworzymy sobie katalog, gdzie chcemy trzymać repozytorium i przechodzimy do niego. Później wywołujemy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-svn init svn://host/trunk/someproject&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-svn fetch&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-svn rebase&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Zaznaczam w tym miejscu, że jeżeli mamy jedno repozytorium z wieloma projektami to warto zrobić kilka repozytoriów &lt;i&gt;gitowych&lt;/i&gt; - każdy &lt;i&gt;per&lt;/i&gt; projekt. Stąd &lt;i&gt;someproject&lt;/i&gt; w podanych wyżej wywołaniach.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 2: sklonowanie repozytorium &lt;i&gt;gita&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;Teraz klonujemy sobie świeżo utworzone repozytorium, na którym będziemy pracować:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git clone /path/to/git dir&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UWAGA:&lt;/b&gt; pracujemy tylko na branchach. Zaraz po sklonowaniu warto utworzyć sobie jakiegoś brancha aby nie pracować na branchu master. Jest to ważne przy późniejszym przerzucaniu zmian. Brancha możemy utworzyć w ten sposób:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git checkout -b nazwa_brancha&lt;/i&gt;&lt;br /&gt;Utworzymy w ten sposób brancha bazującego na branchu master.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 3: wrzucanie zmian&lt;/b&gt;&lt;br /&gt;Jak już popracowaliśmy to pora na &lt;i&gt;commit&lt;/i&gt;. Robi się go w ten sposób:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git add zmienione_pliki&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git commit&lt;/i&gt;&lt;br /&gt;Listę zmienionych plików możemy podejrzeć w ten sposób:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git status&lt;/i&gt;&lt;br /&gt;Ewentualnie aby nie dodawać do &lt;i&gt;commita&lt;/i&gt; każdego pliku z osobna możemy użyć:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git commit -a&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 4: formatowanie patcha&lt;/b&gt;&lt;br /&gt;OK, mamy wrzucone zmiany do lokalnego repo. Jeżeli zrobiliśmy tylko jednego &lt;i&gt;commita&lt;/i&gt; to sprawa jest banalna:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git format-patch -1&lt;/i&gt;&lt;br /&gt;W ten sposób &lt;i&gt;git&lt;/i&gt; utworzy nam plik o nazwie &lt;i&gt;0001-commit_message.patch&lt;/i&gt;&lt;br /&gt;Jeżeli zrobiliśmy więcej &lt;i&gt;commitów&lt;/i&gt; to mamy dwa wyjścia:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;jeżeli chcemy mieć w oryginalnym repozytorium widoczne wszystkie &lt;i&gt;commity&lt;/i&gt; jakie zrobiliśmy w &lt;i&gt;gicie&lt;/i&gt; to wywołujemy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git format-patch origin..HEAD&lt;/i&gt;&lt;br /&gt;To polecenie utworzy nam serię patchy, każdy &lt;i&gt;per commit&lt;/i&gt;&lt;/li&gt;&lt;li&gt;jeżeli natomiast chcemy mieć jeden zbiorczy &lt;i&gt;commit&lt;/i&gt; to musimy sobie &lt;i&gt;zmergować&lt;/i&gt; zmiany. W tym celu wracamy na chwilę do brancha master:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git checkout master&lt;/i&gt;&lt;br /&gt;Tworzymy sobie tymczasowego brancha:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git checkout -b merge_branch&lt;/i&gt;&lt;br /&gt;I mergujemy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git merge --squash nazwa_brancha_ze_zmianami&lt;/i&gt;&lt;br /&gt;Opcja &lt;i&gt;--squash&lt;/i&gt; powodują złączenie wszystkich &lt;i&gt;commitów&lt;/i&gt; w jeden.&lt;br /&gt;Commitujemy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git commit&lt;/i&gt;&lt;br /&gt;Po czym tworzymy patcha:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git format-patch -1&lt;/i&gt;&lt;br /&gt;Może teraz przejść na innego brancha i usunąć ten tymczasowy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git checkout master &amp;&amp; git branch -d merge_branch&lt;/i&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Krok 5: wrzucanie zmian do oryginalnego repozytorium&lt;/b&gt;&lt;br /&gt;Gdy mamy już patcha lub patche to przenosimy je do katalogu z &lt;i&gt;checkoutem&lt;/i&gt; czy to &lt;i&gt;cvsa&lt;/i&gt; czy &lt;i&gt;svna&lt;/i&gt;. Po czym wywołujemy:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; patch -p1 &lt; plik_patcha&lt;/i&gt;&lt;br /&gt;Przeglądamy sobie czy wszystko jest OK i &lt;i&gt;commitujemy&lt;/i&gt; jak zwykle.&lt;br /&gt;Jest też sposób automatyczny, którego nie polecam. Lepiej mieć nad wszystkim kontrolę. Jeżeli ktoś chce to niech poszuka informacji o &lt;i&gt;git-svn dcommit&lt;/i&gt; lub &lt;i&gt;git cvsexportcommit&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 6: aktualizacja centralnego repo &lt;i&gt;gita&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;Przechodzimy do katalogu z naszym centralnym repo &lt;i&gt;gita&lt;/i&gt; i wywołujemy: &lt;br /&gt;&lt;br /&gt;Dla &lt;i&gt;cvsa&lt;/i&gt;:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git cvsimport -a -p -x&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Dla &lt;i&gt;svna&lt;/i&gt;:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-svn fetch&lt;/i&gt;&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git-svn rebase&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Krok 7: aktualizacja lokalnego repo &lt;i&gt;gita&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;W katalogu z naszym lokalnym repo wykonujemy (na każdym branchu, który chcemy zaktualizować):&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt; git pull origin master&lt;/i&gt;&lt;br /&gt;Należy pamiętać, że aby uniknąć konfliktów przy nakładaniu patcha należy zawsze przed utworzeniem brancha tymczasowego zaktualizować brancha master jak również &lt;i&gt;checkout&lt;/i&gt; originalnego repo.&lt;br /&gt;&lt;br /&gt;I tak w kółko :)&lt;br /&gt;Wydaje się, że to dużo roboty. Powiem tak, jak już się raz to wszystko zrobi to potem nie ma jej w cale więcej niż dotychczas (oczywiście poza przerzucaniem zmian). Na przykład aktualizowanie repozytoriów można zautomatyzować jakimś prostym skryptem. Korzyści płynące z pracy z &lt;i&gt;gitem&lt;/i&gt; są bezcenne. Naprawdę warto spróbować.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-1060299365242071904?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/1060299365242071904/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/git-i-wspopraca-z-innymi-systemami.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1060299365242071904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1060299365242071904'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/git-i-wspopraca-z-innymi-systemami.html' title='Git i (współ)praca z innymi systemami kontroli wersji'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-5073530537500314448</id><published>2011-10-18T13:06:00.000-07:00</published><updated>2011-10-18T23:23:55.828-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wolność'/><category scheme='http://www.blogger.com/atom/ns#' term='intelektualna'/><category scheme='http://www.blogger.com/atom/ns#' term='oprogramowanie'/><title type='text'>Własność intelektualna</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.codziennikprawny.pl/images/media/thumbs/sadurski_10-6_th640x480.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0"  width="50" src="http://www.codziennikprawny.pl/images/media/thumbs/sadurski_10-6_th640x480.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;Ostatnio słuchając audycji o tytułowej własności intelektualnej na &lt;a href="http://kontestacja.com"&gt;kontestacji&lt;/a&gt; przyszła mi do głowy pewna myśl. Wyobraźmy sobie, że ktoś stworzył maszynkę, która potrafi kopiować dowolne przedmioty, np. wkładamy do niej jabłko, ona sobie tam chwilę pohałasuje i w efekcie dostajemy drugie dokładnie takie samo jabłko. Teraz wyobraźmy sobie, że kupując na straganie jedno jabłko sprzedawca każe nam podpisać umowę, w której zobowiązujemy się do nie kopiowania takiego jabłka. Wydaje się, że zdrowo myślący człowiek popukał by się w czoło i pomyślał "co mi tu będzie jakiś pe**ł mówił co ja mam robić ze swoim jabłkiem" po czym poszedłby do innego sprzedawcy. W efekcie nasz kochany sprzedawca nie wiele by zarobił. &lt;br /&gt;&lt;br /&gt;Dlaczego więc w umowach (licencjach) dot. oprogramowania powszechnie akceptujemy takie zapisy? Odpowiedź jest dość prosta. Nie dość, że istnieje prawo chroniące takie umowy to jeszcze wszędzie wokół karmią nas stwierdzeniami typu, że nie moralnym jest godzić się na kopiowanie oprogramowania bez płacenia za to. Tak naprawdę nie o zapłatę tu chodzi. Wszechobecnym argumentem są &lt;i&gt;ogromne straty&lt;/i&gt;, które są liczone w oparciu o błędne przekonanie, że gdyby nie kopiowanie to ci wszyscy ludzie by to oprogramowanie kupili. Ale przecież programów nie można traktować na równi z produkcją, dajmy na to, noży. Tworzenie oprogramowania to nie przemysł lecz usługi. I bardziej adekwatne byłoby w moim odczuciu płacenie za wykonaną usługę, tzn. &lt;b&gt;usługę napisania programu&lt;/b&gt;. Jasne, że wówczas cena oprogramowania byłaby dużo droższa ale i to oprogramowanie byłoby znacznie łatwiej dostępne. Powiedzmy, że jeden człowiek nie byłby w stanie kupić sobie żadnego programu ale jak zrzuci się np. gmina, miasto, firma czy inna grupa ludzi to wszyscy mogą później z niego korzystać. Tak więc i programiści więcej zarobią, a i konsumenci więcej mieć będą a do tego jeszcze wytworzy się silna konkurencja więc również jakość oprogramowania wzrośnie. &lt;br /&gt;&lt;br /&gt;Konkludując, uważam, że to nie żadne państwo powinno mówić jakie umowy są moralne a jakie nie, a już tym bardziej nie powinno niczego narzucać odgórnie (jak w przypadku praw autorskich). Zresztą widać dzisiaj, że mimo odgórnego zakazu ludzie i tak kopiują na potęgę. Nie wiem jak Wam ale dla mnie jest to sygnał, że konsumenci chcą jednak czegoś innego niż chce państwo. Niech zatem rynek (czyt. konsumenci) o tym zadecyduje!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-5073530537500314448?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/5073530537500314448/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/wasnosc-intelektualna.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5073530537500314448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/5073530537500314448'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/wasnosc-intelektualna.html' title='Własność intelektualna'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-2075090822833258582</id><published>2011-10-17T13:54:00.000-07:00</published><updated>2011-10-17T13:54:46.715-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pisanie'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='edytor'/><category scheme='http://www.blogger.com/atom/ns#' term='programowanie'/><title type='text'>Niezastąpiony, niepokonany, najlepszy</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://vi-improved.org/success_vim.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" width="50" src="http://vi-improved.org/success_vim.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;Jak już wspominałem ostatnio kontynuuję cykl o tym jak stać się lepszym programistą. Na pierwszy ogień idzie zatem &lt;a href="http://www.vim.org"&gt;Vim&lt;/a&gt;. Na wstępie należałoby przede wszystkim zadać sobie pytanie "po co?". Po co odchodzić od &lt;i&gt;przyjemnych&lt;/i&gt;, &lt;i&gt;intuicyjnych&lt;/i&gt; edytorów (ba, całych IDE) na rzecz kompletnie nieporęcznego, nieintuicyjnego i minimalistycznego (tylko z wyglądu) &lt;b&gt;Vima&lt;/b&gt;. Odpowiedzi mógłbym udzielić całe mnóstwo ale skupię się na tych najważniejszych. Przedstawię je opisując od razu każdą z nich z osobna.&lt;br /&gt;&lt;br /&gt;Rozpocznę od flagowej idei, która przyświecała jeszcze staremu &lt;i&gt;vi&lt;/i&gt; czyli ergonomia i wygoda pisania. W czasach kiedy pisanie czegoś na komputerze nie należało do rzeczy najprzyjemniejszych - głównie z uwagi na szybkość komputerów - powstał edytor zwany &lt;i&gt;vi&lt;/i&gt;. Jak wieść niesie programiści prześcigali się w jak najmniejszej ilości naciśnięć kombinacji klawiszy aby wykonać dane zadanie (&lt;i&gt;notabene&lt;/i&gt; dzisiaj podobna inicjatywa również istnieje, więcej informacji &lt;a href="http://vimgolf.com"&gt;tutaj&lt;/a&gt;). Twórcy &lt;i&gt;vi&lt;/i&gt; skupili się głównie na dwóch aspektach:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;edycja i przeglądanie tekstu musi być jak najszybsze przy jak najmniejszym udziale człowieka,&lt;/li&gt;&lt;li&gt;komendy, skróty klawiszowe powinny być zaprojektowane w sposób jak najbardziej spójny, logiczny, łatwy do zapamiętania oraz rozłożone tak aby człowiek nie musiał odrywać dłoni od klawiatury (ani wyginać nadgarstków).&lt;/li&gt;&lt;/ul&gt;Specjalnie nie napiszę co takiego wymyślili, ponieważ tego dowiesz się w trakcie nauki (do której gorąco namawiam). Warto tutaj wspomnieć, że &lt;b&gt;Vim&lt;/b&gt; jest kompletnie od nowa przepisanym starym &lt;i&gt;vi&lt;/i&gt; i udoskonalonym - stąd nazwa &lt;i&gt;Vi IMproved&lt;/i&gt; (co prawda na początku brzmiała &lt;i&gt;Vi IMitation&lt;/i&gt;, ale jak tylko twórcy przepisali 99% funkcjonalności &lt;i&gt;vi&lt;/i&gt; to zmienili nazwę). W tym miejscu mogę jeszcze nadmienić pomniejsze zalety, które pozostały po poprzedniku takie jak bardzo małe zużycie zasobów systemowych do tego stopnia, że można z powodzeniem pracować zdalnie na słabym łączu - dodam także, że &lt;b&gt;Vim&lt;/b&gt; jest jednym z nielicznych edytorów, które potrafą z powodzeniem pracować z plikami, które zajmują więcej miejsca niż zainstalowana ilość RAMu w komputerze.&lt;br /&gt;&lt;br /&gt;Stary &lt;i&gt;vi&lt;/i&gt; to podstawa, która narzuca filozofię działania i logikę myślenia. Jednakże &lt;b&gt;Vim&lt;/b&gt; oferuje znacznie więcej. Przede wszystkim czyni on z edytora swego rodzaju powłokę, tzn. jak z każdą powłoką komunikujemy się pewnym językiem skryptowym. Nie inaczej jest w tym przypadku, język skryptowy o którym mowa zwie się &lt;b&gt;VimL&lt;/b&gt; (od &lt;i&gt;Vim Language&lt;/i&gt;). Do czego on może służyć? Przede wszystkim do pisania pluginów ale nie tylko. Prostym przykładem, który mógłbym podać jest wypisanie w każdej linii kolejnej liczby (czyli prosta pętla i po sprawie) czy na przykład posortowanie zaznaczonego tekstu lub wczytanie wyjścia odpalonego programu zewnętrznego (tak, &lt;b&gt;Vim&lt;/b&gt; potrafi odpalać programy konsolowe). W tym celu nie musimy ani zapisywać tego kodu (choć oczywiście możemy), ani go kompilować (język jest interpretowany) - wystarczy że go po prostu wpiszemy (nie chcę tutaj przedstawiać dokładnie jak to się robi, podpowiem tylko, że wygląda to podobnie jak w przypadku interaktywnych interpreterów). Na sam koniec wspomnę tylko o takich standardowych funkcjach jak zaznaczanie blokowe, wyrażenia regularne, podświetlanie składni, obsługa tagów, podpowiadanie, zintegrowana pomoc, obsługa okien, wielu schowków, buforów, możliwość definiowania prostych snippetów (właściwie &lt;i&gt;abbreviation&lt;/i&gt;, mechanizm snippetów dostarczają pluginy) i tak można by wymieniać bez końca. Nie będę wypisywał wszystkich możliwości bo raz, że ich wszystkich nie poznałem (ciekawe czy jest to w ogóle możliwe), dwa że zajęło by to mnóstwo miejsca i trzy, że bym Cię, drogi czytelniku po prostu zanudził. Dlatego skupię się teraz na tym jak najszybciej się nie tylko nauczyć ale i pokochać &lt;b&gt;Vima&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;Każda dystrybucja przychodzi z programem (skryptem) o nazwie &lt;b&gt;vimtutor&lt;/b&gt;. Po jego wykonaniu odpali nam się &lt;b&gt;Vim&lt;/b&gt; z wczytanym samouczkiem, który należy czytać i wykonywać podane w nim ćwiczenia. Przerobienie samouczka zajmuje ok. 40 min ale należy go zrobić solidnie, tzn. jak napisane jest "nie używaj strzałek" to ich nie używaj. Naprawdę należy pozbyć się nawyków, które poznałeś do tej pory. Aby utrwalić sobie nabytą w ten sposób wiedzę używaj &lt;b&gt;Vima&lt;/b&gt; gdzie tylko się da. Na początku będziesz pracować nieco wolniej ale później twoja produktywność wzrośnie kilkukrotnie!&lt;br /&gt;&lt;br /&gt;Po samouczku warto poczytać pomoc, przejrzeć takie strony jak &lt;a href="http://vim.wikia.com/wiki/Vim_Tips_Wiki"&gt;Vim Tips&lt;/a&gt;, poczytać parę książek (polecam "Hacking Vim"), poprzeglądać pluginy (&lt;a href="http://www.vim.org/scripts/index.php"&gt;tutaj&lt;/a&gt;) czy też zapisać się na listę dyskusyjną. Ale najważniejsze to po prostu go używać.&lt;br /&gt;&lt;br /&gt;Właściwie mógłbym zakończyć ten przydługi wpis krótką sentencją używaną wśród &lt;i&gt;vimowców&lt;/i&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Jeżeli musisz coś zrobić więcej niż dwa razy to powinieneś się zastanowić jak można to lepiej wykonać.&lt;/i&gt;&lt;/blockquote&gt;Jeżeli interesują Cię konkrety (co i jak zrobić, czego użyć itp.) to daj znać w komentarzu. Postaram się szybko odpowiedzieć, a jak będzie większy odzew to mogę utworzyć jakiegoś większego posta na ten temat. No to pozostaje mi tylko życzyć powodzenia!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-2075090822833258582?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/2075090822833258582/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/niezastapiony-niepokonany-najlepszy.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2075090822833258582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2075090822833258582'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/niezastapiony-niepokonany-najlepszy.html' title='Niezastąpiony, niepokonany, najlepszy'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-498661688875071297</id><published>2011-10-14T08:43:00.000-07:00</published><updated>2011-10-14T08:44:31.609-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='jakość'/><category scheme='http://www.blogger.com/atom/ns#' term='programowanie'/><category scheme='http://www.blogger.com/atom/ns#' term='efektywność'/><title type='text'>Efektywność i jakość</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.lks-zelow.com/Gify/Reszta/podium%20(2).gif" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" width="50" src="http://www.lks-zelow.com/Gify/Reszta/podium%20(2).gif" /&gt;&lt;/a&gt;&lt;/div&gt;Postanowiłem rozpocząć pewien cykl postów na temat jak wyróżniać się (pod względem umiejętności) wśród programistów. Genaralnie jak być jeśli nie najlepszym programistą w zespole to chociaż w ścisłej czołówce. Już na samym początku muszę obalić jeden krążący wokół mit. Nie wystarczy dużo czytać o nowych technologiach czy wzorcach ani też nie wystarczy tylko odpowiednio dużo programować. Tak naprawdę wszystko zależy od tego jak &lt;b&gt;abstrakcyjnie&lt;/b&gt; potrafisz myśleć. Wiem, trudne słowo ale postaram się je nieco przybliżyć.&lt;br /&gt;&lt;br /&gt;Należy zacząć od słowa abstrakcja, ponieważ nie wszyscy chyba to słowo rozumieją. Jest to jedna z najważniejszych rzeczy, gdyż jak niektórzy twierdzą, tylko ona tak naprawdę odróżnia nas od zwierząt. Zapytacie pewnie, jak to? Przecież chodzimy na dwóch nogach, mamy wyprostowaną postawę a co najważniejsze mamy jeszcze świadomość. Otóż są to rzeczy sporne bo jakby się uprzeć to i małpa czasami chodzi na dwóch nogach, a co do świadomości to nie sądzę aby człowiek miał na nią monopol. Wracając do abstrakcji, to dzięki niej możemy w ogóle cokolwiek tworzyć. Abstrakcja to inaczej oderwanie, uogólnienie. Myślenie abstrakcyjne pozwala nam więc uogólniać - wyciągać cechy wspólne, stałe, niezmienne. Po co? Po to by te rzeczy ze sobą łączyć, ulepszać, naśladować oraz tworzyć zupełnie nowe.&lt;br /&gt;&lt;br /&gt;Jak ma się to do programowania? Cóż, abstrakcje w programowaniu to przede wszystkim typy danych. Tworzymy je po to aby ułatwiać sobie pracę, np:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;skracać ilość kodu potrzebnego do napisania, &lt;/li&gt;&lt;li&gt;dbać o poprawność programu, &lt;/li&gt;&lt;li&gt;ułatwiać sobie dzięki nim późniejsze modyfikacje.&lt;/li&gt;&lt;/ul&gt;Oczywiście są też największym źródłem problemów. Począwszy od języków dynamicznych gdzie praktycznie nie mamy możliwości wymuszenia prawidłowego korzystania z nich po chociażby (niewłaściwe) rzutowania w innych językach.&lt;br /&gt;&lt;br /&gt;Co cechuje dobrego programistę? Dobry programista przede wszystkim myśli typami. Można porównać to trochę do Matrixa gdzie jeden z bohaterów nie widział już tych wszystkich liczb i znaków na ekranie ale same kobiety i mężczyzn. Co to oznacza w praktyce? Np. pisząc tekst nie widzimy znaków - widzimy słowa, zdania paragrafy, linie itp. Czytając kod nie widzimy &lt;i&gt;do, while, for, function, mov rax, 0xff&lt;/i&gt; ale widzimy pętle, funkcje, obiekty, instrukcje. Tworząc kod widzimy interakcje i powiązania między typami. Jeżeli w ten sposób funkcjonujesz to nie irytuje Cię poprawianie błędów, nie nudzisz się przy rysowaniu UMLi, z chęcią przeglądasz cudzy kod. Tak, to wszystko sprawia Ci ogromną przyjemność. Nie wystarczy więc być &lt;i&gt;geekiem&lt;/i&gt; aby być dobrym programistą (choć większość programistów nawet &lt;i&gt;geekami&lt;/i&gt; nie jest). Oprócz wiedzy jak działa komputer (czyli znajomości systemów operacyjnych, sieci, imperatywnych języków programowania i assemblera ) trzeba jeszcze umieć i być nieźle wprawionym w myśleniu abstrakcyjnym.&lt;br /&gt;&lt;br /&gt;Jak więc się wprawić? Oto kilka rad:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Do pisania używaj vima - nauczysz się nie tylko jak szybko pisać i edytować tekst ale także widzieć obiekty w tekście i na nich działać. Szybko oznacza tu naprawdę szybko (o wiele szybciej niż chociażby w jakimś Visual Studio). Korzystanie z vima przypomina programowanie, np. wydajesz instrukcje (naciskając odpowiedni ciąg kombinacji klawiszy) usunięcia wszystkiego od miejsca, w którym znajduje się kursor do drugiego wystąpienia litery f.&lt;/li&gt;&lt;li&gt;Dokumenty twórz w LaTeXu - wówczas będziesz widział w tekście strony, rozdziały, różnego rodzaju bloki. Nauczysz się widzieć dokument patrząc na jego źródło. Ponadto twoje dokumenty będą wyglądały nieziemsko w porównaniu do tych z różnych pakietów office.&lt;/li&gt;&lt;li&gt;Naucz się kilku języków deklaratywnych (z rodziny funkcyjnych i logicznych) - tam praktycznie programuje się samymi typami.&lt;/li&gt;&lt;li&gt;Wreszcie staraj się używać jak najbardziej niskopoziomowych narzędzi, pisz skrypty, debuguj w gdb itp. - te narzędzia są znacznie szybsze od graficznych. Mam tu namyśli czas jaki Ty spędzisz nad zrobieniem czegoś, oczywiście gdy już się wprawisz. Zaoszczędzisz sporo czasu oraz wiele się nauczysz.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Myślę, że w kolejnych postach opiszę pokrótce powyższe punkty. Mam nadzieję, że zmotywuje to chociaż niektórych do pracy nad sobą :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-498661688875071297?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/498661688875071297/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/efektywnosc-i-jakosc.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/498661688875071297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/498661688875071297'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/efektywnosc-i-jakosc.html' title='Efektywność i jakość'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-1931063088452016260</id><published>2011-10-13T13:59:00.000-07:00</published><updated>2011-10-13T13:59:10.678-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='wolność'/><category scheme='http://www.blogger.com/atom/ns#' term='oprogramowanie'/><title type='text'>Wolność a otwarte oprogramowanie</title><content type='html'>Nie zastanawiało Was nigdy jak się ma idea wolności do otwartego oprogramowania? To pytanie kieruję oczywiście do ludzi znających jedno i drugie. Pytam bo mi do głowy przyszedł od razu pewien obrazek: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.humor4u.info/zdjecia/smieszne3/150/opensource.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" width="300" src="http://www.humor4u.info/zdjecia/smieszne3/150/opensource.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Moje pierwsze skojarzenie nie bierze się znikąd. Komunizm jak każdy socjalizm cechuje brak własności prywatnej oraz brak prawa do decydowania o sobie ale czy otwarte oprogramowanie takie właśnie jest? Niczyje? Przymusowe? Oczywiście, że nie. Wystarczy spojrzeć nieco głębiej. &lt;br /&gt;&lt;br /&gt;Po pierwsze tworząc oprogramowanie &lt;i&gt;open-source&lt;/i&gt; nikt nie zrzeka się praw do niego (w tym prawa własności). Wręcz przeciwnie - gdyby ktokolwiek spróbował przywłaszczyć je sobie to  złamał by prawo! A przede wszystkim złamał by umowę na jaką się zgodził używając tego typu programów. Ta umowa (licencyjna) przewiduje bowiem, że owszem możesz robić sobie z nim co się Tobie podoba ale pod warunkiem, że będziesz rozprowadzał je dalej na takich samych zasadach.&lt;br /&gt;&lt;br /&gt;Po drugie takie oprogramowanie nie ma jednego wspólnego właściciela. W każdym projekcie jest to inna osoba bądź inna grupa osób. Każdy kto dokłada swoją cegiełkę staje się współwłaścicielem projektu. Ale tylko tego projektu a nie każdego innego.&lt;br /&gt;&lt;br /&gt;Po trzecie i najważniejsze - nikt nikogo do tego &lt;b&gt;nie zmusza&lt;/b&gt;! Otwarte oprogramowanie jest tworzone na tej samej zasadzie co każde inne oprogramowanie, jak każda inna działalność wielu osób - na zasadzie &lt;b&gt;dobrowolnie zawartej umowy&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;Dajmy na to idąc do firmy tworzącej zamknięte oprogramowanie i chcąc je współtworzyć przyjmujemy przecież dobrowolnie warunki na jakich jest ono tworzone (m. in. np. że kod źródłowy nie wyjdzie poza tę firmę). Tak samo dołączając się do projektu &lt;i&gt;open-source&lt;/i&gt; zgadzamy się również na pewne określone warunki. Nie odbiera to nam ani kropli wolności. Zawsze mamy wybór!&lt;br /&gt;&lt;br /&gt;Czy jest to zamknięte czy otwarte w kwestii wolności nie ma znaczenia. To tak jakbyśmy producenta pralek oskarżali o komunizm tylko dlatego, że pozwala klientom rozkręcać swoje pralki. Nikt nikomu tu niczego nie narzuca ani do niczego nie zmusza. Jeżeli zatem ktoś sprzedając pralki, które można samodzielnie naprawiać, rozkręcać i w ogóle robić z nimi wszystko, świetnie trzyma się na rynku i zyskuje co raz większą popularność to oznacza to tylko jedno - &lt;b&gt;ludzie tego potrzebują&lt;/b&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-1931063088452016260?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/1931063088452016260/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/wolnosc-otwarte-oprogramowanie.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1931063088452016260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/1931063088452016260'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/wolnosc-otwarte-oprogramowanie.html' title='Wolność a otwarte oprogramowanie'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-3070934715104364743</id><published>2011-10-13T01:04:00.000-07:00</published><updated>2011-10-13T01:09:40.888-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monitory'/><category scheme='http://www.blogger.com/atom/ns#' term='xmobar'/><category scheme='http://www.blogger.com/atom/ns#' term='xmonad'/><title type='text'>XMonad i XMobar na wielu monitorach</title><content type='html'>Ostatnio dostałem w robocie drugi monitor. Nic nadzwyczajnego, ot takie zwykłe BenQ 22". Jako, że wszędzie, gdzie tylko jest taka możliwość używam &lt;i&gt;XMonada&lt;/i&gt; jako menadżera okien więc i tym razem postanowiłem zobaczyć jak sobie radzi z wieloma monitorami. Muszę przyznać, że jest jeszcze lepiej (!) niż na jednym monitorze w przeciwieństwie do innych menadżerów jak &lt;i&gt;Gnome&lt;/i&gt; czy &lt;i&gt;KDE&lt;/i&gt;, gdzie działa to fatalnie (nigdy nie wiem, na którym monitorze wyświetli się okno i takie tam). &lt;br /&gt;&lt;br /&gt;Oczywiście wszystko działa &lt;i&gt;out of the box&lt;/i&gt; ale nie spodobała mi się jedna rzecz. Otóż mój wspaniały panel (&lt;i&gt;XMobar&lt;/i&gt;) wyświetlał się tylko na jednym monitorze. Niestety nie znalazłem w sieci żadnego poradnika jak zrobić aby wyświetlał się na dwóch więc jakby ktoś też tego potrzebował to zamieszczam poniżej instrukcję jak to zrobić (jako, że znam &lt;i&gt;Haskella&lt;/i&gt; to nie było to dla mnie wielkim problemem).&lt;br /&gt;&lt;br /&gt;Po pierwsze musimy utworzyć dwie instancje &lt;i&gt;XMobara&lt;/i&gt; ze wskazaniem, na którym monitorze ma się uruchomić. W tym celu edytujemy nasz &lt;i&gt;xmonad.hs&lt;/i&gt; i dorzucamy do funkcji &lt;b&gt;main&lt;/b&gt; takie 2 linijki:&lt;br /&gt;&lt;pre class="brush: js"&gt;xmproc &lt;- spawnPipe "xmobar -x 0"&lt;br /&gt;xmpro2 &lt;- spawnPipe "xmobar -x 1 ~/.xmobarrc2"&lt;br /&gt;&lt;/pre&gt;Parametr &lt;i&gt;x&lt;/i&gt; oznacza numer monitora zaczynając od 0. Ja akurat potrzebowałem aby każdy panel wyglądał nieco inaczej (głównie ze względu na program &lt;i&gt;trayer&lt;/i&gt;, który ma się znaleźć tylko na jednym monitorze) więc druga instancja uruchamia się z innym plikiem konfiguracyjnym (&lt;i&gt;.xmobarrc2&lt;/i&gt;).W drugim kroku musimy powiedzieć menadżerowi aby wysyłał takie same dane do dwóch paneli. Edytujemy więc w &lt;i&gt;xmonad.hs&lt;/i&gt; funkcję &lt;b&gt;logHook&lt;/b&gt;:&lt;pre class="brush: js"&gt;xmonad $ defaultConfig&lt;br /&gt;                { manageHook = manageDocks &lt;+&gt; manageHook defaultConfig&lt;br /&gt;                , layoutHook = avoidStruts $ smartBorders $ tabbed shrinkText myTabConfig ||| layoutHook defaultConfig&lt;br /&gt;                , logHook = dynamicLogWithPP $ xmobarPP&lt;br /&gt;                        { ppOutput = \s -&gt; do&lt;br /&gt;                              hPutStrLn xmproc s&lt;br /&gt;                              hPutStrLn xmpro2 s&lt;br /&gt;                        , ppTitle = xmobarColor "green" "" . shorten 150&lt;br /&gt;                        }              &lt;br /&gt;                , modMask = mod4Mask&lt;br /&gt;                , terminal = "gnome-terminal"&lt;br /&gt;                , keys = \c -&gt; myKeys c `M.union` keys defaultConfig c&lt;br /&gt;                }&lt;br /&gt;&lt;/pre&gt;Interesuje nas linijka przy &lt;b&gt;logHook&lt;/b&gt; a dokładnie &lt;i&gt;ppOutput&lt;/i&gt;. Nie ma tam nic nadzwyczajnego, po prostu przekazujemy dane do obydwu instancji &lt;i&gt;XMobara&lt;/i&gt;. I to właściwie wszystko.Mój cały config do &lt;i&gt;XMonada&lt;/i&gt; dostępny jest &lt;a href="http://pastebin.com/kKnAphT5"&gt;tutaj&lt;/a&gt;.A do &lt;i&gt;XMobarów&lt;/i&gt; kolejno &lt;a href="http://pastebin.com/7rxdshzm"&gt;tutaj&lt;/a&gt; i &lt;a href="http://pastebin.com/mHFJLHeq"&gt;tutaj&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-3070934715104364743?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/3070934715104364743/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/xmonad-i-xmobar-na-wielu-monitorach.html#comment-form' title='Komentarze (2)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/3070934715104364743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/3070934715104364743'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/xmonad-i-xmobar-na-wielu-monitorach.html' title='XMonad i XMobar na wielu monitorach'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-6230897310421312317</id><published>2011-10-12T12:20:00.000-07:00</published><updated>2011-10-13T00:16:34.631-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tuner'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tv'/><category scheme='http://www.blogger.com/atom/ns#' term='saa7134'/><title type='text'>Philips SAA7130 pod linuxem</title><content type='html'>Dla odmiany po poprzednim poście napiszę coś technicznego jak na prawdziwego informatyka przystało. Nie tak dawno kupiłem sobie tani tuner tv (ok. 50 zł) oparty na chipset'cie &lt;i&gt;philips saa7130&lt;/i&gt;. Jak tylko tuner doszedł pocztą postanowiłem go od razu wypróbować. Zamontowałem kartę w moim itx'ie, na którym miałem postawione ubuntu. Odpalam, system wykrył co prawda kartę prawidłowo ale w tvtime nie miałem ani obrazu ani dźwięku. Okazało się, że linux załadował odpowiedni sterownik ale nie ustawił odpowiedniego modelu karty i tunera. Niestety na liście kart i tunerów nie ma mojego modelu, gdyż producentem tej karty jest jakaś mała firma jak to zwykle bywa w przypadku wykorzystywania tanich chipsetów. Jeżeli ktoś ma podobną kartę, z którą nie może dojść do ładu to poniżej przedstawiam rozwiązanie jak sobie radzić z tego typu kartami pod linuxem.&lt;br /&gt;&lt;br /&gt;Na wstępie powinniśmy się zaopatrzyć w program &lt;i&gt;tvtime&lt;/i&gt; i &lt;i&gt;tvtime-scanner&lt;/i&gt;. Pod ubuntu najłatwiej w ten sposób:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt;&lt;i&gt;&amp;nbsp;sudo apt-get install tvtime&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Teraz tworzymy prosty skrypt, który będzie testował różne ustawienia modeli kart i tunerów automatycznie. W tym celu tworzymy plik, np. o nazwie &lt;i&gt;skrypt.sh&lt;/i&gt;:&lt;br /&gt;&lt;pre class="brush: bash"&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;echo -e "Program started\n" &amp;gt; out&lt;br /&gt;for i in {52..152} ; do&lt;br /&gt;&amp;nbsp; &amp;nbsp; for g in 0 1 26 ; do&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;echo "Card: $i, Tuner: $g"&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;rmmod -w saa7134_alsa saa7134_dvb saa7134_empress saa7134&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;`modprobe saa7134 card=$i tuner=$g`&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;timeout -9 120 tvtime-scanner 2&amp;gt;&amp;amp;1 | while read f ; do&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;if [[ $f =~ "Znaleziono kana" ]] ; then&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;exit 2; //found!!&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;fi&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;if [[ $f =~ "Nie znaleziono tunera" ]] ; then&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;exit 3; //No tuner found!&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;fi&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;done&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;echo "code = $?";&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;if [ $? -eq 2 ] ; then&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;echo -e "Found Card = $i, Tuner = $g\n" &amp;gt;&amp;gt; out&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;fi&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;done&lt;br /&gt;done&lt;br /&gt;echo -e "Program ended\n" &amp;gt;&amp;gt; out&lt;br /&gt;&lt;/pre&gt;Linie 4 i 5 oznaczają numery kart i tunerów, które będziemy sprawdzać. Ja wpisałem akurat te wartości, które na liście kart (&lt;a href="http://www.mjmwired.net/kernel/Documentation/video4linux/CARDLIST.saa7134"&gt;link&lt;/a&gt;)&amp;nbsp;i tunerów (&lt;a href="http://www.mjmwired.net/kernel/Documentation/video4linux/CARDLIST.tuner"&gt;link&lt;/a&gt;)&amp;nbsp;nazwami najbardziej odpowiadały mojej karcie. Kolejna ważna linijka 9 decyduje o czasie poszukiwania sygnału. Otóż &lt;i&gt;tvtime-scanner&lt;/i&gt; skanuje całe pasmo w poszukiwaniu programów. Trwa to bardzo długo więc jakbyśmy mieli za każdym razem przeszukiwać całe pasmo to sprawdzenie wszystkich kombinacji kart i tunerów zajęłoby nam z miesiąc czasu. Dlatego też programem timeout ustawiamy limit długości trwania skanowania - w moim przypadku 120 sekund wystarczy, gdyż pierwszy program mam zaraz na początku pasma. &lt;br /&gt;&lt;br /&gt;W kolejnym kroku zmieniamy uprawnienia do skryptu na wykonywalne:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt; &lt;i&gt;chmod a+x skrypt.sh&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I uruchamiamy go jako root:&lt;br /&gt;&lt;b&gt;$&lt;/b&gt; &lt;i&gt;sudo ./skrypt.sh&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Skrypt będzie się wykonywał dość długo (parę godzin) więc warto go zostawić na noc. Gdy skrypt zakończy działanie przeglądamy plik o nazwie &lt;i&gt;out&lt;/i&gt;, który się utworzył. Będzie on wyglądał mniej więcej tak:&lt;br /&gt;&lt;br /&gt;Card: 49, Tuner: 0&lt;br /&gt;ERROR: Removing 'saa7134_dvb': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 3&lt;br /&gt;Card: 49, Tuner: 1&lt;br /&gt;ERROR: Removing 'saa7134_dvb': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 3&lt;br /&gt;Card: 49, Tuner: 26&lt;br /&gt;ERROR: Removing 'saa7134_dvb': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 3&lt;br /&gt;Card: 50, Tuner: 0&lt;br /&gt;ERROR: Removing 'saa7134_dvb': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 0&lt;br /&gt;Card: 50, Tuner: 1&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 2&lt;br /&gt;Card: 50, Tuner: 26&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;code = 2&lt;br /&gt;Card: 51, Tuner: 0&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;Killed&lt;br /&gt;FATAL: Error running install command for saa7134&lt;br /&gt;code = 0&lt;br /&gt;Card: 51, Tuner: 1&lt;br /&gt;ERROR: Removing 'saa7134_alsa': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_dvb': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134_empress': No such file or directory&lt;br /&gt;ERROR: Removing 'saa7134': Device or resource busy&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Interesują nas właściwie tylko te linijki, które zawierają &lt;i&gt;&lt;b&gt;code = 2&lt;/b&gt;&lt;/i&gt;. Oznacza to, że złapano sygnał na tych ustawieniach sterownika więc praktycznie mamy zagwarantowany obraz. No to odczytujemy numer karty i tunera i wywołujemy (z roota):&lt;/div&gt;&lt;div&gt;&lt;b&gt;$&lt;/b&gt;&amp;nbsp;&lt;i&gt;rmmod -w saa7134_alsa saa7134_dvb saa7134_empress saa7134&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;$&lt;/b&gt;&amp;nbsp;&lt;i&gt;modprobe saa7134 card=&lt;b&gt;numer_karty&lt;/b&gt; tuner=&lt;b&gt;numer_tunera&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;To, że mamy zagwarantowany obraz nie oznacza, że będziemy mieli dźwięk. Z dźwiękiem na tych kartach jest tak, że jest on analogowy (nie ma żadnego urządzenia w rodzaju karty dźwiękowej) więc należy bezpośrednio podłączyć kabelkiem wyjście audio na karcie do wejścia liniowego na karcie dźwiękowej. Niestety to nie wystarczy. Karty te mają dwa źródła dźwięku. Jednym jest sygnał z wejścia antenowego oczywiście a drugim sygnał z wejścia audio (na karcie są dwa złącza audio-in oraz audio-out). To powoduje, że sterownik musi umieć się pomiędzy nimi przełączać. Dlatego odpalamy tvtime (już z użytkownika) i patrzymy czy mamy obraz i dźwięk. Jak nie mamy to bierzemy następną parę karty i tunera z pliku gdzie &lt;i&gt;code = 2&lt;/i&gt; i powtarzamy powyższy krok aż do skutku. Jak już znajdziemy odpowiednie ustawienia to możemy je wpisać na stałe aby ustawiały się przy starcie systemu. W tym celu w pliku &lt;i&gt;/etc/modules &lt;/i&gt;dopisujemy na końcu:&lt;/div&gt;&lt;div&gt;&lt;i&gt;saa7134 card=&lt;b&gt;numer_karty&lt;/b&gt; tuner=&lt;b&gt;numer_tunera&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Jakby komuś się nie chciało tego wszystkiego robić to może od razu sprawdzić moje ustawienia - karta 126 i tuner 1. Miłej zabawy!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-6230897310421312317?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/6230897310421312317/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/philips-saa7130-pod-linuxem.html#comment-form' title='Komentarze (2)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6230897310421312317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/6230897310421312317'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/philips-saa7130-pod-linuxem.html' title='Philips SAA7130 pod linuxem'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5313652849827589697.post-2543776917523138764</id><published>2011-10-12T07:30:00.000-07:00</published><updated>2011-10-12T07:30:40.532-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wolność'/><category scheme='http://www.blogger.com/atom/ns#' term='godność'/><category scheme='http://www.blogger.com/atom/ns#' term='powody'/><title type='text'>O powodach i godności</title><content type='html'>Człowiek z natury jest leniwy (a może tylko programiści) i jak już zaczyna coś robić to dopiero wtedy kiedy solidnie zaczyna go coś irytować. Mówi się nawet, że najwięcej pieniędzy jest tam gdzie ludzie narzekają. Co by nie mówić musi to być prawdą przynajmniej w moim przypadku. W życiu denerwowało i nadal denerwuje wiele rzeczy ale ostatnia kampania wyborcza wprawiła mnie w nadzwyczaj nieprzyjemny stan. Oczywiście nie jedna kampania już była wcześniej i nie tylko w tej jednej kampanii człowiek nie mógł włączyć telewizji, radia czy przeczytać gazety bez wszechobecnych twarzy i jakże wymownych sentencji. Najczęściej pojawiającym się słowem tej kampanii (przynajmniej tym, które mnie wyprowadzało z błogiego stanu ignorancji) była godność i to w różnych formach: godna płaca, godne życie, bla bla bla. Niezrozumiałość tego słowa przez polityków wcale mnie nie dziwi i nawet to nagminne powtarzanie tych bzdur nie popchnęło mnie do napisania tego postu. Popchnęła mnie natomiast wiara w te słowa przez bądź co bądź bliskie mi osoby. Szukałem w internecie jakiejś sensownej definicji i niestety nie znalazłem więc postanowiłem przedstawić swoją.&lt;br /&gt;&lt;br /&gt;Po pierwsze nie ma czegoś takiego jak godna płaca. Te słowa w żaden sposób się ze sobą nie łączą. Godność (a także i honor) jest bezpośrednio związana z innym bardzo ważnym słowem: &lt;b&gt;wolność&lt;/b&gt;. Godność bowiem tracimy wówczas, gdy zyskujemy korzyści na odbieraniu wolności innych ludzi (oczywiście bez ich zgody). Pozwolę sobie przytoczyć w tym miejscu dobry przykład podany przez pana Janusza Korwin-Mikkego. Otóż człowiek, który zarabia 100zł i przymiera głodem może żyć godnie w przeciwieństwie do tego, który ma się dobrze a nie zarabia i bierze zasiłek socjalny od państwa. Dlaczego? Ponieważ ten pierwszy żyje z tego co sam zarobi, żyje na tyle na ile potrafi i swoje życie zawdzięcza tylko sobie. Ten drugi natomiast korzysta z pieniędzy &lt;i&gt;brutalnie&lt;/i&gt; zabranych innym ludziom (w większości tym biednym, którzy zarabiają za dużo aby móc dostawać zasiłek). Godnie to znaczy żyć za swoje!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Politycy nie tylko kłamią odnośnie godności ale również upadlają ludzi. Jeżeli ktoś wmawia mi, że nie żyję godnie i nakłania mnie do brania zasiłku bo wówczas będę żył godnie a do tego szczyci się jeszcze tym, że te zasiłki podniesie to jest to dokładnie upodlenie ludzi!&lt;br /&gt;&lt;br /&gt;A gdzie tu związek z wolnością? Jeżeli ktoś jeszcze tego jeszcze nie widzi to wyjaśnię to konkretnie. Zabieranie pieniędzy ludziom pod przymusem jest zabieraniem wolności, korzystanie z kradzionych pieniędzy czy innych kradzionych rzeczy jest niemoralne bo wówczas to Ty, drogi czytelniku, stajesz na szczycie tej piramidy. Innymi słowy to Ty jesteś zleceniodawcą &lt;i&gt;ergo&lt;/i&gt; również Ty godzisz w wolność innych ludzi. &lt;br /&gt;&lt;br /&gt;Wiem co powie na to większość ludzi. Powiedzą, że jak tu żyć godnie jak ci wredni bogaci kapitaliści ciągle obniżają im pensje a z czegoś trzeba wyżywić swoje rodziny. Twierdzić będą, że &lt;i&gt;"bogacą się kosztem biednych ludzi"&lt;/i&gt;. Otóż w normalnym wolnym świecie bogatym zostać można tylko w jeden sposób, po prostu robiąc coś dla innych. Na tym polega praca. Ludzie mają różne cele, różne ambicje, ideały, marzenia ale aby je spełniać potrzebują różnych rzeczy. Cała sztuka polega na tym aby dawać ludziom to czego oni &lt;b&gt;potrzebują&lt;/b&gt; a w zamian dostając potrzebne nam rzeczy (np. pieniądze). Zatem ludzie nie bogacą się kosztem innych lecz pracując na rzecz innych. Jest to najlepszy sposób na godne życie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5313652849827589697-2543776917523138764?l=karolsamborski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karolsamborski.blogspot.com/feeds/2543776917523138764/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/o-powodach-i-godnosci.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2543776917523138764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5313652849827589697/posts/default/2543776917523138764'/><link rel='alternate' type='text/html' href='http://karolsamborski.blogspot.com/2011/10/o-powodach-i-godnosci.html' title='O powodach i godności'/><author><name>Karol Samborski</name><uri>http://www.blogger.com/profile/08123299996740017057</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
