<?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-3683853109261477206</id><updated>2011-12-06T05:02:09.672-08:00</updated><category term='boost::asio'/><category term='Grafika'/><category term='jedzenie'/><category term='Kindle'/><category term='hasło'/><category term='Matlab'/><category term='C'/><category term='Riak'/><category term='Narzędzia'/><category term='Perl'/><category term='rebar'/><category term='Teoria informatyki'/><category term='Filozofia'/><category term='Java'/><category term='Bazy danych'/><category term='NoSQL'/><category term='Wydajność'/><category term='Ogólne'/><category term='Rozrywka'/><category term='HTTP'/><category term='Programowanie sieciowe'/><category term='C++'/><category term='Git'/><category term='windows'/><category term='GNU/Emacs'/><category term='Lua'/><category term='Linuks'/><category term='QuickTips'/><category term='Zarządzanie czasem'/><category term='Wykłady'/><category term='Erlang'/><title type='text'>Lukasz Milewski's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>91</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-2972875549717182164</id><published>2011-10-08T05:52:00.004-07:00</published><updated>2011-10-08T06:14:12.813-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Erlang i ** exception exit: {badfun,ok}</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Poprawienie błędu ** exception exit: {badfun,ok} okazało się dość trudne (kilka minut na taką pierdołę?). Skoro już straciłem te kilka minut z życia na poprawkę to może chociaż ten post zaoszczędzi mi, a może nawet komuś jeszcze, trochę czasu w przyszłości.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Jakie były objawy?&lt;/span&gt;
Dodałem nowy gen_server do swojego serwera, oprogramowałem go i nawet działał :-) Pojawiał się jednak błąd ** exception exit: {badfun,ok}  gdy odpalałem serwer ręcznie w Erlangowym shellu spod Emacsa. Oczywiście poza tym błędem dostawałem całkiem spory crash report z supervisora, ale to jest mniej istotne.

&lt;span style="font-weight:bold;"&gt;Co było przyczyną?&lt;/span&gt;
Po kilku minutach wpatrywania się w kod oraz komentowania jego fragmentów nie udało mi się nic znaleźć przyczyny. Skompilowałem plik poleceniem&lt;pre&gt; erlc -P plik.erl&lt;/pre&gt; i jasne było, że winę ponosi moje użycie makra debugHere jako&lt;pre&gt; ?debugHere()&lt;/pre&gt;zamiast&lt;pre&gt; ?debugHere&lt;/pre&gt;Jeżeli zamiast wywołania ?debugHere() wpiszemy w kod to, do czego się tłumaczy (co między innymi robi polecenie erlc -P) to zobaczymy coś takiego:&lt;pre&gt;    begin
        .io:fwrite(user, &lt;&lt;"~s:~w: ~s\n"&gt;&gt;, ["./plik.erl",68,"&lt;-"]),
        ok
    end(),
&lt;/pre&gt;
Widać, że to makro wypisuje coś na ekran i zwraca atom 'ok'. Następnie ten atom jest wywoływany jako funkcja (para nawiasów za słowem kluczowym end). To oczywiście prowadzi do komunikatu ** exception exit: {badfun,ok} 

&lt;span style="font-weight:bold;"&gt;Nauczka&lt;/span&gt;
&lt;ol&gt;
&lt;li&gt;
Makro debugHere jako jedyne z rodziny debug* nie powinno być wywoływane jako funkcja. Ciągle o tym zapominam.
&lt;/li&gt;
&lt;li&gt;
Interfejs powinien być przede wszystkim spójny. Wszelkie udziwnienia prowadzą do błędów podczas używania takiego interfejsu.
&lt;/li&gt;
&lt;li&gt;
Makra potrafią być w Erlangu tak samo błędogenne jak w C.
&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2972875549717182164?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2972875549717182164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/10/erlang-i-exception-exit-badfunok.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2972875549717182164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2972875549717182164'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/10/erlang-i-exception-exit-badfunok.html' title='Erlang i ** exception exit: {badfun,ok}'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3884983202642370627</id><published>2011-09-03T06:46:00.003-07:00</published><updated>2011-09-03T07:55:06.653-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Riak'/><title type='text'>Zaczynamy przygodę z NoSQL</title><content type='html'>&lt;span style="font-weight:bold;"&gt;NoSQL to świetna sprawa i naprawdę namawiam do eksperymentów z tą technologią. Wykorzystanie NoSQL upraszcza i przyspiesza rozwój małych małych aplikacji oraz &lt;span style="font-weight:bold;"&gt;bardzo upraszcza lub wręcz umożliwia&lt;/span&gt; development w przypadku tych większych.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Jak zacząć zabawę z Riak?&lt;/span&gt;
Jest to zaskakująco proste i szybkie. Polecam instalację ze źródeł - proces jest bezbolesny. Oto krótki filmik, który pokazuje krok po kroku jak to zrobić.

&lt;iframe src="http://player.vimeo.com/video/11240885?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="400" height="300" frameborder="0"&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href="http://vimeo.com/11240885"&gt;Setting up a Three Node Riak Cluster&lt;/a&gt; from &lt;a href="http://vimeo.com/bashotech"&gt;Basho Technologies&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
&lt;span style="font-weight:bold;"&gt;Dlaczego Riak?&lt;/span&gt;
Bo jest prosty w obsłudze i szybki, a daje przy tym duże możliwości. Jest to baza danych oparta na Dynamo - krótko mówiąc, ta cecha sprawia, że zapomnisz co oznacza słowo downtime. Riak jest przy tym bardzo stabilny (w końcu to Erlang) i bezpieczny (wykorzystuje vector clocks oraz pozwala na rozwiązanie ewentualnych, konfliktujących zapisów).

&lt;span style="font-weight:bold;"&gt;Dlaczego NoSQL?&lt;/span&gt;
Poza prostotą (co dla mnie samo w sobie jest ogromną zaletą) dostajemy system, który jest odporny na awarie, nie posiada tzw. "single point of failure", łatwo się skaluje na wiele maszyn i jest przy tym bardzo szybki.

&lt;span style="font-weight:bold;"&gt;Dlaczego nie SQL?&lt;/span&gt;
Oczywiście relacyjne bazy danych mają swoje zastosowania i nikt im tego nie odbierze. Jednak z jakiegoś dziwnego powodu ludzie wykorzystują je nawet gdy nie ma takiej potrzeby. Niestety SQL to droga zabawka (nie wchodźmy teraz w szczegóły).

&lt;span style="font-weight:bold;"&gt;Ale ja nie chcę tracić danych!&lt;/span&gt;
I nie będziesz. Nie wiem skąd ten mit się wziął. Czy takie firmy jak Google (Big Table, Megastore) czy Amazon (Dynamo, SimpleDB) ryzykowałyby utratę danych swoich użytkowników?

Są różne NoSQL. Do wyboru do koloru.
&lt;br&gt;
Po jednej stronie są super szybkie rozwiązania jak MongoDB. Osiągają ogromną wydajność dopuszczając przy tym możliwość utraty danych (w przypadku awarii maszyny). Pamiętajmy jednak, że zawsze mamy kilka replik danych i starty są możliwe gdy wszystkie repliki nagle się zepsują (kilka równoległych awarii w datacenter). Oczywiście mamy backup, więc jesteśmy bezpieczni.
&lt;br&gt;
Po drugiej stronie jest CouchDB. Baza, którą można wyłączać przez kill -9 i pod tym względem "rozwala" znane rozwiązania SQLowe.

Prawdziwe niebezpieczeństwo czai się w SQLu, bo gdy zdecydujesz się na rozproszenie go na kilka maszyn to musisz implementować samemu wszystko to co w NoSQLach jest już zrobione i przetestowane.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3884983202642370627?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3884983202642370627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/09/zaczynamy-przygode-z-nosql.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3884983202642370627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3884983202642370627'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/09/zaczynamy-przygode-z-nosql.html' title='Zaczynamy przygodę z NoSQL'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8435558052867075933</id><published>2011-09-03T04:30:00.006-07:00</published><updated>2011-09-03T05:08:58.862-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTTP'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Erlang i żądanie typu GET</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jak wiadomo &lt;a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;protokół HTTP&lt;/a&gt; pozwala na różne typy żądań. &lt;a href="http://tools.ietf.org/html/rfc2616"&gt;RFC 2616&lt;/a&gt; definiuje między innymi metodę POST. Gdy potrzebowałem wykonać zapytanie POST przy użyciu &lt;a href="http://www.erlang.org/doc/apps/inets/http_client.html"&gt;http_client w Erlangu&lt;/a&gt; udało mi się to zrobić dopiero po zapoznaniu się ze źródłami tej biblioteki. Poniżej krótki opis jak to zrobić.&lt;/span&gt;&lt;br&gt;
&lt;pre&gt;http_post(Url, Data) -&gt;
   inets:start(),
   inets:start(httpc, [{profile, ?MODULE}]),
   Result = httpc:request(post, {Url, [], "application/x-www-form-urlencoded", Data}, [], [], ?MODULE),
   inets:stop(),
   Result.&lt;/pre&gt;
Jak widać jest trochę pracy :-)
&lt;br&gt;Pierwsza linia mówi, że definiujemy funkcję o dwóch argumentach (Url oraz Data). W Erlangu, podobnie jak w Pythonie, nie podaje się typów argumentów. &lt;/br&gt;&lt;br&gt;Druga linijka uruchamia serwer inets. To wywołanie uruchamia kilka procesów, między innymi http client. Tworzy także domyślny profil dla httpc. Bez tego wywołania dostalibyśmy w trzeciej linijce błąd {error,inets_not_started}&lt;/br&gt;&lt;br&gt;Trzecia linijka tworzy profil httpc o nazwie ?MODULE (MODULE to makro, pod które jest podstawiana nazwa modułu, w którym jest użyte). W przypadku żądania POST nie można skorzystać z domyślnego profilu.&lt;/br&gt;&lt;br&gt;Kolejna linia wykonuje żądanie typu POST do Url i jako dane przesyła Data. Zwrócony wynik jest przypisywany do zmiennej Result.&lt;/br&gt;&lt;br&gt;Następnie wyłączamy serwer intets i w ostatniej linijce zwracamy wynik żądania.
&lt;/br&gt;&lt;br&gt;Być może istnieje prostsza, jednolinijkowa metoda wykonywania żądania POST w Erlangu. Dla mnie działa opisany kawałek kodu i mam nadzieję, że jeszcze komuś się przyda. Jeżeli znasz prostszy sposób to będę wdzięczny za komentarz.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8435558052867075933?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8435558052867075933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/09/erlang-i-zadanie-typu-get.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8435558052867075933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8435558052867075933'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/09/erlang-i-zadanie-typu-get.html' title='Erlang i żądanie typu GET'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-264686151332983924</id><published>2011-06-23T15:55:00.003-07:00</published><updated>2011-06-23T16:05:33.436-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Zawieszający się appmon</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Ostatnio zrobiłem równocześnie upgrade Erlanga do R14B03 oraz zainstalowałem nowe Ubuntu 11.04. Okazało się, że appmon przestał działać.&lt;/span&gt;

Na początku myślałem, że błąd jest gdzieś w Erlangu - poprzednia wersja Erlanga działała na wszystkich maszynach bardzo dobrze (w tym na Ubuntu 11.04). Błąd objawiał się tym, że wywołanie funkcji appmon:start() nigdy się nie kończyło, a ekran monitora aplikacji się nie pojawiał.

Wpadłem na pomysł, że powodem może być brak zainstalowanego TCL/TK. Po instalacji (w moim przypadku był to pakiet tk8.5-dev) wszystko działa.

Może komuś jeszcze przyda się to rozwiązanie - a może nawet sam kiedyś je wygooglam ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-264686151332983924?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/264686151332983924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/06/zawieszajacy-sie-appmon.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/264686151332983924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/264686151332983924'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/06/zawieszajacy-sie-appmon.html' title='Zawieszający się appmon'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-6951561888491763112</id><published>2011-06-02T03:41:00.005-07:00</published><updated>2011-06-02T03:50:15.853-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rebar'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>rebar 'generate not understood'</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jeżeli wykorzystujesz rebar do budowy projektu to możesz natknąć się na błąd o treści 'generate not understood' gdy chcesz zbudować release.
&lt;/span&gt;
Moim zdaniem błąd powinien brzmieć inaczej, bo wprawadza w błąd. Przecież rebar rozumie polecenie 'generate'.

Przechodząc do rozwiązania - należy utworzyć poprawny katalog 'rel' i go skonfigurować.&lt;pre&gt;$ mkdir rel &amp;&amp; cd rel
$ ../rebar create-node nodeid=nazwa_mojej_aplikacji
$ cd ..
$ emacs rebar.config&lt;/pre&gt;wystarczy teraz dodać do pliku rebar.config linijkę&lt;pre&gt;{sub_dirs, ["rel"]}.&lt;/pre&gt;I wszystko powinno działać - przynajmniej w moim przypadku to pomogło ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6951561888491763112?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6951561888491763112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/06/rebar-generate-not-understood.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6951561888491763112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6951561888491763112'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/06/rebar-generate-not-understood.html' title='rebar &apos;generate not understood&apos;'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3917060486643280980</id><published>2011-05-23T14:21:00.005-07:00</published><updated>2011-05-23T14:36:39.899-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='Git'/><title type='text'>Podświetlamy nadmiarowe znaki w komentarzu gita (GNU Emacs)</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Przyjęło się, że pierwsza linia komentarza commita ma co najwyżej 50 znaków (w przypadku gita). Niektóre edytory (np. vim) podświetlają pierwsze 50 znaków aby pokazać programiście czy już przekroczył limit. Zobaczmy jak łatwo zrobić to w GNU Emacsie&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;magit-mode&lt;/span&gt;
Zakładam, że do obsługi gita wykorzystujesz magit-mode. Jest to świetny pakiet, który bardzo usprawnia pracę z tym systemem kontroli wersji. Łatwo zauważyć, że edycja komentarza odbywa się w magit-log-edit-mode.

&lt;span style="font-weight:bold;"&gt;regex, który wyłapuje nadmiarowe znaki&lt;/span&gt;
Naszym celem jest pokolorowanie znaków w kolumnach 51+ w pierwszej linii. Potrzebujemy regexa, który dopasuje się do tych znaków. Zaczynamy od symbolu &lt;pre&gt;\\`&lt;/pre&gt;który oznacza początek bufora (pierwsza linia). Następnie musi wystąpić 50 znaków, które nie są nową linią &lt;pre&gt;[^\n]\\{50\\}&lt;/pre&gt;Od tego momentu są znaki, które należy pokolorować. Łapiemy je wyrażeniem &lt;pre&gt;\\(.*\\)&lt;/pre&gt;Ostatecznie nasze wyrażenie regularne wygląda tak&lt;pre&gt;"\\`[^\n]\\{50\\}\\(.*\\)"
&lt;/pre&gt;Wykorzystujemy funkcję font-lock-add-keywords aby ustawić font-lock-warning-face na znaki pasujące do wyrażenia&lt;pre&gt;(font-lock-add-keywords 'magit-log-edit-mode
                        '(("\\`[^\n]\\{50\\}\\(.*\\)" 1 font-lock-warning-face)))&lt;/pre&gt;Zaraz po wyrażeniu widzimy liczbę 1. Oznacza ona, że interesuje nas pierwsza dopasowana grupa - to co jest między pierwszą parą nawiasów \\( oraz \\).

&lt;span style="font-weight:bold;"&gt;Ustawiamy kolorowanie słów kluczowych przy starcie magit-log-edit-mode&lt;/span&gt;
Aby przy starcie magit-log-edit-mode bufor został pokolorowany należy wywołać funkcję font-lock-fontify-buffer zaraz po starcie tego trybu. Możemy do tego wykorzystać funkcję add-hook&lt;pre&gt;(add-hook 'magit-log-edit-mode-hook 'font-lock-fontify-buffer)&lt;/pre&gt;To rozwiązuje nasz problem.

&lt;span style="font-weight:bold;"&gt;Podsumowanie&lt;/span&gt;
Okazuje się, że w również w tym przypadku dodanie funkcji do Emacsa, o której nie pomyśleli autorzy jest bardzo proste. Ostateczny kod ma trzy linijki&lt;pre&gt;(font-lock-add-keywords 'magit-log-edit-mode
                        '(("\\`[^\n]\\{50\\}\\(.*\\)" 1 font-lock-warning-face)))
(add-hook 'magit-log-edit-mode-hook 'font-lock-fontify-buffer)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3917060486643280980?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3917060486643280980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/podswietlamy-nadmiarowe-znaki-w.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3917060486643280980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3917060486643280980'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/podswietlamy-nadmiarowe-znaki-w.html' title='Podświetlamy nadmiarowe znaki w komentarzu gita (GNU Emacs)'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1451520136683127313</id><published>2011-05-14T10:41:00.003-07:00</published><updated>2011-05-14T10:58:28.193-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Wydajność'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Mit wydajność pre/post incrementacji w C++</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Co chwilę napotykam na kogoś, kto twierdzi, że i++ jest mniej wydajne niż ++i. Większość osób przeczytało o tym na forum internetowym i nie potrafi wyjaśnić dlaczego tak jest. Co więcej - często trudno ich przekonać o nieprawdziwości takiego stwierdzenia. Przypomniałem sobie o tym gdy podobna dyskusja znalazła się na &lt;a href="http://warsztat.gd"&gt;Warsztacie&lt;/a&gt;. &lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Kiedy i dlaczego ++i jest wydajniejsze niż i++?&lt;/span&gt;
Obie operacje wykonują się w dokładnie takim samym czasie (przy założeniu wykorzystania dobrego kompilatora) dla typów wbudowanych (np. int). Dla typów utworzonych przez użytkownika z przeciążonym operatorem ++() lub ++(int) (np. iteratory) operacja i++ działa wolniej.

Różnica wynika z faktu, że i++ zwraca poprzednią wartość jaką miała zmienna. Musi zatem ją gdzieś zapamiętać - zachodzi dodatkowe kopiowanie, które w przypadku ++i jest zbędne. ++i może zwiększyć wartość i zwrócić już zwiększoną wartość.

&lt;span style="font-weight:bold;"&gt;Dlaczego dla typów wbudowanych nie ma różnicy wydajnościowej?&lt;/span&gt;
Zobaczmy jak wygląda assembler wygenerowany przez obie operacje (zakładając, że kompilujemy przy użyciu g++) Poniżej widać dwa programy w c++ różniące się tylko operacją ++x oraz x++&lt;pre&gt;int main() {
    int x = 0;
    int y = ++x;
}

int main() {
    int x = 0;
    int y = x++;
}&lt;/pre&gt; Teraz generujemy kod asma (przełącznik -S w g++).

Dla x++ kod wygląda następująco:&lt;pre&gt; movl $0, -4(%rbp)
 movl -4(%rbp), %eax
 movl %eax, -8(%rbp)
 incl -4(%rbp)
 movl $0, %eax
 leave
 ret&lt;/pre&gt;natomiast dla ++x&lt;pre&gt; movl $0, -4(%rbp)
 incl -4(%rbp)
 movl -4(%rbp), %eax
 movl %eax, -8(%rbp)
 movl $0, %eax
 leave
 ret&lt;/pre&gt;Wystarczy teraz policzyć ile operacji jest wykonywanych w każdym z przypadków aby zauważyć brak różnicy w wydajności.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1451520136683127313?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1451520136683127313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/mit-wydajnoscprepost-incrementacji-w-c.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1451520136683127313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1451520136683127313'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/mit-wydajnoscprepost-incrementacji-w-c.html' title='Mit wydajność pre/post incrementacji w C++'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6264613545642842900</id><published>2011-05-03T11:07:00.008-07:00</published><updated>2011-05-03T11:55:26.792-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Rozpoczynamy przygodę z Erlangiem</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Przez moje zainteresowanie systemami rozproszonymi jakiś czas temu wpadłem na język, który nazywa się Erlang. Gdy pierwszy raz usłyszałem o tej technologii to wydała mi się nie warta poświęcania więcej jak godzinę (mniej więcej tyle trwała prezentacja, na którą niedługo potem poszedłem). Dużo później przeczytałem pracę doktorską Joe Armstronga &lt;a href="http://www.erlang.org/download/armstrong_thesis_2003.pdf"&gt;Making reliable distributed systems in the presence of software errors&lt;/a&gt; i tak od kilku miesięcy siedzę głównie w tej technologii.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Dlaczego Erlang&lt;/span&gt;
Nie chcę tutaj zachwalać tej technologii - niech każdy sam zdecyduje czy to dla niego . Polecam przeczytać pracę Joe Armstronga - może również Ciebie przekona. Od siebie dodam, że miałem okazję budować systemy rozproszone już wcześniej. Wykorzystywałem do tego C++ oraz bardzo mocno bibliotkę boost::asio. Nie będę ukrywał, że to dobre środowisko do tego takich zastosowań. Niestety część "in presence of software errors" bardzo trudno zachować w C++. Każdy błąd spowodowany przez jednego użytkownika systemu dotyka pozostałych zalogowanych na tym samym serwerze. Jest tak, bo wątki systemowe oraz procesy są bardzo drogie, a zatem tworzy się ich mniej niż jest użytkowników. W takiej sytuacji bardzo ciężko jest sobie poradzić np. z SIGSEGVami czy zwykłymi deadlockami. A gdyby tak tworzyć osobny proces nie tylko na każdego użytkownika ala na każdy jego request? Wtedy w przypadku crasha wystarczy zrestartować taki proces i nikt nie zauważy ;-) 

W tym kierunku idzie Erlang - użytkownik ma nigdy nie widzieć, że z usługą coś jest nie tak jak powinno - nawet gdy wystąpi błąd w oprogramowaniu czy padnie jedna maszyna. Podstawowym założeniem jest to aby usługa nigdy nie była zatrzymywana (nawet gdy robi się jej upgrade - kolejna rzecz trudna do osiągnięcia w C++). Oczywiście wszystko co da się zrobić w Erlangu da się też zrobić w C++. Tylko po co wymyślać na nowo mechanizmy, które przychodzą razem z Erlangiem za darmo - w postaci bibliotek OTP?

Wspomnę jeszcze, że poleceniem:&lt;pre&gt;Process ! Msg&lt;/pre&gt; wysyła się wiadomość o treści Msg do procesu o nazwie Process - nie ważne, na której maszynie fizycznej taki proces się znajduje! To sporo upraszcza.

&lt;span style="font-weight:bold;"&gt;Instalacja maszyny wirtualnej Erlanga&lt;/span&gt;
Erlang ma swoją maszynę wirtualną. Jest to abstrakcja nad systemem operacyjnym. Aby zacząć przygodę z Erlangiem należy ją zainstalować w systemie. Pobieramy zatem jej &lt;a href="http://www.erlang.org/download.html"&gt;źródła&lt;/a&gt; do katalogu domowego (użytkownicy Windowsa mogą ściągnąć od razu binarkę). Następnie rozpakowujemy i kompilujemy:&lt;pre&gt; $ tar -zxvf otp_src_R14B02.tar.gz
 $ cd otp_src_R14B02
 $ ./configure --prefix=~/otp
 $ make
 $ make install&lt;/pre&gt;Mamy maszynę z bibliotekami w katalogu ~/otp

&lt;span style="font-weight:bold;"&gt;Instalujemy środowisko&lt;/span&gt;
Jeżeli jesteś użytkownikiem GNU Emacsa to masz szczęście - w katalogu otp_src_R14B02/lib/tools/emacs znajdziesz erlang-mode.

Możesz też zdecydować się na erlide - plugin do Eclipse'a.

&lt;span style="font-weight:bold;"&gt;Zaczynamy&lt;/span&gt;
Na początek polecam stronę &lt;a href="http://www.tryerlang.org/"&gt;tryerlang&lt;/a&gt;. Można na niej pobawić się shellem Erlanga. Zobaczyć podstawy. Następnie polecam kurs &lt;a href="http://learnyousomeerlang.com/content"&gt;learn you some erlang&lt;/a&gt;. Jest napisany bardzo prosto. Jest skierowany do początkujących.

Jeżeli masz już doświadczenie i chcesz przejść od razu do rzeczy to możesz sprawdzić mój "minitutorial", który napisałem w formie wykładu aby pokazać kilku osobom jak to wszystko wygląda. Jest to jeden plik z opisem oraz szereg plików źródłowych Erlanga, demonstrujących omawiane zagadnienia. Zaczynam od podstawowej składni, a następnie przechodzę do narzędzi. Opisałem te, które bardzo upraszczają pracę. Nie znalazłem w internecie podsumowania dostępnych narzędzi (w chwili pisania tych słów kończę właśnie ostatnią sekcję - troubleshooting). Znadziesz w nim również nazwy książek, które polecam oraz listę linków do bardzo przydatnych materiałów. Całkowicie pomijam temat samego rozproszenia - to można znaleźć we wspomnianych książkach. &lt;a href="https://github.com/lmmilewski/erlang_basics/blob/master/erlang_basics.org"&gt;Moje krótkie wprowadzenie do Erlanga - erlang_basics.org&lt;/a&gt;.

Zaznaczam, że po tym kursie będziesz miał jedynie podstawy. Aby zbudować coś dużego niezbędna jest lektura książek lub &lt;a href="https://github.com/languages/Erlang"&gt;kodów źródłowych&lt;/a&gt;, co jest moim zdaniem lepszą opcją.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6264613545642842900?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6264613545642842900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/rozpoczynamy-przygode-z-erlangiem.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6264613545642842900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6264613545642842900'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/05/rozpoczynamy-przygode-z-erlangiem.html' title='Rozpoczynamy przygodę z Erlangiem'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-4645127154003841556</id><published>2011-02-17T13:53:00.000-08:00</published><updated>2011-02-17T13:53:18.969-08:00</updated><title type='text'>Rysujemy hypercube - video</title><content type='html'>&lt;iframe width="425" height="344" src="http://www.youtube.com/embed/ccws454YiVM?fs=1" frameborder="0" allowFullScreen=""&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4645127154003841556?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4645127154003841556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/rysujemy-hypercube-video.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4645127154003841556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4645127154003841556'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/rysujemy-hypercube-video.html' title='Rysujemy hypercube - video'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://img.youtube.com/vi/ccws454YiVM/default.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-7434030108364805522</id><published>2011-02-17T11:40:00.001-08:00</published><updated>2011-02-17T11:40:58.166-08:00</updated><title type='text'>GOOGLE: Designs, Lessons and Advice from Building Large   Distributed Systems</title><content type='html'>Check out this SlideShare Presentation: &lt;div style="width:425px" id="__ss_2371360"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/xlight/google-designs-lessons-and-advice-from-building-large-distributed-systems" title="GOOGLE: Designs, Lessons and Advice from Building Large   Distributed Systems "&gt;GOOGLE: Designs, Lessons and Advice from Building Large   Distributed Systems &lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse2371360" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=cfakepathdean-keynote-ladis2009-091028200636-phpapp01&amp;stripped_title=google-designs-lessons-and-advice-from-building-large-distributed-systems&amp;userName=xlight" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse2371360" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=cfakepathdean-keynote-ladis2009-091028200636-phpapp01&amp;stripped_title=google-designs-lessons-and-advice-from-building-large-distributed-systems&amp;userName=xlight" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/xlight"&gt;xlight&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7434030108364805522?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7434030108364805522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/google-designs-lessons-and-advice-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7434030108364805522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7434030108364805522'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/google-designs-lessons-and-advice-from.html' title='GOOGLE: Designs, Lessons and Advice from Building Large   Distributed Systems'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2566934848029336113</id><published>2011-02-12T05:46:00.004-08:00</published><updated>2011-02-12T06:06:20.195-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><title type='text'>Perl i switch statement</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Przeglądając źródła ejabberd znalazłem pewną ciekawostkę dotyczącą języka Perl. Można w nim 'symulować' wyrażenie switch znane z innych języków programowania. Tak naprawdę konstrukcja, którą chcę pokazać, jest bardziej zbliżona do lispowego cond niż c++-owego switch.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Przykład użycia switch w perlu&lt;/span&gt;
Tę instrukcję traktuję jako ciekawostkę, bo osobiście jej nie lubię (nawet w innych językach) - moim zdaniem jest zbyt niebezpieczna (to nie dotyczy lispowego cond). Trudno mi zatem wymyślić jakieś ciekawe zastosowanie switch. Z tego powodu przykład może wydawać się dość sztuczny.

Wczytamy string ze standardowego wejścia i jeżeli jest to "1" albo "2" to wypiszemy odpowiednio "jeden", "dwa". Jeżeli jest to inny string to wypiszemy "inny string".

Dość już gadania - zobaczmy kod:&lt;pre&gt;#!/usr/bin/perl

use warnings;
use strict;

print "podaj liczbę: ";
chomp(my $choice = &amp;lt;stdin&amp;gt;);
SWITCH: {
   $choice eq "1" and do {
       print "wybrales jeden\n";
   }, last SWITCH;

   $choice eq "2" and do {
       print "wybrales dwa\n";
   }, last SWITCH;
  
   do {
       print "wybrales inny string niz jeden i dwa\n";
   }, last SWITCH;
}&lt;/stdin&gt;&lt;/pre&gt;Myślę, że działanie tego kodu jest na tyle oczywiste, że mogę pominąć komentarz.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2566934848029336113?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2566934848029336113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/perl-i-switch-statement.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2566934848029336113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2566934848029336113'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/02/perl-i-switch-statement.html' title='Perl i switch statement'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5910891875654861939</id><published>2011-01-16T06:38:00.009-08:00</published><updated>2011-01-16T08:17:12.698-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bazy danych'/><title type='text'>Czego oczekuję od bazy danych?</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Oczywista odpowiedź to "zależy od zastosowania". Dlatego przyjmijmy, że piszemy aplikację taką jak gra MMO, portal internetowy czy forum. Oczywiście mierzymy wysoko i zakładamy ogromną liczbę użytkowników, którzy w każdym momencie wykonują akcje w systemie. Czym powinien cechować się DBMS dla takiego zastosowania?&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Stabilność, poprawność i dostępność&lt;/span&gt;
To oczywiste i w pierwszej wersji posta pominąłem to wymaganie. Być może jednak warto to podkreślić - baza danych ma działać. Nie może przekłamywać wyników albo wysypywać się.

Jest jeden drobiazg, o którym warto wspomnieć - dostępność. Baza danych nigdy nie powinna przerywać działania jako całość. Co mogłoby wymagać takiego zachowania? Gdy sieć, w której są maszyny się podzieli tak, że maszyny można podzielić na grupy, które wzajemnie się nie widzą. Podobnie gdy część maszyn padnie. Chcemy aby nasza usługa miała uptime możliwie bliski 100%. Wyłączająca się baza danych temu nie sprzyja.

&lt;span style="font-weight:bold;"&gt;Skalowalność&lt;/span&gt;
Liczba użytkowników naszej aplikacji ciągle rośnie. Użytkownicy zaczynają odczuwać spadek wydajności w postaci małego laga. Już wiemy, że niedługo będziemy mieli poważne problemy wydajnościowe. Udaje nam się znaleźć problem - baza danych jest przeciążona. Aby wiedzieć czy sobie poradzimy z tym problemem musimy odpowiedzieć na następujące pytania:
 - czy wystarczy dostawić kolejną maszynę aby zwiększyć moc bazy danych?
 - jeżeli nie to w jakim czasie jesteśmy w stanie poprawić nasze rozwiązanie? 
 - czy w ogóle jesteśmy w stanie to zrobić? 
 - jeżeli wystarczy dostawić maszynę, to w jakim stopniu zwiększamy możliwości systemu przez podwojenie ilości maszyn? 
 - dwukrotnie?

System, który skaluje się idealnie mamy gdy maksymalna liczba obsługiwanych użytkowników jest proporcjonalna do ilości maszyn jakie posiadamy i możemy dostawiać te maszyny w nieskończoność. System, który skaluje się beznadziejnie mamy gdy maksymalna liczba obsłużonych użytkowników jest równa liczbie użytkowników obsługiwanych przez jedną maszynę.

&lt;span style="font-weight:bold;"&gt;Szybkość&lt;/span&gt;
Mając skalowalny system, wiemy że jesteśmy w stanie dostarczyć usługę wszystkim użytkownikom. Mimo to, dostarczanie jej może być dla nas nieopłacalne. Maszyny przecież kosztują i to niemało. Dlatego kolejną cechą, jakiej potrzebujemy jest szybkość. Im więcej użytkowników "wchodzi" na jedną maszynę, tym mniej maszyn potrzebujemy. Tę liczbę chcemy minimalizować z wielu powodów. Między innymi:
 - redukujemy koszty utrzymania aplikacji
 - zmniejszamy ryzyko, że w danym momencie jakaś maszyna ma awarię
Pamiętajmy również, że czas odpowiedzi serwera na zapytanie użytkownika w znacznej części składa się z czasu wykonywania zapytań.

&lt;span style="font-weight:bold;"&gt;Możliwość upgradu i backupu&lt;/span&gt;
Gdy mamy np. 100k użytkowników online to nie chcemy wyłączać całego serwera tylko dlatego, że musimy zrobić upgrade. Idealnie nie chcemy wylogowywać kogokolwiek. Dlatego baza danych powinna umożliwiać konfigurację, dostawianie maszyn oraz zmiany schematu (zakładając, że go ma) w trakcie działania. 

Z tego samego powodu niedopuszczalne jest aby backup zatrzymywał działanie bazy. Oczywiście chcemy robić backupy, więc DBMS musi umożliwiać robienie go "w locie".

&lt;span style="font-weight:bold;"&gt;Prostota programowania&lt;/span&gt;
Aby system powstawał szybko i zawierał możliwie mało błędów jego programowanie powinno być możliwie proste. W idealnej sytuacji w ogóle nie musimy martwić się bazą - po prostu to co ma być zapamiętane jest zapamiętane. Nie słyszałem o istnieniu takich systemów, ale chętnie się o nich dowiem. 

Dlatego musimy zmniejszyć swoje oczekiwania. Programista powinien być zmuszony do napisania minimalnej ilości dodatkowego kodu aby obsłużyć bazę danych. Programista nie powinien również być zmuszony przez wybór DBMSa do uczenia się nowego języka - tylko na potrzeby umieszczania danych w bazie i wyciągania ich z niej.

System, w którym piszemy aplikację w języku XYZ i robimy zapytania do bazy w języku XYZ byłby wystarczająco dobry zakładając, że nie zmusza to programistów do stosowania dziwnych konstrukcji językowych.

&lt;span style="font-weight:bold;"&gt;Prostota administracji&lt;/span&gt;
Administrator bazy danych powinien być osobą, która wie jak ją pielęgnować, ale nie przez znajomość znaczenia miliarda zmiennych konfiguracyjnych. Takie podejście powoduje, że ciężko zastąpić admina inną osobą gdy np. ma urlop. Dodatkowo niepotrzebnie sztucznie zmniejsza liczbę dobrych administratorów na rynku.

DBMS powinien być możliwie prosto konfigurowalny. Np. sam powinien dbać o równomierne obciążanie maszyn, których używa. Wykorzystywanie dodatkowych narzędzi (jak haproxy) jest niedopuszczalne.

&lt;span style="font-weight:bold;"&gt;Możliwość tworzenia raportów&lt;/span&gt;
Wiemy, że programiści powinni mieć możliwie prosty interfejs. Większość zapytań przez nich robionych to najprostsze wstawianie danych lub wybieranie danych bez skomplikowanych warunków.

Rzadko trzeba policzyć jakąś skomplikowaną statystykę albo stworzyć raport. Wówczas przydałby się inny interfejs niż ten dla programistów. Mógłby pozwalać na grupowanie, joinowanie, zliczanie itp. Nie musi być zintegrowany z językiem, bo statystyki będą prawdopodobnie liczyć osobne moduły (nie sam system, który przetwarza żądania użytkowników). Być może będą to osobne aplikacje.

&lt;span style="font-weight:bold;"&gt;Tania&lt;/span&gt;
Idealnie system jest darmowy. Dobrze by było gdyby istniały firmy oferujące płatne wsparcie - gdybyśmy nie potrafili sami rozwiązać swoich problemów. Duże i zdrowe community to też ogromny plus, bo nie musielibyśmy płacić za każdy drobiazg, o który chcemy zapytać.

&lt;span style="font-weight:bold;"&gt;Co nie jest potrzebne&lt;/span&gt;
Wiemy jakie są nasze priorytety. Wiemy również, że obecne rozwiązania oferują różne możliwości. Niektóre oferują więcej niż chcemy. Z czego możemy zrezygnować w pierwszej kolejności (mówimy o api dla programistów)?

Operacja join, tranzakcje, integralność referencyjna, grupowanie i zliczanie. Praktyka pokazuje, że są to rzadko wykorzystywane możliwości bazy danych w opisywanym zastosowaniu - skomplikowane zapytania są znacznie rzadsze niż proste zapytania. Mimo to byłoby bardzo miło gdyby baza danych udostępniała te operacje jako osobny interfejs jeżeli to możliwe.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5910891875654861939?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5910891875654861939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/czego-oczekuje-od-bazy-danych.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5910891875654861939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5910891875654861939'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/czego-oczekuje-od-bazy-danych.html' title='Czego oczekuję od bazy danych?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4025076061702400191</id><published>2011-01-09T09:47:00.004-08:00</published><updated>2011-01-09T10:25:38.197-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><title type='text'>Testowanie kodu Perla - część 2</title><content type='html'>&lt;span style="font-weight:bold;"&gt;W poprzedniej notce opisałem jak łatwo można testować kod napisany w języku Perl. Tym razem chcę pokazać jak można tworzyć Unit Testy bardziej podobne do tego co znamy (klasa z testami) oraz jak robić Mocki.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Moduł Test::Class&lt;/span&gt;
Zazwyczaj Frameworki do unit testów dostarczają klasę, po której dziedziczy nasza klasa testów. Taka klasa może posiadać metody setup i teardown - wywoływane odpowiednio na początku i na końcu każdego testu. Testy piszemy wówczas w postaci metod. 

Tak samo możemy zrobić w Perlu. Tworzymy klasę, która dziedziczy po Test::Class. Do każdej metody-testu dopisujemy :Test po jej nazwie aby framework wiedział, że to jest test. Aby stworzyć metody typu setup/teardown piszemy odpowiednio :Test(setup) oraz :Test(shutdown). Pod koniec tej notki jest przykład, który pokazuje jak wygląda plik z testami.

&lt;span style="font-weight:bold;"&gt;Moduł Test::MockObject&lt;/span&gt;
Celem testu jest sprawdzenie konkretnej funkcjonalności. Jest to bardzo proste w przypadku małych klas oraz klas, które nie zależą od innych. Gorzej jeżeli takie zależności są. Wówczas trzeba poprawnie utworzyć te obiekty lub obiekty, które je udają - makiety. Np. jeżeli nie chcemy aby logger wypisywał coś na ekran podczas testowania to możemy dostarczyć makietę klasy Logger, która będzie miała puste implementacje metod.

Do szybkiego tworzenia makiet można wykorzystać moduł Test::MockObject. Jego użycie jest bardzo proste. Najpierw tworzymy mock:&lt;pre&gt;my $mock = new Test::MockObject;&lt;/pre&gt;Następnie definujemy potrzebne metody:&lt;pre&gt;$mock-&gt;mock('NAZWA_METODY', FUNKCJA);&lt;/pre&gt;Przykład kodu jest na końcu notki.

To proste użycie wystarcza jeżeli piszemy kod, który daje się testować - np. przekazujemy zależności przez parametry konstruktora. Test::MockObject pozwala jednak radzić sobie nawet z sytuacją, w której zależności są tworzone wewnątrz testowanej klasy. W tym celu trzeba oszukać Perla i powiedzieć mu, że odpowiednia klasa została już załadowana. Następnie dostarczamy makietę konstruktora. Od tej pory wywołanie new zwraca naszą makietę, a nie prawdziwy obiekt! Prawda, że sprytne?&lt;pre&gt;$mock-&gt;fake_module("NAZWA::MODUŁU");
$mock-&gt;fake_new("NAZWA::MODUŁU");&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;Przykład&lt;/span&gt;
Poniższy kod przedstawia test prostego EventManagera. Ma on pokazać sposób wykorzystania modułów Test::Class oraz Test::MockObject (a nie np. poprawny sposób pisania czy nazywania testów).&lt;pre&gt;#!/usr/bin/perl

package EventManager::Test;
use base 'Test::Class';
use Test::More tests =&gt; 8;
use Test::MockObject;
use warnings;
use strict;

use lib '../';
use EventManager;

sub setup :Test(setup) {
    my ($self) = @_;
    my $mock_logger = new Test::MockObject;
    $mock_logger-&gt;mock('debug', sub {});
    $mock_logger-&gt;mock('info', sub {});
    $mock_logger-&gt;mock('warn', sub {});
    $mock_logger-&gt;mock('error', sub {});
    $mock_logger-&gt;mock('fatal', sub {});

    #
    # If dependency injection is not used then we can fake new
    # 
    # $mock-&gt;fake_module("Module::Name"); # fake that module is loaded
    # $mock-&gt;fake_new("Module::Name"); # new Module::Name() will return mock object
    # 

    $self-&gt;{eventmgr} = new EventManager($mock_logger);
}

sub constructor :Test(3) {
    my ($self) = @_;
    isa_ok($self-&gt;{eventmgr}, 'EventManager');
    can_ok($self-&gt;{eventmgr}, 'register');
    can_ok($self-&gt;{eventmgr}, 'dispatch');
}

sub no_handlers_no_crash :Test {
    my ($self) = @_;
    eval {
        $self-&gt;{eventmgr}-&gt;dispatch("unhandled event");
    };
    is($@, '');
}

sub one_handler :Test {
    my ($self) = @_;
    my $x = 0;
    $self-&gt;{eventmgr}-&gt;register("event", sub { $x++; });
    $self-&gt;{eventmgr}-&gt;dispatch("event");
    is($x, 1);
}

sub many_handlers :Test(2) {
    my ($self) = @_;
    my $x = 0;
    my $y = 0;
    $self-&gt;{eventmgr}-&gt;register("event", sub { $x++; $y++; });
    $self-&gt;{eventmgr}-&gt;dispatch("event");
    is($x, 1);
    is($y, 1);
}

sub orthogonal_handlers :Test {
    my ($self) = @_;
    my $x = 0;
    $self-&gt;{eventmgr}-&gt;register("event", sub { $x++; });
    $self-&gt;{eventmgr}-&gt;dispatch("different_event");
    is($x, 0);
}


__PACKAGE__-&gt;runtests;

&lt;/pre&gt;Jeszcze przykładowa implementacja klasy EventManager&lt;pre&gt;#!/usr/bin/perl

package EventManager;
use warnings;
use strict;

sub new {
    my ($class, $logger) = @_;
    my $self = {
        handlers =&gt; {},
        logger =&gt; $logger
    };
    bless $self, $class;
    return $self;
}

sub log {
    my ($self) = @_;
    return $self-&gt;{logger};
}

sub register {
    my ($self, $event_name, $handler) = @_;
    $self-&gt;log()-&gt;debug("Registering handler for event $event_name");
    push @{$self-&gt;{handler}{$event_name} }, $handler;
}

sub dispatch {
    my ($self, $event_name) = @_;
    my $handler_list = $self-&gt;{handler}{$event_name};
    if (defined $handler_list) {
        $self-&gt;log()-&gt;debug("Calling handlers for event $event_name");
        for my $handler (@$handler_list) {
            &amp;$handler();
        }
    }
    else {
        $self-&gt;log()-&gt;warn("Trying to dispatch event $event_name, but handler list is empty ");
    }
}

1;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4025076061702400191?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4025076061702400191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/testowanie-kodu-perla-czesc-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4025076061702400191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4025076061702400191'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/testowanie-kodu-perla-czesc-2.html' title='Testowanie kodu Perla - część 2'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5235598804038054367</id><published>2011-01-07T16:17:00.005-08:00</published><updated>2011-01-07T17:17:20.719-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><title type='text'>Testowanie kodu Perla</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jak wiadomo bez testów nigdy nie ma pewności, że kod działa poprawnie. Z testami też nie :-) Na szczęście mając dobre testy można czuć się w miarę bezpiecznie. Języki z dynamicznym typowaniem dają większą swobodę programiście - również w popełnianiu błędów.&lt;/span&gt;

Przez długi czas unikałem pisania testów dla kodu w Perlu - w końcu to proste, małe skrypty. Pewnego dnia stało się - zajrzałem do rozdziału książki, który mówi jak pisać testy dla Perla.

&lt;span style="font-weight:bold;"&gt;KISS&lt;/span&gt;
Pisanie testów jest trudne, zajmuje dużo czasu i miejsca w kodzie. &lt;span style="font-weight:bold;"&gt;Nie&lt;/span&gt;. To niesamowite jak daleko posunęli się projektanci modułu Test::Simple podczas upraszczania procesu zamieniania kawy w kod testów.

Pisanie testu jest tak proste jak dwie litery - "ok". Przykładowy, prosty test wygląda tak:&lt;pre&gt;ok(1 == 1);&lt;/pre&gt;W tym prostym przypadku sprawdzamy czy 1 jest równe 1. Można także dopisać komentarz&lt;pre&gt;ok(1 == 1, "Czy jeden równa się jeden?");&lt;/pre&gt;

&lt;span style="font-weight:bold;"&gt;Prosty przykład&lt;/span&gt;
Zobaczmy jak w praktyce pisze się testy. Wykorzystamy pewien znany ciąg: 0,1,1,2,3,5,8,13,... zadany wzorem rekurencyjnym F0 = 0, F1=1, Fn=Fn-1 + Fn-2. Zadanie polega na napisaniu funkcji, która obliczy n-ty wyraz tego ciągu w możliwie najkrótszym czasie (czyli wykonując jak najmniej obliczeń). 

Bezpośrednia implementacja ma złożoność wykładniczą co niespecjalnie nam odpowiada. Dalej moglibyśmy napisać coś takiego:&lt;pre&gt;sub fib_dynamic {
    my ($n) = @_;
    my ($a,$b) = (0,1);

    ($a, $b) = ($b, $a+$b) for (1..$n);
    
    return $a;
}&lt;/pre&gt;To dość prosta implementacja. Złożoność jest liniowa względem n, tzn. dla danego parametru n wykonamy ok n operacji aby obliczyć Fn. Napiszmy kilka testów&lt;pre&gt;#!/usr/bin/perl
use warnings;
use strict;
use Test::Simple tests =&gt; 10;

my $test_data = {
    0 =&gt; 0,
    1 =&gt; 1,
    2 =&gt; 1, 
    3 =&gt; 2,
    4 =&gt; 3,
    5 =&gt; 5,
    6 =&gt; 8,
    45 =&gt; 1134903170,
    49 =&gt; 7778742049,
    -1 =&gt; 0,
};

for my $arg (sort keys %$test_data) {
    my $expected_result = $test_data-&gt;{$arg};
    ok(fib($arg) == $expected_result, "testing fib($arg), expecting $expected_result");
}

sub fib {
    my $n = shift;
    return fib_dynamic($n);
}&lt;/pre&gt;W tym krótkim programie mówimy, że mamy 10 testów. Przygotowujemy 10 par (argument, oczekiwana wartość) i sprawdzamy czy dla każdego z argumentów fib zwraca prawidłowy wynik.

Mogłoby się wydawać, że nie ma sensu testować tak prostej funkcji. Załóżmy jednak, że z czasem nasza funkcja fib_dynamic staje się bardzo popularna w projekcie, jest wywoływana bardzo często i to z bardzo dużymi argumentami (rzędu kilkuset). Staje się problemem wydajnościowym i czas ją usprawnić. 

Jakiś geniusz wpada na pomysł napisania czegoś takiego:&lt;pre&gt;sub fib_matrix {
    my ($n) = @_;

    return 0 if $n &lt; 1;
    my ($aa,$ab,$ba,$bb) = r_fib_matrix_aux($n, 1,1,1,0);
    return $ab;
}

sub r_fib_matrix_aux {
    my ($n, $aa, $ab, $ba, $bb) = @_;

    return (1,1,1,0) if $n == 1;
    if ($n % 2 == 0) {
        my ($raa, $rab, $rba, $rbb) = r_fib_matrix_aux($n/2, $aa*$aa+$ab*$ba, $aa*$ab+$ab*$bb, $ba*$aa+$bb*$ba, $ba*$ab+$bb*$bb); # Aux = A^{n/2}
        return ($raa*$raa+$rab*$rba, $raa*$rab+$rab*$rbb, $rba*$raa+$rbb*$rba, $rba*$rab+$rbb*$rbb);  # Aux^2
    }
    else {
        my ($raa, $rab, $rba, $rbb) = r_fib_matrix_aux($n-1, $aa+$ab, $aa, $ba+$bb, $ba); # Aux = A^{n-1}
        return ($raa+$rab, $raa, $rba+$rbb, $rba);
    }
}&lt;/pre&gt;Zysk widać od razu - zakładając, że warunek $n%2==0 zawsze zachodzi mamy log(n) wywołań (bo zawsze zmniejszamy n o połowę). Zauważmy, że ten warunek może zawodzić tylko co drugi raz (dla jakich danych?), zatem łącznie wykonamy O(log(n)) wywołań funkcji.

Generalnie podoba nam się taka optymalizacja - pod warunkiem, że niczego nie psuje. Myślę, że nie dla każdego poprawność takiej funkcji jest oczywista.

// to jest ten moment kiedy dziękujesz ludziom, którzy kazali Ci pisać testy

&lt;span style="font-weight:bold;"&gt;Gdy potrzebujemy więcej możliwości&lt;/span&gt;
Wiemy już, że warto testować i że jest to proste. Co jeżeli potrzebujemy większych możliwości? Wystarczy wykorzystać moduł Test::More. Oto prosty przykład, który demonstruje jego możliwości:&lt;pre&gt;#!/usr/bin/perl
use warnings;
use strict;
use Test::More;

plan tests =&gt; 7;

is(jeden(), 1, "jeden() returns 1");
isnt(trzy(), 2, "jeden() does not return 2");
my $a = {1=&gt;[1,2,3,4,{5555 =&gt; 3333}], "hello", {"witaj" =&gt; ["hi", "hello"]}};
is_deeply($a, $a);
cmp_ok(1, '&lt;', 2);
cmp_ok("hello", "ne", "hi");

my $m = new Machine;
isa_ok($m, 'Machine');
can_ok($m, 'ask', 'donothing');


sub jeden {
    return 1;
}

sub trzy {
    return 3;
}

package Machine;

sub new {
    my ($class) = @_;
    my $self = {};
    bless $self, $class;
    return $self;
}

sub ask {
    my ($self, $question) = @_;
    if ($question eq "What is the the answer to life the universe and everything?") {
        return 42;
    }
    else {
        return "I don't know\n";
    }
}

sub donothing {

}&lt;/pre&gt;Poza podstawowymi testami potrafimy także sprawdzać czy można wywołać określoną metodę (can_ok) na obiekcie oraz czy obiekt jest odpowiedniego typu (isa_ok). Jest także funkcja do porównywania skomplikowanych, zagnieżdżonych struktur (is_deeply).

&lt;span style="font-weight:bold;"&gt;Uruchamianie testów&lt;/span&gt;
Aby testować nasz projekt tworzymy katalog t/ i umieszczamy w nim pliki z testami (każda testowana funkcjonalność to jeden plik perlowy z rozszerzenim .t - te pliki piszemy wykorzystując moduły Test::Simple oraz Test::More). Teraz wystarczy wywołać metodę runtests z modułu Test::Harness aby otrzymać ładne podsumowanie co się powiodło, a co nie. Dlatego w katalogu nadrzędnym do t/ umieszczamy taki skrypt:&lt;pre&gt;#!/usr/bin/perl

use warnings;
use strict;
use Test::Harness;

runtests(&amp;lt;t/*.t&amp;gt;);&lt;/pre&gt;Uruchomienie tego skryptu spowoduje uruchomienie wszystkich testów z katalogu t/ oraz wyświetlenie raportu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5235598804038054367?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5235598804038054367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/testowanie-kodu-perla.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5235598804038054367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5235598804038054367'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2011/01/testowanie-kodu-perla.html' title='Testowanie kodu Perla'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4622665625721674792</id><published>2010-12-29T02:55:00.004-08:00</published><updated>2010-12-29T03:16:09.405-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>A bus factor</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Czasami podczas developmentu jest tak, że kluczową część wiedzy na temat projektu posiada tylko jedna osoba. Łatwo sobie wyobrazić jak katastrofalne skutki może mieć choroba czy odejście takiego programisty.&lt;/span&gt;

Okazuje się, że ten problem ma swoją nazwę - "a bus factor", czyli ilość developerów jaką musi potrącić autobus aby pogrążyć projekt w chaosie.

Oczywiście im wyższa wartość tego wskaźnika tym lepiej. Z drugiej strony, w przypadku komercyjnych produktów, zatrudnianie większej ilości ludzi kosztuje.

Wydaje się więc, że stosowanie technik, które sprzyjają dzieleniu się wiedzą wewnątrz zespołu może być rozwiązaniem. Dla przykładu: programowanie w parach, code review, wspólny design, małe i częste commity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4622665625721674792?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4622665625721674792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/12/bus-factor.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4622665625721674792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4622665625721674792'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/12/bus-factor.html' title='A bus factor'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1647077947175674271</id><published>2010-12-12T14:55:00.005-08:00</published><updated>2010-12-12T15:15:13.548-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ogólne'/><title type='text'>Pytania sprawdzające pracodawcę by Joel</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Ostatnio na forum gamedev Nembutal rozpoczął wątek, w którym, niby pytając jak zweryfikować pracodawcę, napisał w kilku punktach jak wg niego można to zrobić...&lt;/span&gt;

Wątek można znaleźć &lt;a href="http://forum.gamedev.pl/index.php/topic,19945.0.html"&gt;na forum gamedev.pl&lt;/a&gt;. Część z jego propozycji wydaje mi się trochę zbyt "inwazyjna". Korzyścią z tego wątku jest fakt, że Nembutal w jednym z dalszych postów wrzucił link do artykułu, którego nie miałem przyjemności czytać wcześniej, a który bardzo mi się spodobał.

Można go znaleźć &lt;a href="http://www.joelonsoftware.com/articles/fog0000000043.html"&gt;na stronie Joela&lt;/a&gt;. 

Joel pokazuje jakie jest 12 kroków aby pisać dobry soft. Można zapytać o te rzeczy podczas rozmowy kwalifikacyjnej i przyznać tym samym ocenę danej firmie. Według Joela 10 i mniej punktów oznacza, że jest źle. 

Brak któregoś z tych punktów w firmie może też oznaczać miejsce na ulepszenia :-D więc nie musi być tak źle. IMO na najgorzej byłoby gdyby potencjalny pracodawca nie zgadzał się z proponowanymi praktykami.

Magiczna dwunastka to:

&lt;blockquote&gt;Do you use source control?
Can you make a build in one step?
Do you make daily builds?
Do you have a bug database?
Do you fix bugs before writing new code?
Do you have an up-to-date schedule?
Do you have a spec?
Do programmers have quiet working conditions?
Do you use the best tools money can buy?
Do you have testers?
Do new candidates write code during their interview?
Do you do hallway usability testing?
&lt;/blockquote&gt;

Zapraszam do lektury &lt;a href="http://www.joelonsoftware.com/articles/fog0000000043.html"&gt;artykułu po więcej szczegółów&lt;/a&gt; oraz wyjaśnienia poszczególnych punktów - skąd się biorą i co oznaczają.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1647077947175674271?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1647077947175674271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/12/pytania-sprawdzajace-pracodawce-by-joel.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1647077947175674271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1647077947175674271'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/12/pytania-sprawdzajace-pracodawce-by-joel.html' title='Pytania sprawdzające pracodawcę by Joel'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-5701569429391290482</id><published>2010-10-26T00:00:00.001-07:00</published><updated>2010-10-26T00:00:01.715-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Kindle'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>If you want to use your Kindle, please eject your Kindle from your computer.</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Okazuje się, że niektóre urządzenia trzeba najpierw "wysunąć" aby można było ładować je przez usb i równocześnie na nich pracować. Kindle należy do nich. Niestety trudno jest to osiągnąć z poziomu interfejsu graficznego pod Linuksem&lt;/span&gt;

Można "wysunąć" urządzenie przy użyciu terminala. Dlatego włączamy terminal (np. xterm) i wykonujemy polecenie &lt;pre&gt; $ sudo fdisk -l &lt;/pre&gt;i szukamy urządzenia, które ma system plików FAT32 - to jest nasz Kindle. Jeżeli jest więcej takich urządzeń to należy odłączyć od komputera pendrive, mp3 itp.

Załóżmy, że Kindle to /dev/sdb1. Teraz możemy "wysunąć" urządzenie poleceniem &lt;pre&gt; $ sudo eject /dev/sdb1 &lt;/pre&gt;

HTH :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5701569429391290482?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5701569429391290482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/10/if-you-want-to-use-your-kindle-please.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5701569429391290482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5701569429391290482'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/10/if-you-want-to-use-your-kindle-please.html' title='If you want to use your Kindle, please eject your Kindle from your computer.'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5341575223000063211</id><published>2010-09-21T11:18:00.003-07:00</published><updated>2010-09-21T11:53:24.367-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='hasło'/><title type='text'>Reset zapomnianego hasła admina w Windows Vista....</title><content type='html'>&lt;span style="font-weight:bold;"&gt;... przy założeniu, że mamy system Linuksowy na tym samym komputerze lub przynajmniej jakieś "live cd" (np. knoppix). Metoda jest sprawdzona przeze mnie. Potwierdzam, że na dzień dzisiejszy działa :-) Jest przy tym prosta i szybka.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Po co resetować hasło?&lt;/span&gt;
Nie wiem. W moim przypadku... kupiłem sequela mojej ulubionej gry. Na co dzień używam Linuksa, ale przypomniałem sobie, że przecież z komputerem dostałem Windows Vista. No to do dzieła - instalujemy... Nie tak prędko. Okazało się, że założyłem hasło, którego oczywiście nie pamiętam. Jakie mogą być inne powody? Pewnie różne. Zaznaczam jednak, że tę metodę stosujesz na własną odpowiedzialność - szczególnie jeżeli konto admina nie jest Twoje.

&lt;span style="font-weight:bold;"&gt;Montujemy dysk Windowsa&lt;/span&gt;
Ładujemy się na Linuksa i szukamy partycji, na której mamy zainstalowany Windows. Można to zrobić poleceniem&lt;pre&gt;$sudo cfdisk /dev/hda
&lt;/pre&gt;Po lewej stronie dysku Windowsa będzie napis NTFS (system plików). W moim przypadku wiem, że jest to urządzenie /dev/sda5. Teraz trzeba go zamontować. Wchodzimy do swojego katalogu domowego i wydajemy polecenia:&lt;pre&gt; $ mkdir win
 $ sudo mount /dev/sda5 win
&lt;/pre&gt;Oczywiście przy drugim poleceniu zamiast /dev/sda5 wpisujemy to co znaleźliśmy poleceniem cfdisk.

&lt;span style="font-weight:bold;"&gt;Odszukujemy plik SAM i resetujemy hasło&lt;/span&gt;
Teraz idziemy do katalogu z plikiem SAM (ta metoda może działać też na innych Windowsach, ale tam plik SAM może mieć inną lokalizację).&lt;pre&gt; $ cd win/Windows/System32/config/
&lt;/pre&gt;Instalujemy chntpw: &lt;pre&gt; $ sudo aptitude install chntpw &lt;/pre&gt; Teraz musimy tylko zresetować hasło:&lt;pre&gt; $ chntpw -u lmm SAM
&lt;/pre&gt; Zamiast lmm podajemy nazwę użytkownika, któremu chcemy zmodyfikować hasło (listę użytkowników można uzyskać poleceniem chntpw -l SAM). Na zadane przez program pytanie odpowiadamy 1, czyli "Clear (blank) user password", następnie potwierdzamy literką 'y'.

A teraz... &lt;span style="font-weight:bold;"&gt;You wanna piece of me, boy?&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5341575223000063211?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5341575223000063211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/09/reset-zapomnianego-hasa-admina-w.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5341575223000063211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5341575223000063211'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/09/reset-zapomnianego-hasa-admina-w.html' title='Reset zapomnianego hasła admina w Windows Vista....'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4273155633928148758</id><published>2010-09-19T06:00:00.001-07:00</published><updated>2010-09-19T06:34:52.727-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jedzenie'/><title type='text'>Omlet programisty</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Tym razem temat nieco luźniej związany z programowaniem, ale niemniej ważny - jedzenie. Zobaczmy jak szybko (może to nie jest najszczęśliwsze słowo) przygotować przepyszny omlet programisty. &lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Algorytm przyrządzania: &lt;/span&gt;&lt;pre&gt;
#include &amp;lt;kuchnia.h&amp;gt;
#include &amp;lt;narzędzia_i_sztućce.h&amp;gt; // for łyżka, widelec, patelnia, mikser
#include &amp;lt;naczynia.h&amp;gt; // for garnek, miseczka
#include &amp;lt;składniki.h&amp;gt; // for jajko, mąka, sól
// #include &amp;lt;dodatki.h&amp;gt; // not used in version 1.0

int main() {
  goto kuchnia;

kuchnia:
  // żółtka do jednego pojemnika, białka do drugiego
  for (int i = 0; i &amp;lt; 3; ++i) {
    (zolto, bialko) = jaja[i].rozbij_i_rozdziel_zoltko_od_bialka();
    garnek.insert(bialko);
    miseczka.insert(zoltko);    
  }
 
  // białka ubijamy. do ubitej piany dodajemy żółtka oraz mąkę
  garnek.ubij_piane(mikser);
  garnek.insert(miseczka.get_żółtka());
  for (int i = 0; i &amp;lt; 3; ++i) {
    garnek.insert(mąka.get_łyżka_stolowa());
  }

  garnek.wymieszaj(); // łyżką, NIE mikserem     

  patelnia.podgrzej();
  patelnia.dodaj(olej);
  while (!garnek.empty()) {
    patelnia.insert(garnek.get_łyżka_masy());
  }

  //  sleep(); // jednak to nie jest dobry pomysł
  do {
    wait(1);
  }
  while(!omlet.zarumieniony_od_dołu());

  omlet.przewróć_na_drugą_stronę(); // WARNING! this may crash. Should be fixed in 2.0
  
  do {
    wait(1);
  } while(!omlet.gotowy());

  patelnia.przerzuć_zawartość_na_talerz();

  // gl hf
}&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;Kilka dodatkowych uwag na temat omletu progarmisty&lt;/span&gt;
&lt;ul&gt;
 &lt;li&gt;Powinien go przyrządzać programista - jakoś trzeba uzasadnić nazwę&lt;/li&gt;
 &lt;li&gt;Przy pierwszej próbie proponuję robić go z dwóch jaj - łatwiej go potem obrócić&lt;/li&gt;
 &lt;li&gt;Jeżeli piana nie chce się ubić to prawdopodobnie dostało się do niej żółtko&lt;/li&gt;
 &lt;li&gt;Podawać na słodko (z dżemem, kremem kakaowym, etc) lub na słono (do masy można dorzucić warzywa, ser, szynkę etc). &lt;/li&gt;
 &lt;li&gt;Omlet jest dość sycący, więc nie przesadzać z ilością&lt;/li&gt;
&lt;/ul&gt;

PS. Przepraszam za brak zdjęć / filmów - karta mi się zapełniła, a nie miałem czasu jej wyczyścić.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4273155633928148758?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4273155633928148758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/09/omlet-programisty.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4273155633928148758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4273155633928148758'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/09/omlet-programisty.html' title='Omlet programisty'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3692754915670834327</id><published>2010-08-26T00:00:00.003-07:00</published><updated>2010-08-26T00:00:06.894-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='boost::asio'/><title type='text'>boost::asio i pominięty handler</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Czasami zdarza się tak, że jeden drobny błąd prowadzi do wielu godzin poszukiwań przyczyny. Dlatego chciałbym opisać dość ciekawą sytuację gdy write_handler nie zostaje wywołany w przypadku asynchronicznego zapisu (biblioteka boost::asio).&lt;/span&gt;

Załóżmy, że mamy aplikację składającą się z dwóch węzłów - serwera A i serwera B. Serwer A wysyła co jakiś czas komunikaty do serwera B. Robi to w sposób asynchroniczny (poprzez boost::asio::async_write i wywołanie io_service::run/run_one/poll/poll_one). Niestety może się zdarzyć tak, że handler, którego wywołanie zaplanujemy przez boost::asio::async_write nie zostanie wywołany!

Oczywiście w takim przypadku nie wiemy czy handler nie wywołał się z powodu jakiegoś błędu czy poprostu jeszcze nie jest gotowy. Dla nas to bez znaczenia - efekt jest taki, że komuniakty nie są wysyłane. Aby zdać sobie sprawę z rozmiaru problemu zauważmy, że jedno takie wywołanie może zablokować wszystkie inne wysyłane komunikaty. Jest tak przez wykorzystanie kolejki komunikatów do wysłania - tak jak w przykładzie &lt;a href="http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/chat/chat_server.cpp"&gt;chat servera&lt;/a&gt;. Polecam analizę metod deliver i handler_write.

Szybkie przeszukanie google daje dwie drogi rozwiązania. Pierwsza wynika wprost z dokumentacji - przed kolejnym wywołaniem metody run/run_one/poll/poll_one należy wywołać io_service::reset. To raczej nie będzie typowy błąd bo dokumentacja jasno to zaznacza.

Kolejna możliwość zwrócona przez google to fakt, że mogliśmy zwolnić dane wykorzystywane przez asio. Jest tak, bo boost::asio::buffer i boost::asio::const_buffer pamiętają jedynie wskaźniki do danych. Zatem utworzenie wektora bajtów i wysłanie go asynchronicznie (z wcześniejszą konwersją na boost::asio::const_buffer) spowoduje, że będziemy odwoływali się do nieistniejącej pamięci (wektor wyjdzie z zakresu funkcji i po sobie posprząta).

Na szczęście to nie jest problem o ile do reprezentacji kolejki danych wykorzystujemy std::deque. Jeżeli pokusimy się o std::vector (z trudnych do uzasadnienia przyczyn) to musimy się liczyć z tym, że wektor może urosnąć poza capacity i przenieść dane w inne miejsce pamięci, a nas zostawić z wiszącym wskaźnikiem w const_buffer.

&lt;span style="font-weight:bold;"&gt;Jest jeszcze jedna możliwość&lt;/span&gt;, którą niestety trudno zdebuggować. Serwer A nagle nie wykonuje handlera zapisu i wszystko wskazuje, że to w serwerze A jest błąd. 

Może się zdarzyć tak, że Server B przestanie odczytywać dane z gniazda (nie wywoła asynchronicznego odczytu po jednym z odczytów - bo ktoś się pomylił).

Wówczas jakiś czas później serwer A przestanie wysyłać dane, bo nie ma gdzie ich wysłać. Stanie się tak przez jedną z cech protokołu TCP. Otóż gdyby szybka maszyna wysyłała pakiety do wolnej to ta druga nie dałaby rady odczytać wszystkich danych. Dlatego protokół TCP steruje ilością możliwych danych do wysłania. Niestety jeżeli serwer B przestał odczytywać to szybko nie będzie można do niego nic wysłać - można powiedzieć, że zostanie uznany za tak wolny, że przyjmie 0 bajtów.

W tym przypadku handler zapisu na maszynie A nigdy nie przejdzie w stan "gotowy" a co za tym idzie bez żadnego błędu serwer A przestanie wysyłać dane.

Mam nadzieję, że ten wpis pomoże komuś ocalić te kilka godzin dochodzenia co się tak naprawdę dzieje ;-)

Jedno szczęście, że informację o pustym oknie (czyli fakt, że serwer B nie może odbierać już danych) możemy zobaczyć wykorzystując program Wireshark :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3692754915670834327?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3692754915670834327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/08/boostasio-i-pominiety-handler.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3692754915670834327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3692754915670834327'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/08/boostasio-i-pominiety-handler.html' title='boost::asio i pominięty handler'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1126965491987841375</id><published>2010-08-01T08:00:00.002-07:00</published><updated>2010-08-01T08:00:06.060-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programowanie sieciowe'/><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><title type='text'>Ograniczenia systemu na liczbę socketów</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Przy programowaniu serwerów można natknąć się na problem z maksymalną liczbą otwartych połączeń sieciowych. Zobaczmy jak można sobie z nim poradzić na przykładzie systemu Linux&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Liczba socketów&lt;/span&gt;
Liczba socketów w Linuksie jest ograniczona przez maksymalną liczbę otwartych plików (z oczywistych względów). Można sprawdzić tę liczbę poleceniem&lt;pre&gt;
 $ cat /proc/sys/fs/file-max&lt;/pre&gt;Tę wartość da się łatwo zmienić edytując plik&lt;pre&gt;
/etc/sysctl.conf&lt;/pre&gt;i ustawiając opcję&lt;pre&gt;
fs.file-max = 200000&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;Limit powłoki&lt;/span&gt;
Sama powłoka również narzuca nam limit na liczbę otwartych plików. Limit można sprawdzić dobrze znanym poleceniem ulimit (z flagą -n lub -a)&lt;pre&gt; $ ulimit -a&lt;/pre&gt;
Natomiast trwałej zmiany limitu doknujemy dopisując do pliku /etc/security/limits.conf&lt;pre&gt;* soft nofile 65536
* hard nofile 65536&lt;/pre&gt;Kolejne kolumny to: domena, typ limitu, co ustawiamy, wartość.

&lt;span style="font-weight:bold;"&gt;Liczba połączeń wyjściowych&lt;/span&gt;
Ostatnia z opisywanych opcji to liczba połączeń wyjściowych. Określa ją zakres dostępnych portów. Aktualną wartość można odczytać z pliku&lt;pre&gt;
/proc/sys/net/ipv4/ip_local_port_range&lt;/pre&gt;Aby ustalić nową wartość modyfikujemy ten plik&lt;pre&gt; # echo 1 65535 &gt; /proc/sys/net/ipv4/ip_local_port_range&lt;/pre&gt;lub zapisujemy zmianę w konfiguracji (plik /etc/sysctl.conf):&lt;pre&gt;# increase system IP port limits
net.ipv4.ip_local_port_range = 1024 65535&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;Windows&lt;/span&gt;
Powyższe zmiany powinny wystarczyć dla systemu Linux. Może ktoś chciałby podzielić się wiedzą (w komentarzach) jak zrobić to na Windowsie?

Mam nadzieję, że komuś ta wiedza pomoże.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1126965491987841375?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1126965491987841375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/08/ograniczenia-systemu-na-liczbe-socketow.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1126965491987841375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1126965491987841375'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/08/ograniczenia-systemu-na-liczbe-socketow.html' title='Ograniczenia systemu na liczbę socketów'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4247375407937041629</id><published>2010-06-20T03:38:00.004-07:00</published><updated>2010-06-20T05:52:22.138-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Filozofia'/><title type='text'>Pięć poziomów kompetencji</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Piątkowy post &lt;a href="http://regedit.gamedev.pl/news_1380_programmer_incompetence.html"&gt;Regedita&lt;/a&gt; doprowadził mnie do kilku "przemyśleń" na temat poziomu kompetencji programistów.&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Prezentacje o dobrych praktykach&lt;/span&gt;
Regedit wspomina prezentację &lt;a href="http://www.slideshare.net/olvemaudal/solid-c-by-example"&gt;Solid C++ by Example&lt;/a&gt;. Jedyna moja obserwacja dotycząca tej części jest taka, że część programistów lubi tworzyć slajdy "jak dobrze programować" czy "jak być guru", albo lepiej "dobre praktyki...". Czemu tak robią - nie wiem. Myślę, że rozwikłanie tej zagadki jest raczej tematem dla psychologów :-) 

W tego typu prezentacjach jest zazwyczaj garść porad typu "sekcje private po sekcji public, gwiazdka przy typie", które nie wpływają na końcowy produkt - są to raczej tematy "filozoficzne". Ich jedynym celem jest chyba wywoływanie kłótni wśród programistów, którzy przeczytali prezentacje różnych "guru". Poza takimi pustymi tematami pojawiają się często trywialne wskazówki np. "nie używaj using namespace std;". 

Myślę, że jedyną wartością z przejrzenia takiej prezentacji jest wiedza jakiego typu dokumenty odrzucać już po przeczytaniu tytułu :-)

&lt;span style="font-weight:bold;"&gt;Pięć poziomów niekompetencji&lt;/span&gt;
Regedit wspomina również IMO bardzo wartościowy wpis
&lt;a href="http://coderoom.wordpress.com/2010/03/19/5-stages-of-programmer-incompetence/"&gt;5-stages-of-programmer-incompetence&lt;/a&gt;. Autor zauważa pięć typów kompetencji programistów:
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The Enthusiastic Newbie&lt;/li&gt;
&lt;li&gt;The Budding Genius&lt;/li&gt;
&lt;li&gt;The Abstraction Freak&lt;/li&gt;
&lt;li&gt;The Veteran&lt;/li&gt;
&lt;li&gt;The ‘Guru’&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
Każdy z nich jest pułapką - schematem, z którego ciężko się wydostać. Myśląc w dany sposób wydaje się, że jest on słuszny. Z tego powodu warto jest wiedzieć czym charakteryzują się poszczególne typy. Łatwiej wówczas wydostać się z pułapki. Ta wiedza przydaje się również do rozpoznawania poziomu umiejętności kandydatów na współpracowników. 

Celem nie jest wyznaczenie swojego aktualnego "poziomu", lecz stwierdzenie przez które etapy się już przeszło. Pamiętam siebie na każdym z pierwszych trzech etapów, a Ty?

&lt;span style="font-weight:bold;"&gt;The Enthusiastic Newbie&lt;/span&gt;
Zawsze byłem zdania, że warto wybierać ludzi kontaktowych i pełnych energii. Zmieniłem zdanie po przeczytaniu charakterystyki etapu pierwszego. Patrząc wstecz, widzę że mówienie iż ktoś jest ok, bo ma w sobie dużo pasji było zazwyczaj błędem.

Szczęśliwie niezwykle łatwo rozpoznać newbie - znają tylko jedną technologie i nie mają pojęcia o algorytmice. Często też piszą projekty bez podziału na moduły. 

Trzeba powiedzieć wprost - newbie bardzo szkodzą zespołowi, jeżeli nie widzą pułapki, w której są. Nie mając porównania zalet kilku technologii, często zamiast dobrać narzędzie do problemu, "jedzą zupę widelcem" gdyż umieją wykorzystać tylko widelec.

Jeżeli dostrzegą pułapkę to mogą być bardzo przydatni. Początkujący często są zafascynowani programowaniem. Wprowadzają przez to wiele entuzjazmu.

&lt;span style="font-weight:bold;"&gt;The Budding Genius&lt;/span&gt;
Niestety na temat tej grupy nie udało mi się wyciągnąć żadnych wniosków. Wydaje mi się, że nie ma zbyt wielu takich programistów w moim otoczeniu. Wg autora charakteryzuje ich przeświadczenie, że świat powinien się od nich uczyć. Objawia się to tym, że chcą pisać własną implementację np. klasy vector.

&lt;span style="font-weight:bold;"&gt;The Abstraction Freak&lt;/span&gt;
Pamiętam jak pisałem wszystko, wykorzystując jak najwięcej wzorców i technik OO. Spowodowało to, że na studiach opóźniło się (miałem dość spory poślizg) oddanie kilku projektów. Obecnie myślę, że było to bardzo ważne doświadczenie. Wydaje się wówczas, że dzięki wzorcom nasze rozwiązania są elastyczne, co przekłada się na krótszy czas realizacji projektu.

Niestety to tylko złudzenie! Tworzenie tego typu abstrakcji zajmuje sporo czasu. Co więcej, często trzeba pisać dodatkowe narzędzia, aby ponownie nie dokładać czasu przy wykorzystaniu danej abstrakcji. Często abstrakcja uderza w wydajność. Pisząc kod czysto obiektowy trudno jest np. poprawnie wykorzystać cache.

Jednocześnie bez tego typu doświadczenia nie da się docenić OO (ewentualnie świadomie odrzucić). Nie widząc zalet i problemów OO można co najwyżej powtarzać usłyszane "prawdy".

Myślę, że znajomość tego typu prostamisty jest bardzo ważna. Uważam, że taką osobę warto zatrudnić, bo w niedługim czasie zauważy problemy OO (raczej trudno jest przez dłuższy czas ignorować ilość kodu jaką trzeba dodatkowo naklepać).

Co więcej, jeżeli ktoś przyzna się, że tak robił to mamy pewność, że nie jest już początkującym :-)

&lt;span style="font-weight:bold;"&gt;A jakie etapy Ty przeszedłeś i gdzie możesz być teraz?&lt;/span&gt;
To pytanie warto sobie zadać. Może tkwimy w kolejnej pułapce? Z pewnością łatwiej z niej uciec, jeżeli rozumie się problem.

Sam znam obecnie wiele technologii i staram się zawsze dobierać narzędzia do zadania. Zdarzyło mi się również nauczyć nowego języka tylko dlatego, że te które znałem, nie pasowały do zadania. Unikam pisania nadmiernej ilości "ogólnego kodu", ale jednocześnie wolę umieszczać możliwie dużo elementów w bibliotece - po co pisać kod dwukrotnie, skoro mogę to zrobić raz? Tworzę też spore ilości unit testów, co w jakiś sposób współgra z pisaniem asercji na poziomie "The Veteran".

Na temat "The Veteran" nie chcę wyciągać wniosków, bo obawiam się iż jeszcze nie jest za mną. Kto wie - może któregoś dnia zacznę pisać 10 asercji na początku każdej funkcji? Wiem jedno. Kilkukrotnie już powiedziałem, że należy coś zrobić "porządnie". 

&lt;span style="font-weight:bold;"&gt;Czy "properly" to to samo co "done"?&lt;/span&gt;
Autor artykułu stwierdza, że na poziomie "The Veteran" pułapką jest myślenie iż "Doing things ‘properly’ is the same as getting things done, but better.". Muszę przyznać, że ciężko mi jest pojąć dlaczego to jest złe myślenie. 

Wielokrotnie widziałem działający kod, którego rozszerzanie jest praktycznie niemożliwe (ba! Nawet samo zrozumienie o co chodzi jest trudne) - dodanie funkcjonalności grozi katastrofą. 

Myślę, że to nie jest właściwy sposób tworzenia oprogramowania. W końcu po wydaniu produktu zazwyczaj jest apetyt na kolejne wersje i udoskonalenia. Czy wystarcza więc aby działało? 

Z drugiej strony każdy działający produkt jest lepszy niż napisany "poprawnie", ale nieskończony. 

Może kiedyś ktoś pokaże mi, że "napisany prawidłowo" to to samo co "działający". A może sam to zauważę? :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4247375407937041629?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4247375407937041629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/06/piec-poziomow-kompetencji.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4247375407937041629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4247375407937041629'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/06/piec-poziomow-kompetencji.html' title='Pięć poziomów kompetencji'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5922683121930377464</id><published>2010-05-30T06:00:00.002-07:00</published><updated>2010-05-30T06:00:02.360-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='Lua'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Lua API. PANIC: unprotected error in call to Lua API (invalid key to 'next')</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Ostatnio zdarzył mi się błąd, który spowodował, że program od razu przestawał działać. Na ekranie pojawiało się jedynie tytułowe "PANIC: unprotected error in call to Lua API (invalid key to 'next')". Błąd był związany z Lua i wydaje mi się, że nie od razu jest oczywiste o co chodzi (przynajmniej dla mnie nie było). Mam nadzieję, że tym wpisem oszczędzę komuś trochę czasu spędzonego na szukaniu przyczyny.&lt;/span&gt;

Zanim przejdę do analizy, chcę podkreślić, że nie wyobrażam sobie szukania tego błędu w kodzie działającego programu. Sam miałem wystarczająco dużo szczęścia, że ten drobny problem objawiał się już przy testach. Tak, &lt;span style="font-weight:bold;"&gt;opłaca się testować swój kod, a tdd może zaoszczędzić sporo czasu.&lt;/span&gt;

Ok, to teraz popatrzmy na następujący kod:&lt;pre&gt;int main() {
    lua_State* lua = luaL_newstate();
    luaL_openlibs(lua);

    luaL_dostring(lua, "a = {abc=1, [1]=2, efg=3}"); // (1)
    luaL_dostring(lua, "for i,u in pairs(a) do print(i .. ' = ' .. u); end"); // (2)

    lua_getglobal(lua, "a");
    for (lua_pushnil(lua); lua_next(lua, -2); lua_pop(lua, 1)) { // (3)
        std::cout &lt;&lt; lua_tostring(lua, -2) &lt;&lt; " " &lt;&lt; lua_tostring(lua, -1) &lt;&lt; "\n"; // (4)
    }

    lua_close(lua);
}&lt;/pre&gt;W linijce (1) wykonujemy napis z kodem Lua, który tworzy tablicę o nazwie a. Ta tablica zawiera trzy pary ("abc", 1), (1, 2), ("efg", 3). Warto zaznaczyć, że dwa klucze są typu 'string', a jeden typu 'number'.

W kolejnej linijce (2) wykonujemy prostą pętlę Lua, która wypisuje elementy na ekran. Otrzymujemy taki wynik:
&lt;pre&gt;1 = 2
efg = 3
abc = 1&lt;/pre&gt;Następnie w linijce (3) tworzymy identyczną pętlę, ale w C++. Wykorzystując funkcję lua_next przechodzimy po kolejnych elementach. Spodziewamy się podobnego wyjścia jak w przypadku poprzedniej pętli. Jednak naszym oczom ukazuje się:
&lt;pre&gt;1 2
PANIC: unprotected error in call to Lua API (invalid key to 'next')&lt;/pre&gt;i program kończy swoje działanie.

Dość oczywiste jest, że błąd wynika z faktu iż jednym z kluczy jest 'int', podczas gdy pozostałe są typu 'string'. Mimo to wyłączenie się programu z powodu niepoprawnych danych jest niechcianym zachowaniem (szczególnie, że nie zostaje wypisany sensowny błąd).

Program kończy swoje działanie, bo maszyna Lua wykonuje funkcję panic w przypadku błędnego argumentu funkcji next. Możemy podmienić funkcję panic aby nie wyłączała programu przy użyciu lua_atpanic, ale to nie rozwiązuje naszego problemu (dalej nie wiemy o co chodzi).

Funkcja lua_next przy każdym wywołaniu pobiera wartość klucza ze stosu i znajduje kolejną parę (klucz,wartość). Jeżeli wartość klucza jest nil to zostaje zwrócony pierwszy klucz. Jeżeli klucz jest niepoprawny to zostaje wywołana funkcja panic.

Zauważmy, że nasza pętla na szczycie stosu zawsze zostawia klucz z poprzedniego wywołania. Jak to jest możliwe, że jest on niepoprawny? Okazuje się, że za błąd odpowiedzialna jest funkcja lua_tostring.

Ta funkcja patrzy na wartość ze stosu, rzutuje ją na string i zwraca jako wynik. Okazuje się, że efektem ubocznym jest zmiana typu wartości na stosie na 'string'. Wówczas funkcja lua_next dostaje jako argument "1", a takiej wartości nie ma w tablicy - jest tylko 1.

Można łatwo zaradzić temu problemowi na dwa sposoby:
1. sprawdzając czy wartość nie jest przypadkiem innego typu niż 'string'
lub
2. duplikując wartość na stosie przed konwersją do typu 'string'. &lt;pre&gt;int main() {
    lua_State* lua = luaL_newstate();
    luaL_openlibs(lua);

    luaL_dostring(lua, "a = {abc=1, [1]=2, efg=3}");
    luaL_dostring(lua, "for i,u in pairs(a) do print(i .. ' = ' .. u); end");

    lua_getglobal(lua, "a");
    for (lua_pushnil(lua); lua_next(lua, -2);) {
        // stack: value, key, table
        std::string value = lua_tostring(lua, -1);
        lua_pop(lua, 1);

        // stack: key, table
        lua_pushvalue(lua, -1);

        // stack: key, key, table
        std::string key = lua_tostring(lua, -1);
        lua_pop(lua, 1);

        // stack: key, table
        std::cout &lt;&lt; key  &lt;&lt; " " &lt;&lt; value &lt;&lt; "\n";
    }

    lua_close(lua);
}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5922683121930377464?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5922683121930377464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/05/lua-api-panic-unprotected-error-in-call.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5922683121930377464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5922683121930377464'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/05/lua-api-panic-unprotected-error-in-call.html' title='Lua API. PANIC: unprotected error in call to Lua API (invalid key to &apos;next&apos;)'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8582840637941195018</id><published>2010-04-20T00:00:00.003-07:00</published><updated>2010-04-20T00:00:00.792-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Wykłady'/><title type='text'>"Programowanie to jest umiejętność, którą trzeba wyćwiczyć"</title><content type='html'>&lt;span style="font-weight:bold;"&gt;"Programowanie to jest umiejętność, którą trzeba wyćwiczyć". Te słowa padają z ust Marcina Brodziaka - w swoim krótkim wykładzie (ok 1.5h) opowiada on, jak stać się dobrym programistą...&lt;/span&gt;

Uważam, że ten wykład jest bardzo dobry i dlatego warto go posłuchać. Marcin opowiada o tym, jakie cechy powinien posiadać dobry programista. Jedno jest pewne - z takim programistą, jakiego opisuje Marcin, z pewnością chciałbym współpracować! Przypuszczam, że każdy z nas by chciał.
&lt;center&gt;
&lt;span style="font-weight:bold;"&gt;Nagranie z wykładu&lt;/span&gt;
&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/zYn_lP8vu3g&amp;hl=pl_PL&amp;fs=1&amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/zYn_lP8vu3g&amp;hl=pl_PL&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/center&gt;

Wykład odbył się niedawno (kilka tygodni temu) w Instytucie Informatyki Uniwersytetu Wrocławskiego, a my możemy cofnąć się w czasie i go obejrzeć na portalu youtube.
&lt;a href="http://www.youtube.com/watch?v=zYn_lP8vu3g"&gt;Wszystkie części filmu na youtube&lt;/a&gt;

&lt;span style="font-weight:bold;"&gt;Skrót&lt;/span&gt;
Marcin pokazuje, że dobry programista ma zawsze zespół pewnych cech i umiejętności. Daje rady co robić, aby je posiąść. Są to kolejno:
&lt;ol&gt;
&lt;li&gt;Pisz kod&lt;/li&gt;
&lt;li&gt;Pisz testy&lt;/li&gt;
&lt;li&gt;Naucz się angielskiego&lt;/li&gt;
&lt;li&gt;Naucz się Lispu, Haskella, Prologa, C, Javy, JavaScriptu,...&lt;/li&gt;
&lt;li&gt;Pisz bloga&lt;/li&gt;
&lt;li&gt;Czytaj kod innych programistów&lt;/li&gt;
&lt;li&gt;Pracuj nad koncentracją i pamięcią krótkotrwałą&lt;/li&gt;
&lt;li&gt;Idź na piwo&lt;/li&gt;
&lt;li&gt;Napisz kompilator&lt;/li&gt;
&lt;/ol&gt;
Nie wiem jaka jest istotność poszczególnych elementów wg Marcina. Moim zdaniem trójka najważniejszych elementów to:
&lt;ol&gt;
&lt;li&gt;Pisz kod&lt;/li&gt;
&lt;li&gt;Czytaj kod innych programistów&lt;/li&gt;
&lt;li&gt;Naucz się Lispu, Haskella, Prologa, C, Javy, JavaScriptu,...&lt;/li&gt;
&lt;/ol&gt;
Jeżeli mógłbym dodać coś od siebie to dopisałbym jeszcze punkt "opanuj swoje narzędzia na poziomie mistrzowskim" - przede wszystkim edytor tekstu, gdyż to jeden z elementów, który nie zależy od używanego języka.

&lt;span style="font-weight:bold;"&gt;Co dalej?&lt;/span&gt;
Na samym początku Marcin podaje kilka blogów, artykułów i jedną książkę, które warto czytać. Oto linki

&lt;a href="http://www.catb.org/~esr/faqs/hacker-howto.html"&gt;Hacker howto - Eric Steven Raymond&lt;/a&gt;
&lt;a href="http://steve-yegge.blogspot.com/2008/03/get-that-job-at-google.html"&gt;Steve Yagge&lt;/a&gt;
&lt;a href="http://sites.google.com/site/steveyegge2/practicing-programming"&gt;Practicing programming&lt;/a&gt;
&lt;a href="http://www.paulgraham.com/avg.html"&gt;Paul Graham - polecam wszystkie artykuły&lt;/a&gt;
&lt;a href="http://www.joelonsoftware.com/"&gt;Joel - to IMO bardzo ważne artykuły&lt;/a&gt;
&lt;a href="http://norvig.com/21-days.html"&gt;21-days&lt;/a&gt;

&lt;span style="font-weight:bold;"&gt;"Coders at work", Peter Seibel&lt;/span&gt;
Niestety nie miałem jeszcze przyjemności czytać, jednak ta pozycja od pewnego czasu (raczej niedługiego) gości na mojej liście książek do przeczytania ;-) Jeżeli ją czytałeś/czytałaś to chętnie poznam Twoją opinię o niej.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8582840637941195018?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8582840637941195018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/04/programowanie-to-jest-umiejetnosc-ktora.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8582840637941195018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8582840637941195018'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/04/programowanie-to-jest-umiejetnosc-ktora.html' title='&quot;Programowanie to jest umiejętność, którą trzeba wyćwiczyć&quot;'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-4082604586760414656</id><published>2010-04-18T09:00:00.000-07:00</published><updated>2010-04-18T09:00:00.486-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Wskaźniki na pola i metody</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jednym z mniej znanych elementów języka C++ są wskaźniki na pola i metody. Na pierwszy rzut oka wydaje się, że te konstrukcje są nieprzydatne. Jednak okazuje się, że bez nich programowanie w C++ byłoby trudniejsze. Wiem, że w najbliższych dniach paru osobom to może się przydać, więc postanowiłem napisać kilka słów od siebie ;-)&lt;/span&gt;

&lt;span style="font-weight:bold;"&gt;Po co mi to?&lt;/span&gt;
Nie ma wątpliwości co do małej przydatności tych konstrukcji. Znacznie częściej używa się wskaźnika na zwykłą zmienną niż wskaźnika na pole. Podobnie jest z funkcjami i metodami. Jednak przychodzi taki moment, że potrzebujemy przechowywać metody w zmiennych - np. żeby wywołać je później. Tu przydatne są biblioteki boost::function, boost::bind, boost::lambda. Możemy również wprowadzić swoją implementację delegatów. W obu przypadkach w języku musi występować taka konstrukcja jak wskaźnik na metodę. Inne zastosowanie wskaźników na pola i metody to sprawdzenie czy typ jest klasą (class, struct, union). Płynie stąd prosty wniosek. Programista systemowy (ten, który tworzy biblioteki) prędzej czy później musi zetknąć się z pojęciem wskaźnika na metodę i pole. Jednocześnie podczas pisania aplikacji te konstrukcje praktycznie nie mają zastosowania (a może mają...).


&lt;span style="font-weight:bold;"&gt;Składnia wskaźników na pola i metody nie zachwyca
&lt;/span&gt;Jasne... składnia to tylko składnia. Jeżeli dwa języki różnią się tylko szczegółami składniowymi (po parsowaniu dostajemy takie samo drzewo, które wykonywane jest tak samo) to są tym samym językiem... takie gadanie teoretyczne. W praktyce nawet sam cukier syntaktyczny może być czynnikiem decydującym o wygodzie korzystania z języka (czyli także o jego popularności). Wystarczy popatrzeć na składnię wyrażeń lambda w C++ i w OCamlu/Haskellu/Lispie. W C++ wyrażeń lambda używa się zdecydowanie rzadziej.

Myślę, że dziwna składnia wskaźników na metody i pola jest głównym czynnikiem, przez który wiele osób (większość programistów C++?) uważa, że to błąd składniowy ;-) Zobaczmy jak to wygląda. Aby zdefiniować wskaźnik na pole x w klasie A piszemy&lt;pre&gt;
int A::* px;
&lt;/pre&gt;Czyli dokładnie tak jak w przypadku wskaźnika na zmienną typu int, jednak przed * dodajemy A::. Jeżeli chcielibyśmy przypisać wartość temu wskaźnikowi (np. pole x z klasy A) to robimy to następująco:&lt;pre&gt;
px = &amp;A::x;
&lt;/pre&gt;Aby teraz odwołać się do tego pola potrzebujemy obiektu. Tu mamy dwie składnie, zależnie od tego czy dysponujemy obiektem czy wskaźnikiem na obiekt. Jeżeli o jest obiektem typu A, a po wskaźnikiem A* to odpowiednie pole uzyskamy pisząc:&lt;pre&gt;
o.*px;
po-&gt;*px;
&lt;/pre&gt;Czyli tak samo jak odwołując się do zmiennej, przy czym dodajemy gwiazdkę przed nazwą wskaźnika.

Spójrzmy jeszcze na metody. Tutaj jest podobnie. Wskaźnik na metodę klasy A, która ma dwa argumenty int i float oraz zwaraca int wygląda następująco:&lt;pre&gt;
int (A::*pmfoo)(int, float)
&lt;/pre&gt;Jest to zapis bardzo podobny do wskaźnika na odpowiednią funkcję. Tak samo jak poprzednio - dodajemy A:: przed gwiazdką. 

Jest kilka istotnych różnicc między wskaźnikiem na funkcję i wskaźnikiem na metodę. Mając funkcję int foo(int, float) możemy przypisać ją do wskaźnika na dwa sposoby:&lt;pre&gt;
int (*pfoo)(int, float) = foo;
int (*pfoo)(int, float) = &amp;foo;
&lt;/pre&gt;W przypadku wskaźnika do metody &lt;span style="font-weight:bold;"&gt;tylko jeden sposób&lt;/span&gt; jest poprawny&lt;pre&gt;
int (A::*pmfoo)(int, float) = &amp;A::foo;
&lt;/pre&gt;Aby wywołać taką metodę piszemy następująco:&lt;pre&gt;
A a;
(a.*pmfoo)(2,3); // lub -&amp;gt;* gdy a jest wskaźnikiem
&lt;/pre&gt;Niestety nawiasy &lt;span style="font-weight:bold;"&gt;nie są&lt;/span&gt; opcjonalne. W przypadku wskaźników na funkcje oczywiście nie ma potrzeby używania dodatkowej pary nawiasów.

&lt;span style="font-weight:bold;"&gt;Zastosowanie wskaźników na pola 1&lt;/span&gt;
Niewątpliwie najczęstsze zastosowanie wskaźników na pola to straszenie nowych programistów jaki to C++ jest zły i trudny ;-D Niektórzy zdają się prawdziwie cieszyć z widoku wystraszonego "nooba". 

&lt;span style="font-weight:bold;"&gt;Zastosowanie wskaźników na pola 2&lt;/span&gt;
Rozważmy następująca funkcję:&lt;pre&gt;
int sum(const std::vector&amp;lt;A*gt;&amp; as, int A::* field) {
    int result = 0;
    for (size_t i = 0; i &amp;lt; as.size(); ++i) {
        result += as.at(i).*field;
    }
    return result;
}
&lt;/pre&gt;Dzięki niej jeżeli mamy wektor obiektów, które posiadają dwie wartości (np. wiek i cena) - możemy policzyć sumę wieku i sumę ceny przy użyciu tej samej funkcji. Zatem jest ona ogólniejsza. Zastosowanie trochę na siłę, ale... 

&lt;span style="font-weight:bold;"&gt;Zastosowanie wskaźników na metody
&lt;/span&gt;W przypadku wskaźników na metody, łatwo jest wymyślić zastosowanie. W przypadku systemu sterowanego zdarzeniami możemy np. zapamiętać jaką metodę chcemy wywołać za pół godziny ;-) Rozdzielenie momentu zdefiniowania jak wywołać metodę od momentu samego wywołania jest bardzo przydatnym mechanizmem. 

Można również napisać funkcję, która będzie w stanie wywołać określoną metodę na wszystkich obiektach z kolekcji. Wówczas wystarczy kilkukrotnie ją wywołać z różnymi argumentami, aby wykonać szereg operacji na kolekcji. Takie zbieranie wywołań w jedno miejsce jest bardzo przydatne. Można w ten sposób np. łatwo dodać kod zbierający statystyki jak długo wykonują się różne operacje.

&lt;span style="font-weight:bold;"&gt;Wskaźniki na pola i metody jako element specyficzny dla klas
&lt;/span&gt;Okazuje się, że klasy (class, struct, union) są jedynymi typami, które mogą mieć wskaźnik na pole. Pozwala to wykorzystać ten mechanizm do sprawdzenia czy dany typ jest klasą. Jest to bardzo przydatne podczas programowania generycznego. Wiedząc czy dany typ jest klasą czy nie, możemy określić czy należy go przekazać przez referencję do stałej czy przez wartość. Samo sprawdzenie może wyglądać np. tak:&lt;pre&gt;
template &amp;lt;typename T&amp;gt;
struct is_class {
    typedef char yes;
    typedef yes (&amp;no)[2];
    
    template &amp;lt;typename TT&amp;gt; static yes test(int TT::*);
    template &amp;lt;typename TT&amp;gt; static no test(...);
    
    static const bool value = sizeof(test&amp;lt;T&amp;gt;(0)) == sizeof(yes);
};
&lt;/pre&gt;Wykorzystujemy tu zasadę SFINAE - dla typów, które nie są klasami metoda test&amp;lt;T&amp;gt;(int TT:*) nie będzie poprawnym dopasowaniem w wywołaniu sizeof(test&lt;T&gt;(0)) i zostanie wybrane bardziej ogólne dopasowanie test(...) (elipsa jest najogólniejszym dopasowaniem).

&lt;span style="font-weight:bold;"&gt;Pamięć&lt;/span&gt;
Tu jest kolejna ciekawa różnica między wskaźnikami na funkcje, a wskaźnikami na metody. Spośród wskaźników na zmienne, pola, metody i funkcje wszystkie wskaźniki z wyjątkiem tych na metody mają wielkość 4B. W przypadku wskaźników na metody jest to 8B. 

&lt;span style="font-weight:bold;"&gt;Podsumowanie&lt;/span&gt;
Myślę, że dla każdego programisty systemowego umiejętność posługiwania się wskaźnikami na metody i pola powinna być jedną z podstawowych. Jednocześnie można sobie wyobrazić programistów aplikacji, którzy przeżyją bez tej wiedzy ;-) Składnia wskaźników na pola i metody nie należy do najpiękniejszych, ale przy odrobinie chęci można ją zapamiętać.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4082604586760414656?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4082604586760414656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/04/wskazniki-na-pola-i-metody.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4082604586760414656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4082604586760414656'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/04/wskazniki-na-pola-i-metody.html' title='Wskaźniki na pola i metody'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8719881737042337060</id><published>2010-01-17T00:00:00.002-08:00</published><updated>2010-01-17T00:00:04.193-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><category scheme='http://www.blogger.com/atom/ns#' term='Wykłady'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Wzorce projektowe - krótka prezentacja</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Prezentacje i wykłady to część zajęcia, któremu ostatnio poświęcam trochę czasu, jakim jest studiowanie ;-) W czwartek przedstawiłem moją, krótką prezentację na temat wzorców projektowych. Było to na spotkaniu koła studentów informatyki (pozdrawiam!).
&lt;/span&gt;

Ze względu na olbrzymie rozmiary zagadnienia opowiedziałem o kilku, moim zdaniem ważnych, idiomach i wzorcach: PIMPL, RAII, Template method, Visitor. 

Opowiadałem ok. 1h. Mogliśmy zobaczyć jak często tracimy czas i jak go odzyskać

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_P9Sn6SUv534/S1IRBRrJlHI/AAAAAAAAADk/FxwuKDhFG2w/s1600-h/ucieka_czas.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_P9Sn6SUv534/S1IRBRrJlHI/AAAAAAAAADk/FxwuKDhFG2w/s320/ucieka_czas.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5427419214556796018" /&gt;&lt;/a&gt;

Oraz o tym, że często jesteśmy skrępowani przez zły design

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_P9Sn6SUv534/S1IS7UcmPgI/AAAAAAAAAEE/BmoYQtiukNo/s1600-h/zwiazany.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 283px; height: 213px;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/S1IS7UcmPgI/AAAAAAAAAEE/BmoYQtiukNo/s320/zwiazany.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5427421311245106690" /&gt;&lt;/a&gt;

i że można sobie z tym poradzić

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_P9Sn6SUv534/S1ITljbzRqI/AAAAAAAAAEM/wGqA5JOatX8/s1600-h/pimpl_zniesienie_zaleznosci.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/S1ITljbzRqI/AAAAAAAAAEM/wGqA5JOatX8/s320/pimpl_zniesienie_zaleznosci.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5427422036822804130" /&gt;&lt;/a&gt;

Pojawiło się kilka bardzo ciekawych pytań i odpowiedzi (dzięki Karol za Twoje wyjaśnienia przy template method :-))

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_P9Sn6SUv534/S1IRvX6z12I/AAAAAAAAAD0/A75hc2sbbf0/s1600-h/pytania.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 228px; height: 320px;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/S1IRvX6z12I/AAAAAAAAAD0/A75hc2sbbf0/s320/pytania.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5427420006507075426" /&gt;&lt;/a&gt;

Zauważyliśmy też, że języki obiektowe i funkcyjne mają ciekawe punkty wspólne...

A wszystko po to, abyśmy mogli przekonać się ile zyskujemy stosując wzorce 

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_P9Sn6SUv534/S1IRVv5W3gI/AAAAAAAAADs/Sk5MPwSdHv0/s1600-h/zysk.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/S1IRVv5W3gI/AAAAAAAAADs/Sk5MPwSdHv0/s320/zysk.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5427419566266834434" /&gt;&lt;/a&gt;

OK! Czas powoli kończyć ten wpis. Sama prezentacja i kody źródłowe nie zastąpią dyskusji, ale przecież można nie dostać wszystkiego i nadal doceniać to co się ma :-) Zapraszam do obejrzenia materiałów i wyrażenia swojej opinii w komentarzach... &lt;a href="https://sites.google.com/a/cs.uni.wroc.pl/ksi/main/ogloszenia/materialyzwykladuowzorcachpimplraiitemplatemethodorazvisitor"&gt;Link do materiałów na stronie KSI&lt;/a&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_P9Sn6SUv534/S1ISoz8-TMI/AAAAAAAAAD8/fV3Cgp5P0x0/s1600-h/koniec.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/S1ISoz8-TMI/AAAAAAAAAD8/fV3Cgp5P0x0/s320/koniec.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5427420993284885698" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8719881737042337060?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8719881737042337060/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2010/01/wzorce-projektowe-krotka-prezentacja.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8719881737042337060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8719881737042337060'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2010/01/wzorce-projektowe-krotka-prezentacja.html' title='Wzorce projektowe - krótka prezentacja'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://3.bp.blogspot.com/_P9Sn6SUv534/S1IRBRrJlHI/AAAAAAAAADk/FxwuKDhFG2w/s72-c/ucieka_czas.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6634194792959224202</id><published>2009-11-11T00:00:00.003-08:00</published><updated>2009-11-11T00:00:00.144-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Dropbox w KDE</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Dropbox łatwo integruje się ze środowiskiem Gnome. Niestety nie znalazłem jeszcze paczki, która pozwoliłaby używać dropboksa pod KDE tak jak w przypadku Gnome. Na szczęście nie jest tak źle jak mogłoby się wydawać ;-) Oto krótka instrukcja jak poradzić sobie z instalacją tego wspaniałego narzędzia&lt;/span&gt;

Dla tych, którzy nie wiedzą jeszcze czym jest Dropbox wrzucam &lt;span style="font-weight:bold;"&gt;krótki filmik&lt;/span&gt;:

&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Wozx4lBDVe0&amp;hl=pl&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Wozx4lBDVe0&amp;hl=pl&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;

Na początek pobieramy potrzebną paczkę ze strony &lt;a href="https://www.dropbox.com/"&gt;dropboksa&lt;/a&gt;. Wersja &lt;a href="http://www.getdropbox.com/download?plat=lnx.x86"&gt;32bit&lt;/a&gt; lub &lt;a href="http://www.getdropbox.com/download?plat=lnx.x86_64"&gt;64bit&lt;/a&gt;.Dalej jest już z górki ;-)

Rozpakowujemy paczkę w swoim katalogu domowym:&lt;pre&gt; $ tar -zxvf dropbox-lnx.x86-0.6.571.tar.gz&lt;/pre&gt;i wrzucamy dropbox do autostartu&lt;pre&gt; $ ln -sf /home/lmm/.dropbox-dist/dropboxd /home/lmm/.kde/Autostart/&lt;/pre&gt;
Po &lt;span style="font-weight:bold;"&gt;ponownym zalogowania&lt;/span&gt; możesz już synchronizować swoje pliki z ich wersjami online.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6634194792959224202?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6634194792959224202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/11/dropbox-w-kde.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6634194792959224202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6634194792959224202'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/11/dropbox-w-kde.html' title='Dropbox w KDE'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2417009050402649695</id><published>2009-11-08T00:00:00.001-08:00</published><updated>2009-11-08T00:00:04.181-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>SVN na pendrive</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Okazało się, że poszło bardzo szybko i prosto... Od dłuższego czasu odkładałem "na później" założenie repozytorium tak aby było na pendrive'ie. Myślałem, że zajmie to sporo czasu, a tu taka niespodzianka&lt;/span&gt;

Zapewne nie trzeba nikogo przekonywać o wartości wersjonowania swoich projektów. Podobnie tworzenie taga co jakiś czas jest dobrą praktyką, którą znają chyba wszyscy. Być może jest jeszcze ktoś, kto tak jak ja odkłada utworzenie lokalnego repozytorium tylko dlatego, że nie wie jakie to proste ;-)

Wystarczy utworzyć katalog na urządzeniu przenośnym (zakładam, że pendrive nazywa się "Pendrive"):&lt;pre&gt; $ mkdir /media/Pendrive/svn&lt;/pre&gt;I utworzyć tam repozytorium&lt;pre&gt; $ svnadmin create /media/Pendrive/svn&lt;/pre&gt;Używamy takiego repo jak każdego innego. Można zacząć np. tak:&lt;pre&gt; $ svn mkdir file:///media/Pendrive/svn/NowyProjekt
 $ svn co file:///media/Pendrive/svn/NowyProjekt .
 $ mkdir trunk tags branches
 $ svn add *
 $ svn ci -m "Initial revision"&lt;/pre&gt;Można oczywiście kwestionować przydatność takiego repo - niestety czasami brakuje serwera czy nawet internetu, a gdy projekt jest jednoosobowy to można skorzystać przynajmniej z backupu, możliwości cofania rewizji, automatycznego changeloga i łatwego zarządzania tagami.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2417009050402649695?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2417009050402649695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/11/svn-na-pendrive.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2417009050402649695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2417009050402649695'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/11/svn-na-pendrive.html' title='SVN na pendrive'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6927666420693523391</id><published>2009-06-30T00:00:00.003-07:00</published><updated>2009-06-30T00:00:39.276-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><title type='text'>JAVA - ROTFL</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jedne języki są bardziej intuicyjne, inne mniej. Przykładem takiego nieintuicyjnego zachowania jest porównywanie operatorem == C-stringów. Ten przykład znają wszyscy, a na forach raz w miesiącu jest o tym wątek ;-)&lt;/span&gt;

 Przyszło mi do głowy, żeby napisać taki Javovy "rozbrajacz". Oto co wykombinowałem:&lt;pre&gt; Integer a = 1000;
 Integer b = 1000;
 System.out.println("a == b (1000 == 1000): " + (a == b));

 Integer aa = 100;
 Integer bb = 100;
 System.out.println("aa == bb (100 == 100): " + (aa == bb));&lt;/pre&gt;Zapytasz: "i co?". Odpowiadam: Z dużym prawdopodobieństwem (optymalizacje wg VM mogą wszystko zepsuć :-/) pierwszy test zwróci false, a drugi true (to akurat jest pewne).

O co chodzi pozostawiam do własnej analizy (raczej proste). 

Hint 1: Używamy Integer, a nie int (to dość istotne).

Hint 2: Czym różni się operator == od metody equals?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6927666420693523391?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6927666420693523391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/java-rotfl.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6927666420693523391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6927666420693523391'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/java-rotfl.html' title='JAVA - ROTFL'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1129834359697335869</id><published>2009-06-08T00:00:00.000-07:00</published><updated>2009-06-08T00:00:01.354-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><title type='text'>Jak pisać elastyczny soft "by example"</title><content type='html'>&lt;span style="font-weight:bold;"&gt;W internecie można znaleźć wiele ciekawych porad na temat tego jak programować, aby "było dobrze" i w "słuszny" sposób. Większość to bełkot, ale znajdą się też perełki. Moim zdaniem do takich należy ten film...&lt;/span&gt;

&lt;a href="http://www.vimeo.com/1013263"&gt;http://www.vimeo.com/1013263&lt;/a&gt;

Jest na przykładzie GNU/Emacsa - konkretnie modułu ido-mode. Sam moduł nie jest tu najważniejszy, ale pewna "dobra praktyka", którą autor próbuje przekazać - IMO skutecznie. Nie należy spodziewać się czegoś bardzo odkrywczego. Prosta prawda, którą łatwo odkryć, a prawie nikt jej nie stosuje (przynajmniej patrząc na ograniczenia współczesnych aplikacji).

ido-mode - początkowo ciężko się przyzwyczaić (godzina/dwie). Po miesiącu nie wyobrażałem sobie wyłączenia go (i nadal tak jest). Eksperymenty z nowymi narzędziami to podstawa... . A filmik znalazłem szukając w necie ciekawych zastosowań ido.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1129834359697335869?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1129834359697335869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/jak-pisac-elastyczny-soft-by-example.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1129834359697335869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1129834359697335869'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/jak-pisac-elastyczny-soft-by-example.html' title='Jak pisać elastyczny soft &quot;by example&quot;'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8750170454799033857</id><published>2009-06-01T00:00:00.001-07:00</published><updated>2009-06-01T00:00:02.343-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><title type='text'>A czy Ty masz moc super krowy?</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Instalacja programów przez konsolowego zarządcę pakietów ma wiele zalet - jedną z nich jest to, że można się czasami pośmiać. Zawsze podobał mi się napis "Ten aptitude nie posiada Mocy Super Krowy" gdy omyłkowo wpisałem błędny parametr do aptitude. Ale dlaczego akurat taki tekst? &lt;/span&gt;

Aby się dowiedzieć trzeba wrócić do źródeł. Aptitude to program, który jest następnikiem apt-get. Gdy przeczytamy dokumentację apt-get (apt-get --help) zobaczymy "Ten APT ma moce Super Krowy". Ładny easter egg pojawia się gdy wpiszemy apt-get moo. Widać wtedy ASCII-krowę ;-) Poniżej niej jest napis "Have you mooed today?".

Ta sztuczka działa też z aptitude - tyle, że jest wtedy informacja, że w aptitude nie ma easter eggs.

Jak się właśnie przekonałem - nie warto wierzyć twórcom aptitude. W aptitude jest easter egg ;-D Jaki? Znalezienie go pozostawiam jako proste ćwiczenie (przez google się nie liczy ;-P)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8750170454799033857?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8750170454799033857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/czy-ty-masz-moc-super-krowy.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8750170454799033857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8750170454799033857'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/06/czy-ty-masz-moc-super-krowy.html' title='A czy Ty masz moc super krowy?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1276096307825254772</id><published>2009-05-29T00:00:00.005-07:00</published><updated>2009-05-29T00:00:00.904-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Rekursywne przeglądanie katalogów</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Konieczność przeglądania katalogów w C++ zdarza się raczej rzadko. Zazwyczaj programy, które muszą robić coś tego typu są skryptami ułatwiającymi pracę, czy tworzącymi masowe zmiany w plikach. Sam wolę w takim wypadku użyć jakiegoś języka skryptowego - np. Perla. Tym razem okazało się, że potrzebuję rekursywnie przeszukać katalog w C++ i...&lt;/span&gt;

Ku mojemu zdziwieniu okazało się to dziecinnie proste. Od razu planowałem użyć boost::filesystem, aby uniknąć problemów z różnicami w systemach plików (zapewnić sobie przynajmniej minimalną portowalność rozwiązania). Byłem przekonany, że bez rekursji się nie obejdzie. Nic bardziej mylnego. Biblioteka boost::filesystem wykonała całą pracę za mnie! Jest w niej zdefiniowany iterator, który przegląda katalog rekursywnie. Interfejs nie odbiega od iteratorów, które są obecne w STL (np. std::istream_iterator).

Dość gadania. Aby przeszukać katalog tmp w poszukiwaniu plików wystarczy napisać:&lt;pre&gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;

#include &amp;lt;boost/filesystem.hpp&amp;gt;

int main() {
  namespace fs = boost::filesystem;
  fs::path path("/tmp");
  fs::recursive_directory_iterator it(path);
  fs::recursive_directory_iterator end;
  
  for ( ; it != end; ++it) {
    if (fs::is_regular(it-&amp;gt;status()))
      std::cout &lt;&lt; it-&gt;path() &lt;&lt; "\n";
  }
  
}&lt;/pre&gt;Jak widać nic prostszego. Używamy dwóch konstruktorów recursive_directory_iterator. Pierwszy przyjmuje jako argument ścieżkę - tam zaczniemy szukać. Drugi nie przyjmuje argumentów i oznacza element "zaostatni" - zgodnie z konwencją STLa. Potem prosta pętelka wypisująca nazwy plików.

Wynik jest równoważny wykonaniu polecenia&lt;pre&gt;find /tmp -type f&lt;/pre&gt;Nie rozumiem tylko, dlaczego w dokumentacji jest przykład, który pokazuje jak to zrobić używając rekurencji jawnie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1276096307825254772?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1276096307825254772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/rekursywne-przegladanie-katalogow.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1276096307825254772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1276096307825254772'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/rekursywne-przegladanie-katalogow.html' title='Rekursywne przeglądanie katalogów'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2261869783779021714</id><published>2009-05-20T00:00:00.003-07:00</published><updated>2009-05-20T00:00:00.658-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Wygodne wyszukiwanie</title><content type='html'>&lt;span style="font-weight:bold;"&gt;W przypadku wielu edytorów używa się opcji szukania gdy chce się znaleźć jakiś fragment. W GNU/Emacsie jest jeszcze jedno zastosowanie - poruszanie się po tekście. Często o wiele szybciej jest użyć opcji szukaj niż przejechać kursorem w odpowiednie miejsce (o przesuwaniu ręki na gryzonia, machaniu nim i kilkaniu w odpowiednim miejscu już nie wspominając). Od dłuższego czasu mocno wykorzustuję isearch, ale ostatnio przeczytałem pomoc do isearch i poznałem parę nowych sztuczek. Te nowe i większość starszych zastosowań postanowiłem opisać.
&lt;/span&gt;
&lt;span style="font-weight:bold;"&gt;Incremental search&lt;/span&gt;
isearch zawsze wydawało mi się potężnym narzędziem - tak naprawdę jest to tylko mechanizm wyszukiwania. Jest podobny do tego we współczesnych przeglądarkach, to znaczy że tekst jest wyszukiwany już w czasie pisania zapytania (kawałek, który się da znaleźć). W GNU/Emacsie można szukać w przód (C-s) lub w tył (C-r). Aby przejść do drugiego dopasowania wystarczy powtórzyć C-s lub C-r, przy czym w trakcie wyszukiwania w przód można uzyć C-r aby wyszukać w tył i odwrotnie. 

&lt;span style="font-weight:bold;"&gt;Podstawy isearch&lt;/span&gt;
Napisałem już jak zacząć wyszukiwać, ale co dalej? Gdy już kursor znajdzie się w odpowiednim miejscu wystarczy nacisnąć enter (RET) aby zakończyć wyszukiwanie. Jeżeli natomiast wyniki nas nie satysfakcjonują (lub chcieliśmy tylko sprawdzić czy fraza występuje) to używamy C-g (jak zawsze do kasowania aktualnej akcji) aby powrócić do miejsca, z którego zaczęliśmy.

&lt;span style="font-weight:bold;"&gt;Modyfikacja wyszukiwanej frazy&lt;/span&gt;
Jedną z bardziej przydatnych opcji jest * znana z vim-a. Załóżmy, że stoimy kursorem przed pewnym słowem i chcemy poszukać tego słowa w tekście. Oczywiście nie musimy go kopiować (ani przepisywać). Wystarczy C-s (włącz isearch), a następnie C-w (dodaj następne słowo do wyszukiwania). Użycie C-w kilka razy spowoduje dodawanie kolejnych słów. Podbnie jest z C-y, tyle, że tu dodajemy całą linię od miejsca, w którym jest kursor. W tej grupie jest jeszcze M-y, które dodaje do isearch ostatnio usunięty (wycięty) tekst, ale to raczej mało przydatne. Najlepsze jest to, że w każdej chwili możemy wyedytować ręcznie wyszukiwany tekst, bo M-e przenosi nas do edycji szukanej frazy.

&lt;span style="font-weight:bold;"&gt;Dopasowanie niestandardowych znaków&lt;/span&gt;
Bardzo pomocne jest dopasowanie końca linii przy pomocy C-j. Nieco rzadziej potrzebujemy wyszukać np. ^M (to nie są dwa znaki, tylko jeden - zapisany w ten sposób. GNU/Emacs pokoloruje taką sekwencję, aby ją odróżnić od tekstu). Aby to uczynić można wykorzystać C-q, które robi "quote" na następnym znaku - w naszym przypadku potrzebujemy C-qC-m

&lt;span style="font-weight:bold;"&gt;Historia&lt;/span&gt;
Do "wspomagaczy" można także zaliczyć historię z autouzupełnianiem. Możemy iść w tył M-p, w przód M-n i dopełniać TABem. Jeżeli z jakiegoś powodu przerwiemy wyszukiwanie to możemy je wznowić. Wystarczy C-s - wtedy wyszukujemy pustego ciągu, powtórzenie C-s spowoduje wyszukanie ostatnio wyszukanego ciągu (czyli zatwierdzonego, usunięte przez C-g wyszukiwania nie są brane pod uwagę).

&lt;span style="font-weight:bold;"&gt;Wielkość liter&lt;/span&gt;
Domyślne zachowanie GNU/Emacsa jest bardzo ciekawe. Jeżeli wpisujemy tylko małe litery to wyszukiwanie jest BEZ rozróżniania wielkości liter. W przypadku napisania przynajmniej jednej wielkiej litery przechodzimy w tryb, w którym wielkość liter ma znaczenie. Aby w trakcie wyszukiwania przełączyć znaczenie wielkości liter można użyć M-c.

&lt;span style="font-weight:bold;"&gt;Wyrażenia regularne&lt;/span&gt;
Nic szczególnego, ale warto o tym wspomnieć. M-r przełącza między trybem zwykłego wyszukiwania i wyszukiwania przy pomocy wyrażeń regularnych (który można też włączyć od razu C-M-s lub C-M-r). Czasami się przydaje, ale zazwyczaj wiemy wcześniej czy będzie potrzebne wyrażenie regularne czy nie.  

&lt;span style="font-weight:bold;"&gt;Podmiana&lt;/span&gt;
Do opcji "znajdź" często w pakiecie jest też "zamień" (jeżeli nie ma tego w Twoim edytorze, to go szybko zmień). W GNU/Emacsie przełączenie się na "zamień" jest proste - M-% lub C-M-%, jeżeli potrzebujemy wyrażeń regularnych.

&lt;span style="font-weight:bold;"&gt;Wszechobecna pomoc&lt;/span&gt;
Oczywiście nie musisz tego wszystkiego pamiętać. Warto jest wykorzystywać tylko te opcje, które są potrzebne. Gdy je opanujesz możesz dodać następną do swojego "portfela". W bardzo przystępnej (i krótkiej) formie opcje isearch są opisane w systemie pomocy GNU/Emacsa C-hkC-s - polecam, bo nie opisałem tu wszystkich.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2261869783779021714?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2261869783779021714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/wygodne-wyszukiwanie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2261869783779021714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2261869783779021714'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/wygodne-wyszukiwanie.html' title='Wygodne wyszukiwanie'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-379035647473948715</id><published>2009-05-14T00:00:00.002-07:00</published><updated>2009-05-14T00:00:01.032-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><title type='text'>Zabawy z translate.pl</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Czasem jest tak, że potrzebujemy dobrego translatora. Jak znaleźć odpowiedni? Właśnie o tym NIE będę pisał. Translatory mogą służyć do tłumaczenia tekstów, ale maję też inne zastosowanie - wprowadzają trochę uśmiechu w nasze życie.
&lt;/span&gt;

Niedawno na &lt;a href="http://www.bash.org.pl"&gt;www.bash.org.pl&lt;/a&gt; był taki cytat:&lt;pre&gt;
peszku: google tłumacz rządzi :D
zobo: co tym razem?
peszku: "babcia piekła ciasteczka" przetłumaczył na "grandmother hell cookies"
zobo: ;|&lt;/pre&gt;Ponoć ciekawe efekty można uzyskać prosząc o przetłumaczenie frazy na wybrany język, a następnie spowrotem na język wyjściowy. Zrobiłem coś takiego na &lt;a href="http://www.translate.pl/pl.php4"&gt;http://www.translate.pl/pl.php4&lt;/a&gt;. Tłumaczyłem z angielskiego na angielski poprzez polski. Odpowiedni skrypt wygląda tak (perl):&lt;pre&gt;
#!/usr/bin/perl

use warnings;
use strict;

use LWP::UserAgent;

my $phrase = shift || die "Please specify phrase as an argument";
my $ua = LWP::UserAgent-&gt;new();
my $url = "http://www.translate.pl/pl.php4";

# translate to pl
my $response = $ua-&gt;post($url, { d =&gt; "1", inbuf =&gt; "$phrase" } );
$response-&gt;content =~ m{&amp;lt;font class="translate"&amp;gt;(.*?)&amp;lt;/font&amp;gt;}ms;
my $translation_pl = $1;

# translate to en
$response = $ua-&gt;post($url, { d =&gt; "2", inbuf =&gt; $translation_pl } );
$response-&gt;content =~ m{&amp;lt;font class="translate"&amp;gt;(.*?)&amp;lt;/font&amp;gt;}ms;
my $translation_en = $1;

print "pl: $translation_pl\nen: $translation_en\n";
&lt;/pre&gt;Od strony kodu nic ciekawego, ale można się chwilę pobawić. Niestety dla bashowego cytatu dostajemy mało ciekawy wynik. Ciekawsze przypadki:&lt;pre&gt;
what's up  --&gt; That is

go         --&gt; They manage

I was born in California. 
Which part? 
All of me
  --&gt;
It was born &lt; give birth (be born) &gt; in california. 
Divides what? 
I all 

What's the use of having a train schedule if the trains are always late.
  --&gt;
That is use of existence of schedule of train ( train ) if trains are always late ( train ) ( last; late ). 

How would we know they were late, if we didn't have a schedule? 
  --&gt;
As there would be late learn last be (; late ), have did not we if? &lt;/pre&gt;Ciekawe jest, że trudno wybrać takie zdanie, które uda się przetłumaczyć aby było choć trochę "zabawnie". W większości przypadków translator robi coś naprawdę głupiego, czego nie da się sensownie przeczytać. Wyzwaniem jest znalezienie zdania (powyżej 3 słów), którego translator nie zniszczy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-379035647473948715?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/379035647473948715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/zabawy-z-translatepl.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/379035647473948715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/379035647473948715'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/zabawy-z-translatepl.html' title='Zabawy z translate.pl'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1269574711551129153</id><published>2009-05-05T00:00:00.001-07:00</published><updated>2009-05-05T00:00:00.659-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Braki w C++</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Jak każdy porządny język tak i C++ ma swoje braki. Jednym z nich jest brak typu, który pozwalałby przechowywać dowolną funkcję (albo chociaż wskaźnik do funkcji dowolnego typu). Nie jest to może bardzo uciążliwe, ale potrafi "wyjść" w odpowiednim momencie.&lt;/span&gt;

Większość pomyśli pewnie od razu o void*. Pewnie możnaby się kłócić na temat "elegancji" takiego rozwiązania gdyby... było ono poprawne. Okazuje się, że jest niezgodne ze standardem, a tylko przypadkiem (?) kompilator przepuszcza je jako poprawne odgrażając się jedynie przy użyciu ostrzeżeń.

Zgodnie ze standardem C++ na void* można rzutować obiekty (dane) - jest to zmiana w stosunku do C. Funkcja czy wskaźnik na funkcję oczywiście obiektem nie jest. 

Ostatnio coraz bardziej zastanawia mnie dlaczego standard C++ próbuje być kompatybilny z C, skoro jednocześnie wprowadza tyle różnic, że programy z C niekoniecznie muszą kompilować się w C++. Szkoda, że usunięto wskaźnik na dowolną funkcję, ale jednocześnie zostawiono niejawne konwersje między typami - IMO to drugie jest znacznie bardziej błędogenne.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1269574711551129153?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1269574711551129153/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/braki-w-c.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1269574711551129153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1269574711551129153'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/05/braki-w-c.html' title='Braki w C++'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-8912666876381226741</id><published>2009-04-29T00:00:00.000-07:00</published><updated>2009-04-29T00:00:00.510-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>goal column</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Ostatnio zauważyłem, że bardzo często używam set-goal-column. Z tego powodu postanowiłem podzielić się tym jak wykorzystać tę świetną funkcjonalność GNU/Emacsa.&lt;/span&gt;

C-xC-n to sekwencja, która pozwala ustawić tzw. goal-column na aktualną kolumnę. Od czasu włączenia tej funkcjonalności przesunięcie kursora w górę lub dół będzie skutkowało umieszczeniem go w kolumnie goal-column. C-uC-xC-n przywraca standardowe działanie.

Weźmy najprostszy przykład. Mamy wywołanie&lt;pre&gt;
foo(zmienna1,
   zmienna2,
   zmienna3,
   zmienna4);&lt;/pre&gt;i chcemy zmienić nazwy zmiennych dopisując do każdej 'm_' na początku, tak aby otrzymać:&lt;pre&gt;foo(m_zmienna1,
   m_zmienna2,
   m_zmienna3,
   m_zmienna4);&lt;/pre&gt;Można to zrobić na różne sposoby (najlepiej chyba makrem, bo nazwy kolejnych zmiennych niekoniecznie muszą być podobne, ale przyjmijmy ręczną edycję wszystkich linijek). Trzeba zwrócić uwagę, że po wpisaniu 'm_' przed nazwą 'zmienna1' kursor zmieni swoje położenie i po przeniesieniu go do następnej linii będzie po literze m (bo wpisaliśmy dwa znaki). Aby kontynuować edycję trzeba cofnąć się o dwa znaki i można dalej pisać.

Chodzi właśnie o to cofanie się, bo można go uniknąć. Wystarczy ustawić kursor przed zmienna1 i wydać polecenie C-xC-n.  Przy przechodzeniu do kolejnych linii kursor będzie się ustawiał w odpowiednim miejscu, a my zyskamy trochę czasu. Po zakończeniu należy pamiętać o wyłączeniu goal-column przez C-uC-xC-n.

Jasne, w tym przypadku i tak pewnie użylibyśmy makra, a strata dwóch znaków niespecjalnie ma znaczenie. Mogę powiedzieć tylko tyle, że goal-column znacznie ułatwia tworzenie makr - nie trzeba pamiętać o właściwym pozycjonowaniu kursora. Co do straty tylko dwóch znaków - trzeba zwrócić uwagę, że w tym przypadku jest to drugie tyle pracy do wykonania ;-) Jest tak prawie zawsze, bo trzeba się cofnąć o tyle znaków ile się napisało.

To wchodzi w nawyk szczególnie, że obie kombinacje bardzo szybko się wklepuje.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8912666876381226741?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8912666876381226741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/04/goal-column.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8912666876381226741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8912666876381226741'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/04/goal-column.html' title='goal column'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8603968835147974533</id><published>2009-04-26T00:00:00.000-07:00</published><updated>2009-04-26T00:00:00.882-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Stiff asks, great programmers answer</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Ostatnio ktoś wrzucił na forum studentów ii bardzo ciekawy link. Jest to adres do "wywiadu" ze świetnymi programistami. Co prawda wpis jest stary - widnieje przy nim data "Monday, 16 October 2006", ale moim zdaniem warto go przeczytać.
&lt;/span&gt;

&lt;a href="http://www.stifflog.com/2006/10/16/stiff-asks-great-programmers-answer?a"&gt;Stiff asks, great programmers answer&lt;/a&gt;

To, co jest szczególnie ciekawe to odpowiedź na pytanie o narzędzia - większość deklaruje wykorzystywanie GNU/Emacsa do pracy. Podbnie mają się sprawy z bashem, choć tu raczej chodzi ogólnie o shelle i proste skrypty - w końcu pod Windowsem też się da (nawet bez Cygwina) ale większość użytkowników jakoś o tym nie wie.

Do lektury :-)
&lt;a href="http://www.stifflog.com/2006/10/16/stiff-asks-great-programmers-answer?a"&gt;Stiff asks, great programmers answer&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8603968835147974533?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8603968835147974533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/04/stiff-asks-great-programmers-answer.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8603968835147974533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8603968835147974533'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/04/stiff-asks-great-programmers-answer.html' title='Stiff asks, great programmers answer'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4264187622941801609</id><published>2009-02-26T00:00:00.004-08:00</published><updated>2009-02-26T00:00:01.262-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>c-repl</title><content type='html'>&lt;span style="font-weight:bold;"&gt;REPL to skrót od read-eval-print loop. Działanie tego typu mechanizmu polega na wczytaniu komendy, wykonaniu jej, wypisaniu wyniku i przejściu na początek tej pętli - do czytania. Wiele języków programowania, szczególnie tych interpretowanych, jest dostarczanych wraz z tego typu mechanizmem. 
&lt;/span&gt;

REPL jest dostępny dla OCamla (toplevel), Haskella, Pythona, LUA, Matlaba, JavaScriptu itd... Jest nieoceniony w fazie nauki języka, ponieważ pozwala na bieżąco wykonywać kod i widzieć wyniki. Przydaje się też gdy chcemy sprawdzić działanie wyrażenia, które właśnie przyszło nam do głowy. Inną zaletą jest łatwość testowania napisanego kodu - nie trzeba pisać już testów aby sprawdzić czy dany kawałek kodu zadziała (chociaż nadal jest to zalecane, bo taki test można uruchomić więcej niż raz).

Byłem zaskoczony gdy w repozytorium zobaczyłem c-repl. Od razu zainstalowałem paczkę i... faktycznie. To jest REPL dla języka C. Mając w myślach jak bardzo przydatne będzie to narzędzie uruchomiłem c-repl i zacząłem pisać.

Niestety jest to bardzo ułomny program. Działa niezbyt szybko. W dodatku można wpisywać tylko po jednej linijce kodu co jest bardzo dużym ograniczeniem. Mimo wszystko sam fakt istnienia c-repl uświadamia, że nie wszystko jest oczywiste. Polecam chociaż spróbować c-repl.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4264187622941801609?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4264187622941801609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/c-repl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4264187622941801609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4264187622941801609'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/c-repl.html' title='c-repl'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5279827159197164874</id><published>2009-02-22T00:00:00.000-08:00</published><updated>2009-02-22T00:00:00.853-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><title type='text'>Współpraca xterm &lt;-&gt; alt</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Korzystam z xterma od kiedy pamiętam. Zawsze dręczył mnie fakt, że nie mogę używać klawisza alt jako meta-key w GNU/Emacs włączonym pod xtermem. Również podczas wydawania komend w xtermie mogę się poruszać tak jak w GNU/Emacs. Dla przykładu C-p pokazuje poprzednią linię (czyli w tym przypadku komendę), natomiast M-f przechodzi o jedno słowo w przód. Można robić "incremental search" na komendach C-r. To wszystko byłoby niesamowicie wygodne gdyby nie konieczność używania ESC jako klawisza META.&lt;/span&gt;

Chyba byłem zbyt leniwy, żeby poszukać rozwiązania. Jak już się za to zabrałem to wszystko okazało się dziecinnie proste. Wystarczy do konfiguracji xterma wrzucić linijkę:&lt;pre&gt;XTerm*metaSendsEscape: true&lt;/pre&gt;W rzeczywistości robimy to przez mechanizm zasobów. Powyższą linijkę należy umieścić w pliku ~/.Xresources (czasami ~/.Xdefaults - nie wiem od czego to zależy). Następnie wysatrczy załadować ten plik. Można to zrobić poleceniem&lt;pre&gt;xrdb ~/.Xresources&lt;/pre&gt;Zamiast xrdb można zresetować serwer X (ctrl-alt-backspace), bo zasoby są ładowane przy starcie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5279827159197164874?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5279827159197164874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/wspopraca-xterm-alt.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5279827159197164874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5279827159197164874'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/wspopraca-xterm-alt.html' title='Współpraca xterm &lt;-&gt; alt'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-549075226903753410</id><published>2009-02-02T00:00:00.001-08:00</published><updated>2009-02-02T00:00:00.576-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Ciężkie nadużycie</title><content type='html'>&lt;p&gt;Nie wiem co skłania ludzi do pisania beznadziejnego kodu. Może brak wiedzy? Akurat to nie jest, moim zdaniem, wytłumaczeniem. W końcu łatwo rozpoznać kod kiepskiej jakości. Tym bardziej, że w przypadku do którego zaraz dam link programista sam dał komentarz "/* evil macro magic */". &lt;/p&gt;&lt;p&gt;Zmiana znaczenia słów kluczowych zawsze jest złym pomysłem. Kod o którym myślę opisał &lt;a href="http://plonacazyrafa.blogspot.com/2009/02/evil-macro-magic.html"&gt;Tener&lt;/a&gt;.&lt;p&gt;A  poza tym to sesji ciąg dalszy ;-) 
 &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-549075226903753410?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/549075226903753410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/ciezkie-naduzycie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/549075226903753410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/549075226903753410'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/02/ciezkie-naduzycie.html' title='Ciężkie nadużycie'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-7024928215293748233</id><published>2009-01-11T00:00:00.003-08:00</published><updated>2009-01-11T00:00:02.824-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Ograniczamy zasoby - ulimit</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Pracując z komputerem można często stosować ograniczenia. Popularny przykład to praca na koncie, które nie jest uprzywilejowane, a tylko w przypadku prac administracyjnych zmiana na root lub wykorzystanie sudo. Podobnie programując często używamy słowa kluczowego const. Czy istnieje sensowny powód dla którego sami się ograniczamy?&lt;/span&gt;

Istnieje i jest bardzo znany - im większe ograniczenia na swoje konto założymy tym większa szansa, że w przypadku błędu komputer nie pozwoli nam spowodować zbyt wielkich szkód. Prosty przykład to&lt;pre&gt;# rm -r katalog1/ katalog2/ katalog3 /&lt;/pre&gt;Zazwyczaj pisząc szybko trudno zauważyć błąd w tym poleceniu (spacja po katalog3). Oczywiście w tym przypadku dość szybko żegnamy się z zawartością naszego dysku. Rozwiązaniem jest praca na koncie, które nie ma uprawnien aby wykonać to polecenie. Inny sposób to&lt;pre&gt;alias rm='rm -i'&lt;/pre&gt;co niestety jest czasami denerwujące. 

Skoro możemy zrzucić część kontroli na system to należy to zrobić. Popatrzmy na przykład kawałka programu w C:&lt;pre&gt; while(true) fork();&lt;/pre&gt;Ten program może powstać w wyniku błędu (sam podobny naskrobałem pisząc program tworzący w pętli 50 procesów potomnych). Jeżeli ktoś jeszcze tego nie zauważył, to ten program tworzy nowe procesy. Ich ilość rośnie wykładniczo! Jak wynika z praktyki jest to dosć dobry sposób na atak DoS.

Można się jednak zabezpieczyć. Wystarczy chwilę wcześniej skorzystać z polecenia ulimit, które jest składową basha. Aby zobaczyć krótki opis piszemy&lt;pre&gt;$ ulimit -a&lt;/pre&gt;Z opisu można wyczytać, że opcja -u odpowiada za liczbę procesów jakie możemy utworzyć. Szybkie &lt;pre&gt;$ ulimit -u 50&lt;/pre&gt;i wspomniany program jest juz niegroźny. Trzeba jednak pamiętać, że ulimit pozwala tylko zmniejszać ograniczenia na zasoby. Po ponownym uruchomieniu powłoki limity ustalają się na domyślne. Jeszcze tylko jedna uwaga - ustalanie limitu procesów na małą liczbę (np. 10) jest bardzo głupim pomysłem.

Krótki opis wszystkich zasobów, które można ograniczać znajduje się pod adresem &lt;a href="http://www.ss64.com/bash/ulimit.html"&gt;http://www.ss64.com/bash/ulimit.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7024928215293748233?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7024928215293748233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/01/ograniczamy-zasoby-ulimit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7024928215293748233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7024928215293748233'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/01/ograniczamy-zasoby-ulimit.html' title='Ograniczamy zasoby - ulimit'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4344626414770232188</id><published>2009-01-04T00:00:00.000-08:00</published><updated>2009-01-04T00:00:02.162-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Czy konsolowy menadżer okien może być przydatny?</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Pamiętam jak pierwszy raz przeczytałem o programie screen. Wydawał mi się wówczas bezużyteczny. Ostatnio mam przyjemność dość sporo pracować ze zdalnymi serwerami. Screen okazuje się być bezcenny. Pozawala na wygodną pracę z wieloma terminalami naraz oraz zostawianie uruchomionych programów po wylogowaniu.  &lt;/span&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_P9Sn6SUv534/SV99-_U_qYI/AAAAAAAAADY/TxPYjsi1Baw/s1600-h/konsola.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 222px;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/SV99-_U_qYI/AAAAAAAAADY/TxPYjsi1Baw/s320/konsola.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5287083008661105026" /&gt;&lt;/a&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_P9Sn6SUv534/SV99xr3sPAI/AAAAAAAAADQ/AZmTLx7Nrnw/s1600-h/screen.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 222px;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/SV99xr3sPAI/AAAAAAAAADQ/AZmTLx7Nrnw/s320/screen.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5287082780099623938" /&gt;&lt;/a&gt;

&lt;span style="font-weight:bold;"&gt;Tabela skrótów&lt;/span&gt;

Postanowiłem na początku umieścić tabelę przydatnych skrótów, aby była łatwo dostępna gdyby ktoś chciał z niej skorzystać w przyszłości. Oczywiście lista nie jest pełna. Jeżeli nie wiesz czym jest screen, możesz do tej tabeli wrócić po przeczytaniu reszty notki. Pełną listę skrótów można znaleźć w manualu (man screen).

Jak czytać skróty? Tak jak w GNU/Emacs. Dla przykładu C-a c oznacza, że musisz nacisnąć control i trzymając go literę a. Następnie puszczasz oba klawisze i przyciskasz c. Z kolei skrót C-a C-c oznacza naciśnij control i trzymając go naciśnij a, puść literę a i naciśnij c (nadal trzymając control).

Poniższe skróty powinny być poprzedzone kombinacją C-a. Innymi słowy skrót dla stworzenia okna to C-a C-c.&lt;pre&gt;C-c --&gt; Stwórz nowe okno (z powłoką)
C-n --&gt; Przejdź do następnego okna
C-p --&gt; Przejdź do poprzedniego okna
"   --&gt; Pokaż interaktywną listę okien
0-9 --&gt; Przejdź do okna o danym numerze
C-a --&gt; Pokaż poprzednio wyświetlane okno
A   --&gt; Nazwij okno
C-d --&gt; Odłącz sesję
D D --&gt; Odłącz sesję i wyloguj się
Q   --&gt; Zabij wszystkie regiony poza aktualnym
TAB --&gt; Przejdź do następnego regionu
S   --&gt; Podziel aktywne okno na regiony w poziomie
|   --&gt; Podziel aktywne okno na regiony w pionie
X   --&gt; Usuń (zabij) aktywny region
C-x --&gt; Zablokuj terminal
C   --&gt; Wyczyść ekran
C-l --&gt; Odśwież okno
C-g --&gt; Przełącz między dźwiękowym/wizualnym dzwonkiem
C-\ --&gt; Zabij okna i zakończ screen&lt;/pre&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_P9Sn6SUv534/SV94fC8jmwI/AAAAAAAAAC4/yaN6gvABNSI/s1600-h/zablokowana_sesja.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 194px;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/SV94fC8jmwI/AAAAAAAAAC4/yaN6gvABNSI/s320/zablokowana_sesja.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5287076962318392066" /&gt;&lt;/a&gt;

&lt;span style="font-weight:bold;"&gt;Najważniejsze polecenie&lt;/span&gt;

Zawsze to podkreślam i tak będzie też tym razem: pomoc została napisana po to, aby z niej korzystać. W przypadku pracy ze screenem pomoc jest dostępna pod skrótem C-a ?. W pomocy klawisz ctrl jest zaznaczany znakiem ^. Nie należy także zapominać o podręczniku systemowym (man screen).

&lt;span style="font-weight:bold;"&gt;Praca z programem&lt;/span&gt;

Ze screenem najwygodniej komunikować się przy pomocy skrótów umieszczonych w tabeli na początku. Można również wydawać komendy bezpośrednio (C-a : przechodzi do trybu wydawania komend). Jeszcze jedna możliwość to przekazywanie parametrów do polecenie screen czy w końcu wpisanie ustawień do pliku ~/.screenrc.

&lt;span style="font-weight:bold;"&gt;Przejdź na początek wpisywanej komendy&lt;/span&gt;

Normalnie pod C-a jest podpięte przechodzenie na początek wpisywanej komendy. W programie screen skrót C-a jest zarezerwowany jako prefiks skrótów klawiszowych. Aby przejść na początek komendy można skorzystać ze skrótu C-a a.

&lt;span style="font-weight:bold;"&gt;Okna, okna, okna i regiony&lt;/span&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_P9Sn6SUv534/SV94ezs238I/AAAAAAAAACw/eg1kiNqyCUM/s1600-h/wiele_okien.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 194px;" src="http://3.bp.blogspot.com/_P9Sn6SUv534/SV94ezs238I/AAAAAAAAACw/eg1kiNqyCUM/s320/wiele_okien.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5287076958226014146" /&gt;&lt;/a&gt;

Jak każdy porządny menadżer okien, screen pozwala tworzyć nowe okna, usuwać (zabijać) je oraz nawigować między nimi. W pojedynczym oknie może znajdować się jedna instancja powłoki systemowej. Dzięki regionom można wyświetlać jednocześnie wiele okien. Szczegóły na temat zarządzania można znaleźć w tabeli na początku.

Aby uczynić pracę wygodniejszą można podzielić ekran na regiony i w każdym z nich wyświetlić jedno okno. Po utworzeniu regionu jest on pusty. Należy do niego przejść (C-a TAB) i wówczas wybrać okno do wyświetlenia z listy (C-a ") lub utworzyć nowe okno (C-a C-c).

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_P9Sn6SUv534/SV94eURH9AI/AAAAAAAAACY/nzOq-mp8bcs/s1600-h/interaktywna_lista_okien.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 194px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/SV94eURH9AI/AAAAAAAAACY/nzOq-mp8bcs/s320/interaktywna_lista_okien.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5287076949788193794" /&gt;&lt;/a&gt;

&lt;span style="font-weight:bold;"&gt;Odłączanie i podłączanie&lt;/span&gt;

Jednym z ważniejszych zastosowań programu screen jest "zostawienie programu na screenie". Polega to na tym, że możesz odpalić program przez screen na zdalnym serwerze, zerwać połączenie (np. wyłączyć ssh), a program będzie nadal działał w tle na tym serwerze. Oczywiście gdyby zrobić to bez programu screen to podczas wylogowywania wszystkie procesy użytkownika zakończyłyby swoje działanie.

Odłączenie screena (C-a C-d) powoduje, że działa on w tle. Można go następnie przywrócić poleceniem screen -r. Jeżeli w systemie jest więcej niż jedna odłączona sesja to screen -r wypisze te sesje. Można następnie podać nazwę sesji jako parametr screen -r. Zazwyczaj odłączenie screen jest powiązane z wyłączeniem okna konsoli (C-a D D).

&lt;span style="font-weight:bold;"&gt;Podsumowanie&lt;/span&gt;

Screen pozwala na równoległą pracę z wieloma terminalami. Umożliwia zawieszanie i wznawianie sesji oraz pozostawianie uruchomionych programów nawet po wylogowaniu. Jest to dość nietypowy menadżer okien, który jest bezcenny podczas pracy zdalnej. Jednocześnie można go z powodzeniem stosować lokalnie, co również poprawi wygodę pracy. Te cechy powodują, że jest warty przynajmniej chwili uwagi poświęconej na zapoznanie się z nim.

Przedstawiłem screen jako prosty program. W rzeczywistości oferuje on duże możliwości konfiguracji. Ma ogromne możliwości. Po więcej szczegółów zapraszam do lektury man screen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4344626414770232188?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4344626414770232188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2009/01/czy-konsolowy-menader-okien-moe-by.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4344626414770232188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4344626414770232188'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2009/01/czy-konsolowy-menader-okien-moe-by.html' title='Czy konsolowy menadżer okien może być przydatny?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://4.bp.blogspot.com/_P9Sn6SUv534/SV99-_U_qYI/AAAAAAAAADY/TxPYjsi1Baw/s72-c/konsola.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6979818650741633503</id><published>2008-12-24T07:00:00.000-08:00</published><updated>2008-12-24T07:00:01.409-08:00</updated><title type='text'>Smacznej choinki, pięknego mikołaja oraz hojnych potraw</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_P9Sn6SUv534/SVE0dB7jnhI/AAAAAAAAACI/OSf9F3nJ_YA/s1600-h/choinka.JPG"&gt;&lt;img style="float:right; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 213px;" src="http://3.bp.blogspot.com/_P9Sn6SUv534/SVE0dB7jnhI/AAAAAAAAACI/OSf9F3nJ_YA/s320/choinka.JPG" border="0" alt="" id="BLOGGER_PHOTO_ID_5283061511221386770" /&gt;&lt;/a&gt;Z okazji świąt oraz, jakby nie patrzeć, zbliżającego się nowego roku, życzę Ci dużo zdrowia i radości, wielu wspaniałych pomysłów, ogromu ukończonych i udanych projektów, mnóstwa satysfakcji z wykonywanej pracy, mało stresu, ambitnych planów, niewielkiej ilości bugów oraz dobrze zakończonego roku. Na same  święta chciałbym abyś mógł cieszyć się mnóstwem śniegu i dobrej zabawy, miłymi chwillami spędzonymi w gronie rodzinnym, wieeeelką choinką i jeszcze większą górą prezentów pod nią.&lt;div align="left"&gt;Zatem &lt;strong&gt;WESOŁYCH ŚWIĄT I SZCZĘŚLIWEGO NOWEGO ROKU.&lt;/strong&gt;&lt;/div&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 268px;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/SVE5xqoPTlI/AAAAAAAAACQ/RGpQ4MswnGk/s320/mikolaj_ramka.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5283067363301740114" /&gt;Pewnie wszyscy już zrobili prezenty, ale trzeba pamiętać aby je dobrze schować, bo może się zdarzyć taka oto sytuacja:&lt;/p&gt;&lt;pre&gt;—MAMUSIU Z MOJEGO LISTU do świętego Mikołaja wykreśl kolejkę elektryczną, a wpisz ŁYZWY
— A co, nie chcesz już pociągu?
—Chcę, ale jeden znalazłem już w waszej szafie.&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6979818650741633503?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6979818650741633503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/smacznej-choinki-piknego-mikoaja-oraz.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6979818650741633503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6979818650741633503'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/smacznej-choinki-piknego-mikoaja-oraz.html' title='Smacznej choinki, pięknego mikołaja oraz hojnych potraw'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://3.bp.blogspot.com/_P9Sn6SUv534/SVE0dB7jnhI/AAAAAAAAACI/OSf9F3nJ_YA/s72-c/choinka.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1734251363383963327</id><published>2008-12-12T00:00:00.000-08:00</published><updated>2008-12-12T00:00:00.785-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Garść przydatnych poleceń</title><content type='html'>&lt;strong&gt;&lt;span style="font-family:times new roman;"&gt;Słynna zasada 80:20 mówi, że 80% zysków to zasługa 20% naszych wysiłków. Nie inaczej jest w przypadku użytecznych programów. Bez powoływania się na procenty, które zazwyczaj są wzięte z sufitu można śmiało stwierdzić, że większość pracy wykonujemy z wykorzystaniem zaledwie kilku/kilkunastu narzędzi. Chciałbym pokazać, gdzie warto szukać młotka pasującego do wykonywanego zadania.&lt;/span&gt;&lt;/strong&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Podstawa czyli BASH&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Po ostatnim wpisie znasz już jedno z takich narzędzi - język bash. Oczywiście nie warto się go uczyć w całości - szkoda czasu. Osobiście bardzo rzadko wykorzystuję instrukcje warunkowe tego języka. W szczególności case, której nawet nie opisywałem. W bashu można też tworzyć funkcje, ale czy jest to aż takie istotne?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Nie. Dobrze jest znać kilka idiomów, dzięki którym rozwiążesz (wyssane z palca) 80% zadań. Na pewno warto pamiętać pętlę for (z szególnym przypadkiem, który opisałem - wget).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Nawigacja&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Ponadto bardzo przydaje się cd (przejście do katalogu domowego), cd - (przejście do poprzedniego katalogu), cd nazwa (przejście do katalogu o nazwie 'nazwa'). Warto też wiedzieć, że można tworzyć stos katalogów. pushd nazwa_katalogu odkłada na stos katalog. Po wydaniu polecenia popd zostajemy przeniesieni do ostatniego katalogu. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 217px;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/SUD_LXmMZtI/AAAAAAAAAB4/D2HiCER1jfI/s320/pwd_cd_pushd_popd.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499334056077010" /&gt;&lt;span style="font-family:times new roman;"&gt;Chodzenie po drzewie katalogów nie należy do najciekawszych zajęć. Dlatego ktoś wpadł na genialny pomysł polecenia ls. Dzięki temu można czasami zobaczyć zawartość katalogu. Warto zapamiętać (czy wręcz utworzyć alias) ls -lah oraz ls -lahd. Pierwsza komenda listuje wszystko (nawet pliki normalnie niewidoczne) podając szegóły. Wszystko w formacie przystępnym człowiekowi. Czasem niepożądne gdy ls -lah * listuje nie tylko pliki i katalogi, ale również zawartość tych ostatnich. Wtedy rzydaje się opcja -d.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Orientacja w terenie&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Przydatne jest także polecenie pwd (wypisuje nasze aktualne położenie). Gdy czujemy się samotni i zagubieni w gąszczu poleceń, to z pomocą przychodzi nam &lt;strong&gt;podręcznik systemowy, czyli man&lt;/strong&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;. Ten człowiek podpowiada do czego służy dane polecnie. Podstowa składnia to man nazwa. Spróbuj np. man ls. Ale co zrobić gdy nie wiemy jakie polecenie wykonuje daną czynność? Oczywiście zapytać&lt;a href="http://www.google.pl/"&gt; wujka&lt;/a&gt;. Można też zapytać miłego człowieka, który raz już nam pomógł. man -k  game da wykaz gier w które warto sobie pograć w wolnych chwilach (szczególnie polecam man -k chess).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 118px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/SUD_LFA_RAI/AAAAAAAAABw/4Fs-yCnU5y8/s320/nawigacja_pwd_ls_which.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499329068188674" /&gt;&lt;span style="font-family:times new roman;"&gt;Czasem trzeba znaleźć dane polecenie - wtedy przydaje się komenda which. Np. which ls zwrócić bezwzględną ścieżkę do polecenia ls. Fajne jest też locate - sprawdź sam.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;W ostateczności można wydać polecenie mc (o ile mc jest zainstalowany). Sam z tego nie korzystam, ale niektórzy na pewno docenią istnienie tego potężnego narzędzia.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Moja ciocia to ma władzę! Obsługuje ksero w IPN. O archiwizacji...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Jest wiele sposobów. Moje ulubione polecenie to tar. Jest niesamowicie stare, ale za to obsługuje to co trzeba. Sam tar nie kompresuje, a jedynie archiwizuje. Czyli zamiast zmniejszać objętość tylko skleja wszystko w jeden plik. Kompresji zazwyczja dokonuje gzip lub bzip. Warto wiedzieć o tar -tf nazwa, które wypisuje zawartość archiwum 'nazwa', tar -zxvf nazwa.tgz, które wypakowuje archiwum w przypadku gdy jest spakowane przy pomocy gzip. W przypadku bzipa obowiązuje tar -xjvf nazwa. Do tworzenia zazwyczaj wykorzystuje się tar -cjvf nazwa_archiwum.tar.bz2 lista_plików_do_archiwum. W przypadku współpracy z użytkownikami 'jedynego słusznego' OSa przydają się też programy takie jak unzip czy unrar.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Niewiątpliwie bardzo istotny jest rdiff-backup, przy pomocy którego można wykonywać w prosty sposób kopie bezpieczeństwa swojej pracy. Pisałem o tym jakiś czas temu...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Głupie żarty&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Można sobie pożartować poleceniem eject i eject -t. Wystarczy wywoływać je na innym komputerze (np. przez ssh). Ciekawie to wygląda, gdy wykonuje się to skryptem. Wówczas w całej pracowni...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;A gdy któryś się wychyli...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 83px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/SUD_K_O84_I/AAAAAAAAABo/HD_YS1uJshA/s320/kill_iceweasel.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499327516140530" /&gt;&lt;span style="font-family:times new roman;"&gt;Gdy jakiś proces jest niesforny to można wysłać mu sygnał, aby się poprawił. Często jest to niestety śmiertelny komunikat. Służy do tego polecenie kill. Jako argument podaje się pid procesu-adresata. Można go pozyskać poleceniem top lub ps + grep. Domyślnie wysyłany jest niezbyt przyjemny komunikat. Można to zmienić podając identyfikator sygnału przez przełącznik -s (np. kill -s SIGKILL). Nieco przyjaźniejszym dla użytkownika poleceniem jest killall. Zamiast PID podaje się nazwę procesu (np. firefox). &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Tuning&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;GNU/Linux ma bardzo wygodny system instalowania nowego oprogramowania. Nie wierz w to co mówią Ci wszyscy użytkownicy W...., którzy Linuksa na oczy nie widzieli - za to lubią się mądrzyć :-) Taki naród. Ich podstwowym "argumentem" jest tzw. "piekło zależności". Pracuję na Linuksie już parę lat i jakoś problemów "niet". &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Łatwość instalacji oprogramowania na Linuksie jest często zaskoczeniem dla nowych w temacie. Jeżeli posiadasz GNU/Debiana lub dystrubucje na nim opartą (np. Ubuntu) to masz do dyspozycji aptitude.&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ aptitude search jakis_ciag_znakow&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Wyszukuje oprogramowanie, pasuje do ciagu znakow. Zostajemy uraczeni listą nazw programów i bibliotek wraz z krótkimi opisami.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Po wybraniu tego "jedynego" wystarczy użyć magicznego&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ sudo aptitude install nazwa_pakietu&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Zostanie zainstalowany ten pakiet, oraz wszystkie zależności. Po więcej szczegółów odsyłam do man aptitude.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Oczywiście wpisywanie za każdym razem takich długich ciągów znaków nie jest wygodne. Dlatego warto utworzyć aliasy. Do swojego ~/.bashrc należy wklepać&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;alias ai='sudo aptitude install'
alias as='aptitude search'&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;i cieszyć się skróconymi komendami ai oraz as ;-)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Trzeba też pamiętać o aktualizacji systemu. Można ustawić aby robiły się same (w Ubuntu pojawia się taki śmieszny znaczek w tray). Można też ręcznie, czyli&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ aptitude update
$ aptitude upgrade (lub dist-upgrade)&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Stale pod kontrolą&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;watch to bardzo porządne narzędzie, które pokazał mi Marcin. Służy do monitorowania wyniku innego polecenia. Sam najczęściej wykorzystuję&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ watch 'acpi -t'&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;W ten sposób dostaję mini programik, który śledzi temperaturę procesora oraz stan baterii. Program acpi jest nieco dokładniejszy niż klasyczne aplety z traya. Trochę mnie to dziwi, ale cóż...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 79px;" src="http://2.bp.blogspot.com/_P9Sn6SUv534/SUD_Lor9k8I/AAAAAAAAACA/2K6vVxIMTPU/s320/watch_acpi.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499338643674050" /&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Grepnij to&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Narzędzia konsolowe mają to do siebie, że wypluwają ogromne ilości tekstu. Nas zazwyczaj interesuje tylko część danych. Można poszukać odpowiednich przełączników, zawężać zapytania itp. Ale można też użyć grep. W GNU/Linuksie jest wiele małych poleceń. Są to zazwyczaj wyspecjalizowane proste programy typu cat, echo, sort, uniq, head, sed,... Co ważne, można je łączyć w większe maszyny przy pomocy potoków. Dla przykładu aby dostać posortowaną listę nazwisk z pliku, bez powtórzeń robimy&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ sort nazwiska.txt | uniq&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Grep pozwala wybierać wiersze pasujące do wzorca (dokładniej: wyrażenia regularnego). Gdybyśmy chcieli wypisać nazwiska kończące się na ski to:&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ sort nazwiska.txt | uniq | grep -i "ski$"&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;ski$ to wyrażenie regularne. Litery pasują do siebie, zaś znak $ oznacza koniec wiersza. Analogicznie początek oznaczany jest przez ^. Więcej na temat wyrażeń regularnych w manie do grepa lub man regex.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Warto pamiętać opcje:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;-i jak &lt;strong&gt;i&lt;/strong&gt;gnore case, czyli wielkość liter nie ma znaczenia&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;-v jak in&lt;strong&gt;v&lt;/strong&gt;ert, czyli wypisujemy wiersze niepasujące do wzorca (*)
&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;(*) To wbrew pozorom nie jest takie proste, bez tej opcji. Konia z rzędem, temu kto napisze, w sensownym czasie, podstawowe wyrażenie regularne pasujące do tekstu &lt;strong&gt;nie&lt;/strong&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;zawierającego wzorca baba. Oczywiście tyczy się to osób, które nie znają teorii języków formalnych i automatów ;-P&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Szukajcie a znajdziecie&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Polecenie find jest jednym z podstawowych. Warto poznać jego składnię. Jest o wiele potężniejsze od klikalnych odpowiedników. Zadawane pytania mogą być bardzo szczegółowe i dowolnie złożone. Możńa nawet zagnieżdżać find. Podstawowa składnia to&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;find lista_katalogow_do_przeszukania wzorzec&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;np.&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find /etc -iname "*tab"&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;opcja -iname to dopasowanie do nazwy, bez uwzględniania wielkości liter (jest też -name). Często przydaje się coś w stylu&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.cpp" -o "*.h"&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;czyli wyszukanie wszystkich plików źródłowych lub nagłówkowych w drzewie katalogów (-o oznacza OR). (czyli aktualnym). Ponieważ w przypadku chodzenia po systemowych katalogach dostaniemy masę błędów (brak praw dostępu) na standardowe wyjście błędów, to warto pokusić się o przekierowanie go w stronę odchłani&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find /etc -iname "*tab" 2&gt; /dev/null&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Przydatne jest składanie poleceń. Aby na każdym znalezionym pliku wykonać akcję można posłużyć się mechanizmem wbudowanym w find (który jest IMO niewygodny z uwagi na konieczność zabezpieczania wielu znaków przed interpretacją przez konsolę) lub programem xargs. Ciekawy przykład to moje ulubione&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.h" -o -iname "*.cpp" | xargs wc -l&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Ten krótki kawałek wypisze na wyjście dla każdego pliku z kodem ile ma linii kodu (wc -l). Dodatkowo na koniec poda sumę. Fajne jest to, że między find i wc można wrzucić skrypt, który np. usunie w locie komentarze. Poznamy wówczas ilość linii efektywnego kodu (szczególnie, że po drodze można jeszcze wyrzucić puste linie choćby przy pomocy seda).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Potęga łączenia narzędzi&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Nie tak dawno miałem okazję pracować z pewną biblioteką. Jeden z moich znajomych też nad nią pracował (ponad rok temu). Był ciekawy czy kod, który napisał jeszcze istnieje. Ciekawe, że NIKT nie wiedział jak to łatwo sprawdzić. Zaproponowałem&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.java" | xargs grep -i Nazwisko&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Oczywiście rozwiązało to problem. Czas? Jakieś kilka sekund na wklepanie tej linii. Można też pokusić się o &lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.java" | xargs grep -i Nazwisko | sed -e 's/:.*$//'&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Które wyrzuci niepotrzebny szum i zostawi tylko nazwy plików. Na koniec jeszcze sort i uniq&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.java" | xargs grep -i Nazwisko | sed -e 's/:.*$//' | sort | uniq&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;oraz wc -l&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;$ find . -iname "*.java" | xargs grep -i Nazwisko | sed -e 's/:.*$//' | sort | uniq | wc -l&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;W efekcie dostajemy liczbę plików, w których widnieje nazwisko. W ostatecznej wersji może się to wydawać dość skomplikowane ;-) Proces tworzenia jest jednak prosty i zazwyczaj zaczyna się od małych bloczków, dokłada po cegiełce, aż wychodzi porządny program. Porządny? Tak! Napisz taki program w C. Zrób to równie sprawnie jak przy pomocy tego ciągu poleceń.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;Last but not least&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/SUD-7bYcfdI/AAAAAAAAABY/xDLO_wPz7rE/s320/emacs.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499060194246098" /&gt;&lt;span style="font-family:times new roman;"&gt;Właściwie najważniejsze narzędzie to... drugi system operacyjny. Przez niektórych nazywany GNU/Emacs i często mylony z edytorem tekstu. Z pomocą GNU/Emacsa można zrobić wszystko i to w krótkim czasie. Więcej na ten temat można znaleźć na tym blogu. Można też poszukać dodatkowych informacji w formie wideo: &lt;a href="http://www.emacsblog.org/2008/12/05/emacs-in-5-minutes/"&gt;http://www.emacsblog.org/2008/12/05/emacs-in-5-minutes/.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Na stronie &lt;a href="http://coldpeer.jogger.pl/2008/05/17/emacs-linkowisko/"&gt;Coldpeera&lt;/a&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt; jest linkowisko na temat GNU/Emacsa. Warto tam zajrzeć.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Bardzo wygodne jest również to, że z GNU/Emacsa jest dostępny terminal...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="http://3.bp.blogspot.com/_P9Sn6SUv534/SUD_KjcWQ6I/AAAAAAAAABg/T539qMD0Ahw/s320/emacs_terminal.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5278499320056136610" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1734251363383963327?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1734251363383963327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/gar-przydatnych-polece.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1734251363383963327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1734251363383963327'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/gar-przydatnych-polece.html' title='Garść przydatnych poleceń'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://4.bp.blogspot.com/_P9Sn6SUv534/SUD_LXmMZtI/AAAAAAAAAB4/D2HiCER1jfI/s72-c/pwd_cd_pushd_popd.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6872127045418509403</id><published>2008-12-11T05:00:00.003-08:00</published><updated>2008-12-11T05:00:01.715-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie czasem'/><title type='text'>Wygooglam to dla Ciebie</title><content type='html'>&lt;p&gt;&lt;strong&gt;&lt;span style="font-family:times new roman;"&gt;Wiele osób lubi udzielać się na forach. Odpowiadać na pytania, pomagać innym. Czasem zdarzają się niestety osobnicy, którzy zniechęcają takie osoby zadając elementarne pytania, na które google daje odpowiedź już w pierwszym wyniku. Co zrobić? Jak nie stracić czasu na głupią dyskusję?&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Ja zazwyczaj używam magicznej kombinacji klawiszy Ctrl+w, co skutecznie zamyka karte w przeglądarce. Ale są metody, które wyglądają nieco ciekawiej...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Można w odpowiedzi pokazać odpowiednio przygotowaną stronkę. Dla przykładu tak: &lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;&lt;a href="http://letmegooglethatforyou.com/?q=Jak+napisa%C4%87+bota+gadu+gadu"&gt;tu znajdziesz odpowiedź&lt;/a&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;. Można bardzo łatwo spreparować tego typu stronkę. Odpowiedni generator jest pod adresem &lt;a href="http://letmegooglethatforyou.com/?q="&gt;http://letmegooglethatforyou.com/?q=&lt;/a&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Można też wesprzeć akcję &lt;a href="http://www.google-nie-boli.pl/"&gt;google nie boli&lt;/a&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt;, lub posłużyć się klasyką &lt;a href="http://www.catb.org/~esr/faqs/smart-questions.html"&gt;RTFM&lt;/a&gt;&lt;/span&gt;&lt;span style="font-family:times new roman;"&gt; lub polskim &lt;a href="http://rtfm.bsdzine.org/"&gt;odpowiednikiem&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:times new roman;"&gt;Jest też cała masa śmiesznych rysunków wyśmiewających lenistwo osób niekorzystających z usług google'a. Jednak można zauważyć, że po zobaczeniu takiego obrazka osoba pytająca się obraża. Dlatego proponuję jednak żartobliwe "użyję za Ciebie google".&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6872127045418509403?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6872127045418509403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/wygooglam-to-dla-ciebie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6872127045418509403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6872127045418509403'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/wygooglam-to-dla-ciebie.html' title='Wygooglam to dla Ciebie'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-56511177446923895</id><published>2008-12-03T00:00:00.002-08:00</published><updated>2008-12-03T00:00:01.922-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><title type='text'>Tekst nie gryzie</title><content type='html'>Obecnie powłoka systemowa jest niestety niedoceniana. Mało jest osób, którym chce się poświęcić chwilę na nauczenie się podstaw pracy z systemem przez interfejs tekstowy. Często łatwiej jest 'wyklikać' to, czego się chce. Gdy trzeba pewne czynności powtarzać to zazwyczaj trzeba dużo klikać. Niestety możliwości nakładek graficznych są ograniczone przez to, co wymyślił kiedyś programista.

  Warto poznać shell choćby po to, aby wiedzieć z czego się rezygnuje. Osobiście zawsze włączam xterma z bashem zaraz po GNU/Emacsie przy starcie środowiska. W tym poście chciałbym ograniczyć się do samego programowania w bashu. Naturalnie aby wydajnie pracować w tego typu środowisku należy poświęcić chwilę na zapoznanie się z dostępnymi programami tekstowymi.

  Pragnę jeszcze obalić pewien mit. Konfiguracja programów przez edycję plików konfiguracyjnych *nie* jest trudna. Zazwyczaj niczym nie różni się to od konfiguracji przez okienko. Dostajemy listę opcji wraz z opisem w komentarzach. Wystarczy odkomentować to co się chce, a czasami dodatkowo napisać coś po znaku równości. W skrajnych przypadkach zachodzi potrzeba znalezienia przykładowego pliku w internecie.

  Powłoka bash udostępnia wszystkie podstawowe konstrukcje językowe. Mamy więc pętle, instrukcję warunkową czy instrukcję wyboru (taki switch z C++). Każdy skrypt należy rozpocząć sekwencją&lt;pre&gt;#!/bin/bash&lt;/pre&gt;I nadać skryptowi prawo do wykonania&lt;pre&gt;chmod +x nazwa_skryptu&lt;/pre&gt;Następnie można go uruchomić jak zwykły program (np. skompilowany przy pomocy gcc). Do wypisywania tekstu służy polecenie echo. Klasyczny program powitalny wygląda zatem tak:&lt;pre&gt;#!/bin/bash
echo "Hello world";&lt;/pre&gt;echo ma dwa ciekawe przełączniki: -n oraz -e. Pierwszy powoduje, że do tekstu nie zostanie dodany znak nowej linii na koniec (domyślnie jest dodawany). -e natomiast powoduje, że dwuznaki zaczynające się znakiem \ będą rozpoznawane. Aby uzyskać ten sam efekt co wyżej można również napisać&lt;pre&gt;#!/bin/bash
echo -ne "Hello world\n";&lt;/pre&gt;O opcji -E możesz doczytać sam (man echo).

  W tekście możesz wywoływać dowolne komendy. Do najbardziej użytecznych należy zaliczyć grep, sort, sed, awk, tr, sleep, echo, tar, find, wget. Dodatkowo warto znać polecenia do zarządzania plikami, katalogami oraz procesami. Podczas pisania oprogramowania pomocniczego przydaje się polecenie install. O wszystkich możesz przeczytać na stronach podręcznika systemowego.

  Jest jeszcze jedna szczególnie przydatna instrukcja - seq. Ma trzy formy seq K, seq S K, seq S M K. seq tworzy ciąg liczb, który zaczyna się od S, kończy na K i idzie z krokiem M. np. seq 10 -2 4 wygeneruje ciąg 10 8 6 4. Jest to przydatne w pętli for:&lt;pre&gt;for i in $(seq 10 -2 4); do echo $i; done&lt;/pre&gt;Dość często wykorzystuję taką sekwencję, gdy potrzebuję pobrać kilka plików z internetu, które mają podobne nazwy. Np. jeżeli na stronie wykładu mam linki lista1.pdf, lista2.pdf lista3.pdf itd. To wystarczy napisać&lt;pre&gt;for i in $(seq 10); do wget http://adres_strony/lista${i}.pdf; done&lt;/pre&gt;Nie trzeba tworzyć osobnego skryptu, wystarczy tę linijkę wpisać po znaku zachęty. Oczywiście nie trzeba tego robić jednym ciągiem. Można napisać część warunkową (od słowa for aż do słowa do) i nacisnąć enter. Dalszą część wpisujemy wówczas w kolejnych liniach. Zamiast konstrukcji $(..) można użyć 'wstecznych' apostrofów (~ bez shift). Zauważ, że wartość $X można zapisać jako ${X}. Jest to przydatne gdy, tak jak w przykładzie powyżej, trzeba umieścić wartość zmiennej w pewnym tekście i mogłoby dojść do niejednoznaczności.
 
  Jak widać aby wyłuskać wartość zmiennej X należy poprzedzić ją znakiem dolara. Jeżeli X jest równe 3, to $X jest właśnie tą liczbą. Istnieje zatem syntaktyczne rozróżnienie l-wartości i r-wartości. Aby przypisać coś do zmiennej piszemy jej nazwę, znak równości i wartość.&lt;pre&gt;X=4;
echo $X;&lt;/pre&gt;Spróbuj do X przypisać 2+3 ;-) Na ekranie nie zobaczysz 5, tylko 2+3. Aby przypisać obliczoną wartość można np. napisać let X=2+3; (istotny brak spacji).

  Kolejną komendą wartą zapamiętania jest test. Ta instrukcja jest przydatna w pętli typu while oraz instrukcji if. while'a nie będę opisywał, bo jest oczywisty gdy zobaczy się przykład&lt;pre&gt;#!/bin/bash

i=3;
while test $i -lt 10; do
   echo $i;
   let i=$i+3;
done&lt;/pre&gt;Zobaczyłeś jak można zapisać warunek. test można wywołać także mniej jawnie przez napisanie warunku jako [ $i -lt 10 ];

  Instrukcja if wygląda tak jak zwykle:&lt;pre&gt;if warunek; then
  instrukcje;
elif warunek;
  instrukcje;
# ... dowolna ilość elif
else
  instrukcje;
fi&lt;/pre&gt;A oto przykład&lt;pre&gt;#!/bin/bash

wiek=45;
echo -n "jesteś ";
if [ $wiek -le 10 ]; then
   echo "małolat";
elif [ $wiek -lt 18 ]; then
   echo "niepełnoletni";
elif [ $wiek -le 40 ]; then
   echo "młody";
else
   echo "stary";
fi&lt;/pre&gt;Spróbuj napisać kilka prostych skryptów. To na prawdę nie jest trudne.

Jak widać nauczyć się basha jest bardzo prosto. Równie łatwo się go wykorzystuje. Często szybciej jest wklepać jedną linijkę niż klikać i klikać...

Może kiedyś opiszę jak wydajnie pracować w powłoce, a nie tylko jak pisać proste skrypty. Jako ćwiczenie dla wszystkich okienkowych wyjadaczy mam następujące zadanie: wypisać wszystkie pliki na dysku, które odnoszą się do tego samego pliku (np. są linkami / synonimami). Wynik powinien być listą grup takich samych plików. To może być przydatne, aby usunąć zbędne duplikaty. W powłoce można to zrobić zagnieżdżając find :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-56511177446923895?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/56511177446923895/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/tekst-nie-gryzie.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/56511177446923895'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/56511177446923895'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/12/tekst-nie-gryzie.html' title='Tekst nie gryzie'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-4394639438292091657</id><published>2008-11-26T00:00:00.005-08:00</published><updated>2008-11-29T15:48:21.745-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Teoria informatyki'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Jak zmniejszyć ilość danych do analizy?</title><content type='html'>&lt;p&gt;&lt;strong&gt;Często nasze programu muszą analizować pewne dane. Może to być np. obraz w programie OCR, dane dla sieci neuronowej, genotyp w algorytmach ewolucyjnych, muzyka w programie do analiz i obróki dźwięku... Tego typu dane są często duże, co wiąże się z odpowiednim nakładem pracy jaki musi wykonać nasz program aby przeanalizować wejście. Gdy złożoność użytego algorytmu jest ponadliniowa to zaczyna robić się nieciekawie. Powstaje pytanie: "czy da się zmniejszyć liczbę danych do analizy?". Oczywiście standardowa kompresja nie jest rozwiązaniem - chodzi o zmniejszenie liczby informacji, a nie upakowanie ich w mniejszej pamięci. Odpowiedź na postawione pytanie jest twierdząca.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Jak powszechnie wiadomo dane, to poprostu ciąg zer i jedynek. Można jednak na nie patrzeć z punktu widzenia trochę wyższej struktury. Dla nas dane niech będą ciągiem liczb rzeczywistych. Z algebraicznego punktu widzenia taki ciąg (matematycznie raczej chodzi o wektor) jest elementem pewnej przestrzeni liniowej, o znanym wymiarze (ilość składowych tego wektora). Wielkość danych jest zatem charakteryzowana przez ten wymair. Przy tym im mniejszy wymiar przestrzeni tym mniej złożone są dane i łatwiej będzie je przetworzyć. Wystarczy zatem zapisać wektor w taki sposób, aby był jak najkrótszy.&lt;/p&gt;&lt;p&gt;Niestety nie jest to możliwe bez utraty informacji. Jedno z ważniejszych twierdzeń algebry mówi o równoliczności wszystkich &lt;a href="http://pl.wikipedia.org/wiki/Baza_(przestrzeń_liniowa)"&gt;baz&lt;/a&gt; danej algebry. Pozostaje zatem pogodzić się z utratą pewnych informacji. Warto oczywiście wybrać te, które są najmniej istotne. W tym celu można posłużyć się &lt;a href="http://pl.wikipedia.org/wiki/Analiza_głównych_składowych"&gt;analizą głównych składowych&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Standaryzacja danych&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Analiza jest bardzo prosta. Załóżmy, że mamy macierz danych (jedna kolumna odpowiada jednemu wektorowi danych, natomiast jeden wiersz jednej cesze). Najpierw należy przeprowadzić standaryzację (chodzi o to, żeby siła sygnału nie miała znaczenia - np. silny sygnał cechy 1 nie powinien przyćmić słabszych sygnałów pozostałych cech - a jedynie jego "rodzaj").  &lt;/p&gt;&lt;p&gt;Aby ustandaryzować cechę, liczymy dla niej średnią i odejmujemy ją od wszystkich wartości dla danej cechy. Dzięki temu średnia wynosi 0. Takie przygotowanie wystarcza, ale warto wszystkie wartości podzielić przez odchylenie standardowe dla cechy, aby je znormalizować.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Wartości własne macierzy kowariancji&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Następnym krokiem jest obliczenie macierzy kowariancji dla macierzy cech. Taka macierz odzwierciedla powiązania między cechami. Można np. zauważyć, że posiadana wiedza silnie zależy od wieku badanej osoby. Tego typu związki zostają wykryte podczas tworzenia macierzy kowariancji. Wiem, że brzmi to nieco magicznie. Faktycznie macierz kowariancji jest zbiorem zależności między parami wektorów macierzy wejściowej - taka zależność jest nazywana &lt;a href="http://pl.wikipedia.org/wiki/Kowariancja"&gt;kowariancją&lt;/a&gt;. Nie jest to też takie świetne, na jakie może wyglądać - kowariancja określa tylko &lt;u&gt;liniową&lt;/u&gt; zależność. Jej zerowość o niczym nie świadczy.&lt;/p&gt;&lt;p&gt;Z macierzy kowariancji następnie oblicza się &lt;a href="http://pl.wikipedia.org/wiki/Wektor_własny"&gt;wektory własne&lt;/a&gt; wraz z &lt;a href="http://pl.wikipedia.org/wiki/Wartość_własna"&gt;wartościami własnymi&lt;/a&gt;. Wektor własny macierzy A, to taki wektor w, który jest skalowany przez A. Skalowany, czyli mnożony przez pewien współczynnik l, który jest nazywany wartością własną. Matematycznie można zapisać tę zależność jako Ax = lx.&lt;/p&gt;&lt;p&gt;W tym momencie możemy pozbyć się części informacji (albo wybrać te potrzebne). W tym celu wybieramy pewną liczbę wartości własnych (np. k największych z nich w ten sposób aby sumowały się do przynajmniej 95% sumy wszystkich wartości). W ten sposób godzimy się na utratę tych 5% mniej znaczących informacji. Jest to dość obrazowe, ale mniej więcej oddaje o co chodzi i co się dzieje z danymi.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Przechodzenie do nowej bazy
&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Wybrane wartości własne wyznaczają jednocześnie wektory własne. Ustawiając je w macierz otrzymujemy "macierz przejścia" M, która pozwala nam rzutować cechy z początkowej przestrzeni na nową przestrzeń (tracąc przy tym część danych). Następnie każdy wektor wejściowy x mnożymy przez taką macierz otrzymując wektor wyjściowy y = Mx. Jeżeli wszystkie takie wektory ustawimy w macierz to dostniemy nową reprezentację danych wejściowych. To co nas interesuje, to fakt, że nowe dane mniej więcej odzwierciedlają wejściowe dane, oraz drugi fakt, że rozmiar nowych danych jest mniejszy od rozmiaru tych "starych".&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Podsumowanie&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Wymiaru przestrzeni danych nie da się zmniejszyć. Można jednak znacznie go zredukować jeżeli zgodzimy się na straty w informacjach. Zazwyczaj nie są one duże, a nowe dane odzwierciedlają sedno danych wejściowych. W praktyce zazwyczaj zostają odfiltrowane błędy pomiarów oraz szum. &lt;/p&gt;&lt;p&gt;Aby przeprowadzić dane do nowej przestrzeni należy:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Ustandaryzować dane&lt;/li&gt;&lt;li&gt;Obliczyć k wektorów własnych (odpowiadających k największym wartościom własnym) macierzy kowariancji&lt;/li&gt;&lt;li&gt;Ustawić te wektory w macierz, przez którą nalezy mnożyć wektory danych wejściowych, aby uzyskać dane w postaci "zredukowanej".&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Moim zdaniem warte przeczytania są &lt;a href="http://ordination.okstate.edu/PCA.htm"&gt;http://ordination.okstate.edu/PCA.htm&lt;/a&gt; oraz  &lt;a href="http://www.statsoft.com/textbook/stfacan.html"&gt;http://www.statsoft.com/textbook/stfacan.html&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4394639438292091657?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4394639438292091657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/jak-zmniejszy-ilo-danych-do-analizy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4394639438292091657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4394639438292091657'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/jak-zmniejszy-ilo-danych-do-analizy.html' title='Jak zmniejszyć ilość danych do analizy?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2246369852116037993</id><published>2008-11-20T00:00:00.003-08:00</published><updated>2008-11-20T00:00:00.367-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Równy czy równoważny?</title><content type='html'>Tym razem mniej tekstu, więcej kodu ;-) To będzie notka inspirowana przez wpis &lt;a href="http://xion.org.pl/2008/11/17/c-referencje-i-porownywanie/"&gt;Xiona&lt;/a&gt;. Napiszę czym różni się 'equal' od 'equivalent' - jest to "niebezpieczeństwo", które jako pierwsze przyszło mi do głowy po przeczytaniu wspomnianego posta. Rozważmy następujący przykład:&lt;pre&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;iterator&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;

class Osoba {
public:
 explicit Osoba(const std::string&amp;amp; imie, size_t wiek)
     :  m_imie(imie), m_wiek(wiek) {
 }

 bool operator&amp;lt; (const Osoba&amp;amp; osoba) const {
     return m_wiek &amp;lt; osoba.m_wiek;
 }

 bool operator== (const Osoba&amp;amp; osoba) const {
     return (m_imie == osoba.m_imie) &amp;amp;&amp;amp; (m_wiek == osoba.m_wiek);
 }

 std::string get_imie() const {
     return m_imie;
 }

 size_t get_wiek() const {
     return m_wiek;
 }

private:
 std::string m_imie;
 size_t m_wiek;
};

std::ostream&amp;amp; operator&amp;lt;&amp;lt; (std::ostream&amp;amp; os, const Osoba&amp;amp; osoba) {
 os &amp;lt;&amp;lt; osoba.get_imie() &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; osoba.get_wiek();
 return os;
}&lt;/pre&gt;Na początek zdefiniowaliśmy klasę Osoba. Wszystko co jest potrzebne do tego przykładu jest zdefiniowane jawnie. Osoba charakteryzuje się tym, że ma wiek oraz imię. Osoby są równe gdy noszą to samo imię i są w tym samym wieku. Chcemy ponadto ustawiać ludzi wg. wieku. Aby móc wypisać osobę został zdefiniowany specjalny operator. Czas przejść do sedna przykładu&lt;pre&gt;int main() {
 std::set&amp;lt;Osoba&amp;gt; dwadziescia_lat;

 Osoba lukasz("Lukasz", 20);
 Osoba michal("Michal", 20);
 Osoba monika("Monika", 20);
 Osoba hugon("Hugon", 20);

 dwadziescia_lat.insert(lukasz);
 dwadziescia_lat.insert(michal);
 dwadziescia_lat.insert(monika);
 dwadziescia_lat.insert(hugon);

 std::copy(dwadziescia_lat.begin(),
       dwadziescia_lat.end(),
       std::ostream_iterator&amp;lt;Osoba&amp;gt;(std::cout, "\n"));

 return 0;
}&lt;/pre&gt;Tworzymy cztery osoby i umieszczamy je w zbiorze dwudziestolatków. Następnie wypisujemy wszystkich na wyjście. Zastanów się jaki będzie wynik na konsoli... i dlaczego taki.

&lt;a href="http://www.sgi.com/tech/stl/set.html"&gt;Dokumentacja dla zbioru&lt;/a&gt; mówi "It is also a Unique Associative Container, meaning that no two elements are the same.".

Ale równe czy równoważne? Ogólnie mówiąc STL posługuje się polityką równoważności. Zakłada się, że a jest równoważne b wtedy i tylko wtedy gdy !(a &amp;lt; b) &amp;&amp; !(b &amp;lt; a). Powinno być teraz oczywiste, że na wyjściu pojawi się dokładnie jeden wpis - w końcu zbiór jest jednoelementowy.

Oczywiście jest to udokumentowane. Można spojrzeć choćby na stronę &lt;a href="http://www.sgi.com/tech/stl/StrictWeakOrdering.html"&gt;Strict Weak Ordering&lt;/a&gt; aby dowiedzieć się czym jest równoważność w C++. Na stronie dokumentacji kontenerów asocjacyjnych zawsze jest zaznaczone, że do porównywania jest używany obiekt typu StrictWeakOrdering. Moim zdaniem taka dokumentacja nie wystarcza, bo podaje informacje "pośrednio".

Na zakończenie dodam, że dla typu char* domyślne porównanie nie ma nic wspólnego z porządkiem leksykograficznym :-D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2246369852116037993?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2246369852116037993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/rwny-czy-rwnowany.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2246369852116037993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2246369852116037993'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/rwny-czy-rwnowany.html' title='Równy czy równoważny?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8521533171144129238</id><published>2008-11-18T00:00:00.000-08:00</published><updated>2008-11-18T00:00:00.825-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Szyfry typu rot oraz szyfr base64</title><content type='html'>Szyfr Rot13 to bardzo popularny sposób zakodowywania wiadomości. Polega na tym, że każdy znak z alfabetu angielskiego zamienia się znakiem oddalonym od niego o 13 pozycji w przód. Dla przykładu litera A przechodzi na N. Przesunięcie jest cykliczne, co oznacza, że litera z jest przekształcana na m, a nie na jakiś znak spoza alfabetu. Bardzo łatwo napisać program kodujący. W Perlu wystarczy napisać jedną linię:&lt;pre&gt;perl -pe 'tr/a-zA-Z/n-za-mN-ZA-M/'&lt;/pre&gt;Rot47 brat Rot13. Są dwie różnice między tymi szyframi. Rot47 przesuwa o 47 pozycji, jak łatwo się domyślić. Dodatkowo Rot47 działa na znakach o kodach z zakresu 33-126, a nie tylko na alfabecie angielskim.

W przypadku obu szyfrów istnieją słowa czy frazy, które zaszyfrowane dają się nadal odczytać. Co gorsze - sens wynikowego zdania zostaje niezmieniony, w stosunku do wejściowego. Miałoby to pewne znaczenie, gdyby nie fakt, że oba szyfry są beznadziejne. Nie należy nimi kodować ważnych wiadomości (tych mniej ważnych raczej też nie). Oba szyfry mają tę własność, że szyfrując tekst dwa razy dostajemy tekst wejściowy.

Mimo to oba mają zastosowanie. Dzięki temu, że prawie każdy potrafi rozpoznać wiadomość zaszyfrowaną tymi szyframi, RotXX świetnie nadają się do ukrywania pewnej części wiadomość (np. odpowiedzi do zagadki). Dzięki temu ta część nie daje się odczytać, ale łatwo ją rozkodować.

Innym ciekwym tworem jest Base64. Ten szyfr również jest bardzo prosty. Wiadomość traktujemy jako ciąg bitów (nie bajtów). Jeden znak ma 8 bitów, ale w Base64 przetwarzamy bity szóstkami. Na 6 bitach można zapisać 64 różne liczby (od 0 do 63). Te liczby są zamieniane na znaki alfanumeryczne. 0 jest mapowane na literę A, 1 na B, ..., 25 na Z, 26 na a, 27 na b, 51 na z, 52 na 0, 53 na 1, 61 na 9, 62 na +, 63 na /. Dodatkowo znak = jest używany do wyrównania.

Ten szyfr oczywiście również bardzo łatwo złamać, ale nie utajnianie danych jest celem Base64. Zauważ, że wynikowy tekst składa się wyłącznie ze znaków alfanumerycznych. Dzięki temu można bezpiecznie przesyłać pliki binarne np. przez e-mail. Base64 jest też wykorzystywany do szyfrowania haseł podczas wysyłania poczty elektronicznej (przy logowaniu do skrzynki).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8521533171144129238?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8521533171144129238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/szyfry-typu-rot-oraz-szyfr-base64.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8521533171144129238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8521533171144129238'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/szyfry-typu-rot-oraz-szyfr-base64.html' title='Szyfry typu rot oraz szyfr base64'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4148866569426125565</id><published>2008-11-15T00:00:00.008-08:00</published><updated>2008-11-18T08:54:59.580-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie czasem'/><title type='text'>Kiedy programowanie "profilaktyczne" może być złe? Przypadek ze slicingiem</title><content type='html'>Większość programistów z czasem wyrabia sobie pewne "prawa programowania". Zazwyczaj mają postać warunków: "jeśli... to...". Szczególnie tyczy się to programistów C++, ponieważ jest to bardzo potężny język i łatwo jest się "postrzelić w nogę". Można się spotkać z określeniem 'wypracowane procedury' czy 'wypracowane zachowania' w odniesieniu do takiego zbioru reguł.

Na początek może przykład spoza programowania, aby wyjaśnić co mam na myśli. Gdy wychodzę z domu, zamykam drzwi na klucz i sprawdzam czy są zamknięte naciskając klamkę. To co jest dobre, to fakt, że jest to odruch. Nigdy nie zdarzyło mi się zastanawiać czy dobrze zamknąłem drzwi. Każdy z nas ma na pewno wiele takich zasad. Prosta czynność polegająca na naciśnięciu klamki daje to, że "mózg nie zastanawia się nad tym czy drzwi zostały zamknięte i ma więcej czasu na coś ważniejszego".

Niestety takie postępowanie, jak się okazuje, ma też wady (które moim zdaniem nie są istotne, ale o tym później). Na razie przedstawię kilka przydatnych, moim zdaniem, reguł. Moim celem nie jest dostarczenie spisu wszystkich sensownych reguł dla programowania, ale kilka, których ja przestrzegam to:
&lt;ul&gt;&lt;li&gt;Destruktory zawsze są wirtualne - niezależnie od przeznaczenia klasy. Co z tego, że po klasie nie będzie można dziedziczyć? Musisz się niepotrzebnie zastanawiać nad tym czy może kiedyś semantyka ulegnie zmianie i będzie trzeba dodać virtual. Dodanie virtual nic nie kosztuje i niczego nie psuje&lt;/li&gt;&lt;li&gt;Konstruktory zawsze są 'explicit' - niezależnie od liczby argumentów. Liczba argumentów może się zmienić np. na 1 i wtedy trzeba by uważać na explicit. Deklarując każdy konstruktor jako explicit oszczędzasz czas, bo można wstawiać konstruktor snippetem. Oszczędzasz energię, bo jest to kolejna rzecz, o której nie musisz pamiętać.&lt;/li&gt;&lt;li&gt;Unikaj konstrukcji switch. Po pierwsze switche mają to do siebie, że się rozrastają. Zazwyczaj switch można wyeliminować przez zastosowanie polimorfizmu (dodając też trochę elastyczności do projektu). Co więcej switche nie obsługują zakresów, więc kiedyś może się okazać konieczna zamiana na if..else if... Nie wspominając już o typowym braku słówka break ;-)
&lt;/li&gt;&lt;li&gt;Wszystkie metody powinny być zawsze wirtualne. Jeżeli nie musisz pamiętać o virtual to bardzo dobrze... masz czas na inne rzeczy. (UPDATE: wszystkie w sensie, te od implementacji. Metody interfejsu raczej nie powinny być wirtualne).
&lt;/li&gt;&lt;li&gt;Nie używaj wyrażenia warunkowego (to jest to ? : ).  Jeżeli będziesz musiał kiedyś dodać zagnieżdżony warunek, to gdy będziesz potem czytał takie zagnieżdżone instrukcje warunkowe prawdopodobnie stracisz dużo czasu.&lt;/li&gt;&lt;li&gt;Uważaj na niekompletne typu (tylko zapowiedziane). Niektórzy chcą być sprytni i zamiast include dają zapowiedź klasy. To może prowadzić do problemów, więc ja na to uważam. Typowym problemem jest zwolnienie pamięci po obiekcie wskazywanym przez wskaźnik niekompletnego typu. Jeżeli destruktor tego typu jest nietrywialny to nie zostaje wywołany. Tak na prawdę nie musisz nawet wiedzieć o tej sytuacji, jeżeli zawsze dostarczasz kompilatorowi wszystkich informacji jakich potrzebuje - w szczególności jeżeli nie oszukujesz go przez zapowiedź zamiast include.&lt;/li&gt;&lt;li&gt;Jedna klasa - dwa pliki. Zawsze rób dwa pliki - z interfejsem i z implementacją. Nawet jeżeli używasz szablonów. Wyjątkiem mogą być malutkie klasy, o których wiadomo, że nie urosną. Ludzie mają to do siebie, że łatwiej im analizować mniejsze kawałki (dlatego nie jest zalecane uczenie się poprzez wyłożenie mnóstwa książek na biurko - mózg boli na sam widok tej sterty).&lt;/li&gt;&lt;li&gt;Nie używaj delete. Dokładnie tak. Zamiast tego lepiej zastosować inteligentne wskaźniki (np. boost::shared_ptr). Po pierwsze nie trzeba pamiętać o zwolnieniu pamięci - ale trzeba określić kiedy powinna być zwolniona, dla własnej wiadomości. Po drugie delete ma w C++ dwie formy delete i delete [], o czym niestety wszyscy zapominają. Po trzecie delete nie sprawdza czy typ jest kompletnym. Po czwarte przy użyciu delete trzeba się niepotrzebnie zastanawiać czy ktoś nie potrzebuje jeszcze danego zasobu - kolejna możliwość pomyłki i okazja do zmarnowania odrobiny czasu.
&lt;/li&gt;&lt;li&gt;Dokładnie poznaj wybrane inteligentne wskaźniki - często mają wady, które wynikają z samej konstrukcji. Typowym błędem są cykle utworzone przez takie wskaźniki. Wówczas jest wyciek pamięci. Dlatego ten element biblioteki zawsze trzeba dobrze poznać.
&lt;/li&gt;&lt;li&gt;Nie używaj std::auto_ptr. Jeżeli poznałeś dobrze auto_ptr to wiesz dlaczego ;-P Ich polityka własności powoduje, że nietrudno o błąd. W szczególności można kopiować obiekty typu auto_ptr, ale wówczas oryginał traci ważność(!) - jest zerowany. Dodam tylko, że algorytmy STL-a używają kopiowania dość sporo...
&lt;/li&gt;&lt;li&gt;Nie rób using namespace XXX; w zasięgu globalnym. using namespace std; czy using namespace boost; jest typowym błędem. W książkach tak jest dla skrócenia przykładów. Nie należy tego naśladować w prawdziwym kodzie. Nikt nie zna bibliotek doskonale. Nie wiesz co siedzi w przestrzeni nazw. Możesz w ten sposób zmienić znaczenie jakiegoś elementu biblioteki, lub dostać błąd, który bardzo ciężko będzie znaleźć. Są dwa rozwiązania. Użyć selektywnego using (np. using std::cout) lub zawsze dawać pełną kwalifikację (czyli w kodzie piszesz std::cout zamiast cout). Ja wybrałem to drugie rozwiązanie. Pierwsze jest strasznie męczące. Jeżeli korzystasz z dobrego edytora tekstu, to nie będziesz miał problemu z przyjęciem nowej konwencji - edytor może przecież od razu wyłapywać takie "błędy" i poprawiać na bieżąco. Niby oczywiste jest, że przestrzenie nazw nie zostały wprowadzone po to, żeby z nich nie korzystać. Mimo to wyłączanie tego mechanizmu jest na porządku dziennym.
&lt;/li&gt;&lt;li&gt;Pod żadnym pozorem nie rób tego w pliku nagłówkowym. To już by była katastrofa. Zabezpieczenia zostają zdjęte w każdym pliku, w którym zostanie włączony nagłówek z using namespace. Gorzej, że osoba włączająca taki plik nie będzie wiedziała, że ma bajzel z przestrzeniami (bo ktoś inny spierniczył nagłówek).
&lt;/li&gt;&lt;li&gt;Unikaj dziedziczenia wirtualnego. Gdy jest to możliwe, bo pominięcie virtual w tym przypadku może być czasami groźne w skutkach. Chodzi o to, że hierarchia dziedziczenia nie powinna być grafem (który nie jest jednocześnie drzewem). Jeżeli już jest to trzeba bardzo uważać na operator= definiowany automatycznie. Polecam spróbować jak to działa, to zobaczysz dlaczego lepiej tak nie robić.
&lt;/li&gt;&lt;li&gt;Umieszczaj includy STL-a i BOOST-a przed swoimi. To standardowa rada. Dobrze ją stosować, bo narzuca pewien porządek. Jeżeli umieścisz obrzydliwe using namespace std; i właczysz plik STL-a po swoim pliku, to może się okazać, że jakiś element Twojej biblioteki zostanie np. nadpisany przez STL-a. Takiego błędu nikomu nie życzę.
&lt;/li&gt;&lt;li&gt;Nie zmieniaj znaczenia funkcji ani makr (!!!) biblioteki standardowej. Tak się zdarzyło, że zmieniłem kiedyś definicję makra assert ;-) Stało się ono funkcją, która nie wywalała programu, ale zgłaszała wyjątek. Miało to potem opłakane skutki. Pewne kody przestawały działać po dodaniu include. Inne zgłaszały niezgodność typów. Ogólnie było z tym sporo pracy.
&lt;/li&gt;&lt;li&gt;Jeżeli coś może być const to powinno być const. Zawsze warto wybierać najsłabszy środek, który rozwiązuje problem. W ten sposób pomagamy kompilatorowi pomagać nam. Łatwiej mu wykryć błąd już na etapie kompilacji gdy ma do dyspozycji więcej warunków narzuconych na kod. Dodawanie const jest typowym przykładem. Inny przykład to rzutowanie - gdy można użyć static_cast to nie należy używać np. reinterpret_cast czy (broń Boże) rzutowania w stylu C (to są te nawiasy () ).
&lt;/li&gt;&lt;li&gt;Asercje są dobre. Warto używać asercji. Pozwalają sprawdzić czy wywołanie metody faktycznie spełnia zadane warunki wstępne (ang. preconditions). Niejednokrotnie dzięki temu mechanizmowi wykrywałem błędy, które przeszłyby testy modułowe. Typową sztuczką jest assert(warunek &amp;amp;&amp;amp; "Komunikat błędu") aby dostać trochę sensowniejszy komunikat niż tylko numer linii i nazwę pliku z błędem. Niestety nie można odpowiedniego tekstu tworzyć dynamicznie.
&lt;/li&gt;&lt;li&gt;Asercje są złe. Niestety asercje z C++ są złe. W przypadku błędnego warunku przerywają program. Wyobraź sobie, że grasz w grę komputerową i nagle aplikacja zostaje przerwana z komunikatem "assertion failed". Dlatego warto stworzyć własną funkcję asercji, która będzie rzucała wyjątek - przynajmniej można go potem złapać. Dodatkowo można dać dokładniejsze informacje o błędzie (np. że index = 5 a powinno być &amp;lt;4&lt;/li&gt;&lt;li&gt;Jedna klasa - jedna odpowiedzialność. To podstawowa zasada programowania obiektowego. Klasa powinna mieć tylko jeden powód aby się zmienić. Dzięki temu oszczędzisz sporo czasu gdy będzie trzeba faktycznie zmienić klasę. Po prostu - gdy kod jest w jednej klasie to jest bardzo mocno powiązany, a to przekłada się na dużą ilość pracy przy wprowadzaniu zmiany.
&lt;/li&gt;&lt;li&gt;Określaj inwariant klasy. Tego chyba nie trzeba tłumaczyć. Każda klasa ma zbiór warunków, które są zawsze spełnione. Dobrze jest je dodatkowo testować jawnie w kodzie.
&lt;/li&gt;&lt;li&gt;Pozwalaj na to co dozwolone zamiast zakazywać tego co niedozwolone. To już klasyka. Jeżeli piszesz forum i chcesz zakazać pewnych znaków wpisywanych w postach to należy pozwolić na to co jest dozwolone. Jeżeli zaczniesz zakazywać pewnych konstrukcji to zawsze znajdzie się mądrala, który wymyśli sprytniejszy sposób niż Ty.&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;Mam nadzieję, że ta lista mniej więcej pokazuje co mam na myśli. Można by wypisywać te zasady bardzo długo. Każdy ma swoje. Pewnie niektóre są sprzeczne z zasadami innych :-). Na pewno nie zgadzasz się z przynajmniej jedną "moją" zasadą. Warto pamiętać, że to nie są prawa i można je łamać, trzeba tylko wiedzieć dlaczego zostały wprowadzone.

Istotnie jest to, po co robi się takie listy. Otóż skraca to czas pracy (i to bardzo). Przede wszystkim każda z zasad eliminuje przynajmniej jeden błąd jaki mógłbyś popełnić. Usuwanie błędów zazwyczaj jest kosztowne, jeżeli chodzi o zużyty czas.

Dobrze jest, jeżeli zasady są przedstawione w sposób trochę zabawny. Niech przykładem będą dwie zasady pozwalające pisać trochę lepsze GUI. Pierwsza to: im mniej przycisków, tym łatwiej wybrać ten właściwy. Druga jest lepsza: im większy przycisk, tym łatwiej w niego trafić myszą ;-)

Wracając jeszcze do list zasad. Kiedyś napisałem bardzo rozbudowany logger. W jednej z funkcji odpowiedzialnych za podstawianie zmiennych miałem na początku instrukcję warunkową i w przypadku spełnienia warunku następował powrót z funkcji. Dopiero potem następowała właściwa część. Jak na kod spojrzał &lt;a href="http://plonacazyrafa.blogspot.com/"&gt;Tener&lt;/a&gt; to powiedział, że on tak nie robi (z tym ifem) bo często są w takich przypadkach błędy. Około trzy dni później robiłem przegląd kodu i faktycznie był tam błąd związany właśnie z źle klasyfikowany jednym przypadkiem. Właśnie dlatego warto wyrabiać swoje własne zasady "dobrego programowania".

Dlaczego więc taki temat tego postu? Dlaczego niebezpieczne. Otóż to jest jak z małymi dziećmi. Te które są chowane przez rodziców w sterylnych warunkach potem często dużo chorują, bo nie są odporne. Tak samo tu. Po pewnym czasie zapominamy dlaczego taka reguła a nie inna. Nie chodzi o całkowite zaćmienie lecz o to, że nie do końca kojarzymy jak działał "brzydki" mechanizm z języka.

Niedawno kolega zapytał mnie czy można wywołać konstruktor z innego konstruktora tej samej klasy. Po chwili namysłu udało mi się odpowiedzieć poprawnie, mimo że nigdy takiej konstrukcji nie próbowałem zrobić (ani nie widziałem). Poprawna odpowiedź jest dość prosta. Jednak sprawdź swoją z kompilatorem - zauważyłem, że większość źle diagnozuje problem.

Innym razem nie było tak wesoło. Zostałem zapytany o slicing. Nie bezpośrednio lecz przez przykład w C++. W skrócie była klasa A i jej pochodna B. Była zdefiniowana funkcja foo z argumentem typu A. Ta funkcja w main była wywołana z argumentem typu B. Pytanie brzmiało co się stanie? Zastanów się...

Tak!!! To nie ma prawa się skompilować. Typy się nie zgadzają. Ale... przykład trochę za prosty a pytający (który znał odpowiedź) oczekiwał innej odpowiedzi niż "nie skompiluje się". Zacząłem zastanawiać się nad modelem pamięci w C++. Może będzie jakieś rzutowanie? W końcu obiekt klasy B składa się (fizycznie w pamięci) również z obiektu klasy A. To rozumowanie doprowadziło mnie do "mogę się mylić, ale nie wierzę, że to się skompiluje"...

Powiedziano mi, że to się skompiluje (jak później sprawdziłem nawet nie ma żadnych ostrzeżeń). Gdy wracałem do domu zastanawiałem się cały czas jak to możliwe, że to w ogóle działa. Przecież to ewidentny błąd. Kompilator nie powinien tego puścić... Wróciłem do mojej pierwszej myśli, którą miałem zaraz po otrzymaniu pytania (która wydaje się absurdalna).

A może rzutowanie? Ale wtedy klasa A musiałaby mieć konstruktor kopiujący, który jako argument przyjąłby obiekt typu B lub przynajmniej referencję do B. Co wiemy o B? Jest podtypem A. Więc jako referencję A&amp;amp; można przekazać B&amp;amp; (tak samo jak można przekazać B* jako A* - jest to dość typowa sytuacja). Stąd niedaleko do rozwiązania. Ponieważ klasa A nie ma zdefiniowanego konstruktora kopiującego, to jest on wygenerowany automatycznie. Argumentem takiego konstruktora jest referencja do stałej typu A  (to, że nie może być tam przekazywania przez wartość jest powszechnie znaną, dość oczywistą, prawdą). Taki konstruktor kopiujący kopiuje zawartość A. Ponieważ argumentem jest tak na prawdę B, więc część pól B zostaje pominięta (te, które nie są jednocześnie polami A).

Wszystko jasne. Moim zdaniem ten mechanizm w C++ jest... beznadziejny. Nie daję też gwarancji na poprawność mojego rozumowania. Może się mylę. Polecam poszukać na google prawidłowej odpowiedzi.

Po tego typu porażkach zawsze jest czas na analizę błędu i sprawdzenie co poszło nie tak. Co robię źle, że wpadłem w taką pułapkę? Że nigdy się z tym nie spotkałem. Przekonasz się o tym, jeżeli zadeklarujesz w A konstruktor kopiujący... poprzedzony słówkiem explicit.

Swoją drogą, ciekawe jak to jest z operatorem przypisania. Tam powinien być ten sam problem. Tyle, że nie da się go wyeliminować w prosty sposób. Może sytuacja z operatorem= jest na tyle rzadka, że jeszcze jej nie widziałem? Mniejsza z tym. Operator= już ma dwie wady (pierwsza to zachowanie domyślnego operatora= w przypadku wielodziedziczenia wirtualnego).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4148866569426125565?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4148866569426125565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/kiedy-programowanie-profilaktyczne-moe.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4148866569426125565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4148866569426125565'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/kiedy-programowanie-profilaktyczne-moe.html' title='Kiedy programowanie &quot;profilaktyczne&quot; może być złe? Przypadek ze slicingiem'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-3438359397789610084</id><published>2008-11-14T07:00:00.000-08:00</published><updated>2008-11-14T07:00:01.859-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><category scheme='http://www.blogger.com/atom/ns#' term='Teoria informatyki'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Podstawy rachunku lambda na wesoło</title><content type='html'>Znajomość podstaw rachunku lambda to jedna z elementarnych umiejętności każdego programisty. Stanowi Pewne rzeczy po prostu trzeba wiedzieć, aby inne móc wymyślić już samemu. Ale skąd brać tę wiedzę? Można iść np. na studia i się nauczyć o ile taki przedmiot będzie wykładany (btw. pozdrowienia dla TWI). 

Można też poszukać w necie i znaleźć &lt;a href="http://worrydream.com/AlligatorEggs/"&gt;http://worrydream.com/AlligatorEggs/&lt;/a&gt;

Są tam wytłumaczone podstawy rachunku lambda. Są przedstawione w formie "kolorowych krokodyli". Jak ktoś ma problemy ze zrozumieniem rachunku lambda, to polecam.

Oczywiście to jest raczej dla zabawy bo za dużo przydatnej teorii to na tej stronie nie ma ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3438359397789610084?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3438359397789610084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/podstawy-rachunku-lambda-na-wesoo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3438359397789610084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3438359397789610084'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/podstawy-rachunku-lambda-na-wesoo.html' title='Podstawy rachunku lambda na wesoło'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1014487319075127404</id><published>2008-11-13T00:00:00.001-08:00</published><updated>2008-11-13T00:00:02.136-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Od czego jest typename w C++?</title><content type='html'>Po pierwsze typename można użyć przynajmniej w dwóch różnych kontekstach. Pierwszy przypadek to definicja szkieletu klasy. Zaraz zostanie pominięty jako trywialny, bo standard mówi "There is no semantic difference between class and typename in a template-parameter". Chodzi oczywiście o sytuację w której piszemy&lt;pre&gt;template&amp;lt;typename T&amp;gt;&lt;/pre&gt;lub&lt;pre&gt;template&amp;lt;class T&amp;gt;&lt;/pre&gt;Jak już cytowałem, te dwa napisy nie różnią się jeżeli chodzi o semantykę. Na tym zakończę tę część rozważań.

   Istnieje jeszcze jeden kontekst użycia typename (tu już nie da się go zamienić na class). Gdy tworzymy klasę szablonową (lub funkcję, metodę czy cokolwiek "szablonowego") to jest pewna sprawa, którą warto sobie uświadomić. Załóżmy, że mamy taką funkcję&lt;pre&gt;template&amp;lt;typename T&amp;gt;
void foo() {
// tu coś...
}&lt;/pre&gt;Kompilator nie jest w stanie powiedzieć nic o typie T. Tak na prawdę to nie jest to definicja funkcji, ale specyfikacja szablonu takiej definicji. Dopiero gdy za T zostanie podstawiona wartość, to nastąpi wygenerowanie kodu takiej definicji. Czyli funkcje foo&amp;lt;int&amp;gt; i foo&amp;lt;float&amp;gt; to dwie różne funkcje (sic!). 

No dobra, spróbujmy więc zrobić takie coś:&lt;pre&gt;template&amp;lt;typename T&amp;gt;
void foo() {
    T x;
}&lt;/pre&gt;Co się z tym stanie? Kompilator dobrze wie co z tym zrobić, więc taki kod przejdzie bez problemu. Można jednak postawić kompilator w stan "ogłupienia" pisząc&lt;pre&gt;template&amp;lt;typename T&amp;gt;
void foo() {
    T::moj_typ x;
}&lt;/pre&gt;Co z tym zrobić? Czy taki kod jest poprawny? Odpowiedź brzmi "nie wiadomo", albo jak kto woli "to zależy". Istotnie, aby wiedzieć czy ten szablon jest ok, trzeba wiedzieć czy T zawiera definicję typu moj_typ, np. po podstawieniu za T&lt;pre&gt;struct A {
   typedef int moj_typ;
};&lt;/pre&gt;wszystko będzie ok, ale po podstawieniu int za T już powinien zostać zgłoszony błąd. Ewidentnie kompilatorowi brakuje wiedzy na temat T. Słówko kluczowe typename służy właśnie do tego, żeby mu tę wiedzę uzupełnić...&lt;pre&gt;template&amp;lt;typename T&amp;gt;
void foo() {
   typename T::moj_typ x;
}&lt;/pre&gt;i wszystko jest w porządku.

Jeszcze jeden kontekst (podobny) kontekst, to definicja metody w klasie parametryzowanej. Niepoprawne jest&lt;pre&gt;template &amp;lt;typename T&amp;gt;
class A {
public:
    typedef int type;

public:
    type foo();
    
};

template &amp;lt;typename T&amp;gt;
A&amp;lt;T&amp;gt;::type A&amp;lt;T&amp;gt;::foo() {
    
}&lt;/pre&gt;z wiadomych powodów (nie wiadomo czy A&amp;lt;T&amp;gt;::type jest typem). Poprawne jest natomiast&lt;pre&gt;template &amp;lt;typename T&amp;gt;
class A {
public:
    typedef int type;

public:
    type foo();
    
};

template &amp;lt;typename T&amp;gt;
typename A&amp;lt;T&amp;gt;::type A&amp;lt;T&amp;gt;::foo() {
    
}&lt;/pre&gt;Można przyjąć, że zawsze gdy trzeba odwołać się do typu zagnieżdżonego w typie parametrycznym, to należy użyć słówka typename. 

Pozostaje pytanie dlaczego to opisuję? Przecież nie rozwodzę się zazwyczaj nad znaczniem słów kluczowych... Chodzi o to, że w przypadku braku typename można dostać bardzo enigmatyczny komunikat błędu (który często w ogóle nie wspomina o typename). W przypadku szablonów metod jest w porządku. Dla szablonu&lt;pre&gt;template &amp;lt;typename T&amp;gt;
void foo() {
    T::type x;
}&lt;/pre&gt;komunikat błędu brzmi tak (g++ 4.3.1):&lt;pre&gt;error: expected `;' before ‘x’&lt;/pre&gt;Bardzo trafne, nieprawdaż? Z metodami klas jest jeszcze gorzej, dla&lt;pre&gt;template &amp;lt;typename T&amp;gt;
class A {
public:
    typedef int type;

public:
    type foo();
    
};

template &amp;lt;typename T&amp;gt;
A&amp;lt;T&amp;gt;::type A&amp;lt;T&amp;gt;::foo() {
    
}&lt;/pre&gt;&lt;pre&gt;error: expected constructor, destructor, or type conversion before ‘A’&lt;/pre&gt;Taki urok szablonów ;-) Jest to potężny mechanizm i nie brak mu zastosowań. Niestety nie brak też 'drobnych' niedogodności.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1014487319075127404?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1014487319075127404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/od-czego-jest-typename-w-c.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1014487319075127404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1014487319075127404'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/od-czego-jest-typename-w-c.html' title='Od czego jest typename w C++?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-3735366669492874086</id><published>2008-11-10T00:00:00.001-08:00</published><updated>2008-11-10T00:00:01.460-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Zmiana koloru tła w GNU/Emacs</title><content type='html'>Kiedyś pisałem jak dokonać podstawowej konfiguracji. Napisałem wówczas, że można zmienić kolor tła przy pomocy set-background-color. Jest to prawdą, ale nie napisałem jednej rzeczy (którą wiesz, o ile przeczytałeś dokumentację dla set-background-color). Kolor jest ustawiany tylko dla aktualnej ramki. Wadą jest to, że speedbar czy inne otwierane ramki (np. z manualem) mają inny motyw graficzny niż główne okno (co szczególnie razi w przypadku czarnego tła w głównej ramce i domyślnego, białego tła w pozostałych ramkach).

   Rozwiązanie jest bardzo proste. Ustawienia domyślne dla tworzonych ramek są pamiętane na liście default-frame-alist (jakie to odkrywcze...). Jest to lista par, którą można ustawić np. tak:&lt;pre&gt;(setq default-frame-alist
      (append default-frame-alist
       '((background-color . "black")
  (foreground-color . "white")
  (cursor-color     . "white"))))&lt;/pre&gt;Co się tu dzieje? Kod generalnie nie powinien sprawić Ci trudności, o ile czytałeś moje wprowadzenie do GNU/Emacsa (i samego elisp). W skrócie wykorzystujemy funkcję setq aby ustawia nową wartość dla zmiennej default-frame-alist. Aby zdefiniować nową wartość wywołujemy append na liście default-frame-alist (w ten sposób zachowujemy poprzednie informacje np. o wymiarach ramek) oraz na liście trzech par. Każda para jest zdefiniowana przez nazwę-atrybutu i wartość. Nazwy atrybutów są oddzielane od wartości przy pomocy kropki.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3735366669492874086?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3735366669492874086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/zmiana-koloru-ta-w-gnuemacs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3735366669492874086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3735366669492874086'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/zmiana-koloru-ta-w-gnuemacs.html' title='Zmiana koloru tła w GNU/Emacs'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5362820064097234191</id><published>2008-11-08T11:00:00.001-08:00</published><updated>2008-11-08T11:00:01.098-08:00</updated><title type='text'>Zmiany, zmiany, zmiany... jak i po co obserwować blog?</title><content type='html'>Mniej więcej dwa tygodnie temu &lt;a href="http://coldpeer.jogger.pl/"&gt;Coldpeer&lt;/a&gt; swoim komentarzem spowodował, że zacząłem myśleć o wprowadzeniu linków na blog. Minęło trochę czasu, przemyślałem sprawę i wprowadziłem kilka zmian.

Na początek popatrzyłem jakie gadżety udostępnia blogger. Znalazłem listę linków, listę blogów oraz listę osób obserwujących. Ta ostatnia opcja wydaje mi się szczególnie ciekawa, dlatego odpowiedni gadżet jest na górze paska bacznego. Jeżeli będzie cieszył się zainteresowaniem to zostanie na zawsze. 

Polega to na tym, że możesz zaznaczyć, że obserwujesz blog. Dzięki temu link do Twojego bloga będzie dostępny dla innych odwiedzających na liście obserwatorów, a ja będę wiedział, że zdecydowałeś się obserwować blog, co na pewno bardzo mnie ucieszy ;-D

Niestety lista obserwatorów wydaje się być ograniczona do użytku przez osoby mające konto na bloggerze. Dlatego dodałem drugą listę (zarządzaną ręcznie). Jeżeli nie masz bloggera to wystarczy, że wyślesz mi e-mail (lmilewski@gmail.com). Dodam Cię do listy jak tylko dostanę list, wyślę też potwierdzenie. Możesz też umieścić komentarz pod dowolnym postem, w którym zaznaczysz, że chciałbyś, abym Cię dodał do listy.

Od teraz na pasu bocznym jest również lista blogów, które czytam i które polecam subskrybować. Większość jest związana bezpośrednio z programowaniem, ale nie wszystkie... 

Lista linków jest w kolejności rosnącego czasu od ostatniej aktualizacji. Oznacza to, że na początku są najbardziej aktualne notki. Nie oczekuję, że jeżeli Twój blog jest na mojej liście, to mój znajdzie się na Twojej (choć byłoby miło ;-)), bo linki umieszczam zależnie od tego czy sam czytam dany blog. Jak zatem spowodować, żeby Twój blog znalazł się na liście? Wystarczy, że wyślesz mi jego adres - jeżeli blog zainteresuje mnie na tyle, że dodam link do czytnika RSS to link znajdzie się też na pasku bocznym.

Ponieważ nie wszystkie linki, za którymi moim zdaniem warto podążyć, prowadzą do blogów, wprowadziłem też listę linków. Tym razem sortowaną alfabetycznie.

Zastanawiam się też nad poszerzeniem strony, bo wydaje mi się trochę wąska. Szczególnie część, w której umieszczany jest tekst notki. Niby szerokość jest ok, bo to ok 50 znaków na linijkę (czyli tak jak się zaleca). Z drugiej strony trochę dziwnie to wygląda. Jeżeli ktoś chciałby wyrazić swoje zdanie to zachęcam do umieszczenia komentarza lub wysłania do mnie e-maila.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5362820064097234191?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5362820064097234191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/zmiany-zmiany-zmiany-jak-i-po-co.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5362820064097234191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5362820064097234191'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/zmiany-zmiany-zmiany-jak-i-po-co.html' title='Zmiany, zmiany, zmiany... jak i po co obserwować blog?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5570197541032823588</id><published>2008-11-02T01:00:00.003-07:00</published><updated>2008-11-02T06:47:20.583-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Substitution failure is not an error (SFINAE)</title><content type='html'>SFINAE to podstawowa zasada jaką trzeba znać korzystając z mechanizmu szablonów w C++. Jest ona bardzo prosta. Mówi, że przy próbie skorzystania z szablonu, który nie pasuje nie zostanie wygenerowany błąd. Dany szablon zostanie odrzucony i nastąpi próba dopasowania kolejnego. Błąd zostaje wygenerowany gdy żaden z szablonów nie pasuje.

   Dlaczego to jest takie istotne? Wyobraźmy sobie następującą sytuację:&lt;pre&gt;#include &amp;lt;iostream&amp;gt;

int dodaj(int x, int y) {
    return x + y;
}

int main() {
    std::cout &amp;lt;&amp;lt; dodaj(5.0, 3.0) &amp;lt;&amp;lt; "\n";
    return 0;
}
&lt;/pre&gt;Mamy funkcję dodaj, która przyjmuje dwie liczby całkowite i zwraca ich sumę. W funkcji main funkcja jest wywołana z argumentem typu double (zachodzi niejawne rzutowanie do int). Program się kompiluje i po uruchomieniu wypisuje 8. 

Załóżmy, że mamy klasę, która definiuje typ 'type' (jakiś). Chcemy móc dodawać instancje tej klasy, więc piszemy odpowiednią funkcję dodaj.&lt;pre&gt;#include &amp;lt;iostream&amp;gt;

int dodaj(int x, int y) {
    return x + y;
}

template &amp;lt;typename T&amp;gt;
T dodaj(T x, T y) {
    typename T::type Type;
    // tu wykorzystujemy w jakiś sposób Type
    return x + y;
}

int main() {
    std::cout &amp;lt;&amp;lt; dodaj(5.0, 3.0) &amp;lt;&amp;lt; "\n";
    return 0;
}
&lt;/pre&gt;Okazuje się, że program, który wcześniej się kompilował nagle przestał. Nie jest dobrze gdy nowy kod w ten sposób zmienia znaczenie poprzedniego. Dodaliśmy jeden szablon, który nawet nie został użyty, a to zniszczyło poprzedni program.

Co tu się stało? Spróbuj się zastanowić, to nie jest trudne... Tak! Ponieważ nie ma funkcji dodaj(double,double) to jest próba podstawienia do szablonu. Podstawienie się udaje (w końcu na T nie ma żadnych warunków). Następnie w instancji szablonu jest odwołanie do pola T::type, które dla typu double nie istnieje. 

Gdzie leży problem? Autor szablonu chciał dodać nową funkcjonalność do programu (obsługę nowej klasy). Niestety jednocześnie popsuł kod, który wcześniej działał.

Tu przychodzi nam z pomocą zasada SFINAE. Popatrzmy&lt;pre&gt;#include &amp;lt;iostream&amp;gt;

int dodaj(int x, int y) {
    return x + y;
}

template &amp;lt;typename T&amp;gt;
T dodaj(T x, T y, typename T::type* = 0) {
    typename T::type Type;
    return x + y;
}

int main() {
    std::cout &amp;lt;&amp;lt; dodaj(5.0, 3.0) &amp;lt;&amp;lt "\n";
    return 0;
}
&lt;/pre&gt;Dodaliśmy trzeci argument do szkieletu funkcji. Jest on typu T::type*, ma wartość domyślną 0 i nie ma nazwy (bo po co, skoro nie będzie wykorzystany). Chodzi o to, że teraz w definicji szablonu jest uzyte T::type, zatem dodaj(5.0, 3.0), gdzie za T zostanie podstawione double, nie dopasuje się do szablonu. Kompilator milcząco pominie szablon i znajdzie funkcję, przyjmującą liczby całkowite.

Ten mechanizm można bardzo łatwo uogólnić i stworzyć możliwość wyłączania konkretnych szablonów dla wybranych typów danych. W ten sposób została napisana biblioteki enable_if i pochodne (ze zbioru bibliotek boost).

Pytanie kontrolne. Czy można dać typename T::type* jako typ zwracany, zamiast dodatkowego argumentu?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5570197541032823588?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5570197541032823588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/substitue-failure-is-not-error-sfinae.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5570197541032823588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5570197541032823588'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/11/substitue-failure-is-not-error-sfinae.html' title='Substitution failure is not an error (SFINAE)'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-7722002098815376760</id><published>2008-10-30T01:00:00.003-07:00</published><updated>2008-10-30T01:00:01.052-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie czasem'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Parkinson's Shed</title><content type='html'>Tym razem będzie tylko cytat. Opisuje sytuację, która często jest spotykana np. na forach internetowych, albo w przypadku kłótni o edytor czy przeglądarkę internetową albo gdzie postawić gwiazdkę aby w C++ zrobić wskaźnik (przy typie czy przy zmiennej). Chodzi o takie "głupoty", gdzie każdy ma swoje zdanie (bo łatwo je mieć). Jednocześnie w przypadku poważniejszej dyskusji zazwyczaj jest cisza. Ten fenomen jest opisany przez następującą anegdotę:

  Avoid Parkinson's Bicycle Shed. Parkinson described a committee
  formed to oversee design of an early nuclear power plant. There
  were three agenda items - when to have tea, where to put the
  bicycle shed, and how to ensure nuclear safety. Tea was disposed of
  quickly as trivial. Nuclear safety was discussed for only an hour -
  it was so complex, scary, and technical that even among experts few
  felt comfortable with the issues. Endless days were then spent
  discussing construction of the bicycle shed (the parking lot would
  be the modern equivalent) because everyone though they understood
  the issues and felt comfortable discussing them.

Cytat za &lt;a href="http://en.wikipedia.org/wiki/Wikipedia:Avoid_Parkinson's_Bicycle_Shed_Effect "&gt;Wikipedią&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7722002098815376760?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7722002098815376760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/parkinsons-shed.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7722002098815376760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7722002098815376760'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/parkinsons-shed.html' title='Parkinson&apos;s Shed'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1660091244961174453</id><published>2008-10-27T01:00:00.000-07:00</published><updated>2008-10-27T01:00:00.652-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Tablice wielowymiarowe w C++ (boost::multi_array)</title><content type='html'>W C++, jak w każdym porządnym języku, można tworzyć kontenery umożliwiające dostęp do każdego elementu w czasie stałym (po jego numerze). Mowa o tablicach. Tablice mogą być jedno lub wielowymiarowe. Mogą być statycznie lub dynamicznie tworzone. Można też tworzyć (dynamicznie) tablice kwadratowe o nierównej liczbie elementów w wierszach...

  Akurat w C++ tablice są zoptymalizowane pod kątem szybkości działania. Jak zawsze, nie ma nic za darmo. Tym razem szybkość jest osiągnięta kosztem bezpieczeństwa. W C++ istnieje możliwość operowania na pamięci na bardzo niskim poziomie. Aby korzystać z tablic, trzeba tę możliwość wykorzystywać. Innym razem (jeżeli w ogóle) opiszę dlaczego to nie jest dobre. Teraz przejdźmy do tego, jak zastąpić tradycyjne tablice.

  Dla tablic jednowymiarowych, zamiennikiem jest wektor (std::vector z nagłówka vector) z biblioteki standardowej. Zwiększa wygodę korzystania z tablic (nie trzeba już dbać o zarządzanie pamięcią, czy przejmować się limitami - std::vector potrafi sam dopasować swój rozmiar). Co ważne - zwiększone jest również bezpieczeństwo (należy jednak pamiętać o wykorzystaniu metody at, zamiast przeciążonego operatora []). Koszt korzystania z wektorów jest raczej niski - zwiększone zapotrzebowanie na pamięć (wektor potrafi zarezerwować do dwóch razy więcej pamięci, niż potrzebuje), oraz zmniejszona szybkość działania. Oba te czynniki nie mają znaczenia, bo pamięć jest tania, a narzut związany ze zwiększaniem tablicy (elementy są wtedy kopiowane) amortyzuje się. Koszt zamortyzowany jest tym dokładniejszym oszacowaniem dla struktur danych. Oznacza to, że narzut czasowy nie jest duży - sprawdź sam! Co więcej jeżeli nie wykorzystujesz możliwości "samorozszerzania" wektora, to nie ma w ogóle narzutu czasowego. Wektory są, w praktyce, prawie tak samo wydajne jak zwykłe tablice - przetestuj.

Interfejsu wektorów nie będę opisywał, bo docelowo chciałem opisać tablice wielowymiarowe. Odpowiednią dokumentację można znaleźć np. na &lt;a href="http://www.cplusplus.com/reference/stl/vector/"&gt;www.cplusplus.com&lt;/a&gt;.

  Niestety wygoda wektorów kończy się na jednym wymiarze. Tablice dwuwymiarowe tworzy się niewygodnie. Czyli wracamy do punktu wyjścia. Tę lukę dostrzeżono już wcześniej. Dlatego powstała biblioteka boost::multi_array. Tworzenie tablicy wielowymiarowej jest bardzo proste. Dla przykładu tablicę trójwymiarową tworzy się tak:&lt;pre&gt;boost::multi_array&amp;lt;double, 3&amp;gt; tablica(boost::extents[8][2][15]);&lt;/pre&gt;Przykład zaczerpnięty z dokumentacji&lt;pre&gt;
#include "boost/multi_array.hpp"
#include &amp;lt;cassert&amp;gt;

int main () {
 // Create a 3D array that is 3 x 4 x 2
 typedef boost::multi_array&amp;lt;double, 3&amp;gt; array_type;
 typedef array_type::index index;
 array_type A(boost::extents[3][4][2]);

 // Assign values to the elements
 int values = 0;
 for(index i = 0; i != 3; ++i)
   for(index j = 0; j != 4; ++j)
     for(index k = 0; k != 2; ++k)
       A[i][j][k] = values++;

 // Verify values
 int verify = 0;
 for(index i = 0; i != 3; ++i)
   for(index j = 0; j != 4; ++j)
     for(index k = 0; k != 2; ++k)
       assert(A[i][j][k] == verify++);

 return 0;
}&lt;/pre&gt;Do tablic wielowymiarowych istnieje też drugi interfejs na specyfikowanie wymiarów. Jest także inny interfejs do pobierania elementu o zadanej pozycji. Moim zdaniem te interfejsy są mniej wygodne. Więcej w &lt;a href="http://www.boost.org/doc/libs/1_36_0/libs/multi_array/doc/index.html"&gt;dokumentacji&lt;/a&gt;.

Bardzo ciekawą możliwością jest tworzenie widoków dla danej tablicy. Mając tablicę dwuwymiarową np.&lt;pre&gt;1  2  3  4
5  6  7  8
9 10 11 12&lt;/pre&gt;Można utworzyć widok na dowolną jej podtablicę. Można np. zdefiniować widok, tablicy zawierającej co drugą kolumnę itp. Ułatwia to operowanie na tablicach wielowymiarowych. Co ciekawe, można w ten sposób nawet zmienić liczbę wymiarów tablicy (sic). Jest wiele sposobów tworzenia widoków (specyfikowania przedziałów). Można np. w ten sposób (zakładając, że tab234 to tablica o wymiarach 2x3x4):&lt;pre&gt;    typedef boost::multi_array_types::index_range range;
   array_type::array_view&amp;lt;3&amp;gt;::type widok24 =
       tab234[ boost::indices[range(0,2)][range(1,3)][range(0,4,2)] ];&lt;/pre&gt;W ten sposób pierwszy wymiar pozostaje bez zmian, w drugim pomijamy pierwszy element, a w ostatnim wybieramy co drugi element, zaczynając od zera. Przypomina to wycinanie podmacierzy, o którym wspominałem &lt;a href="http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba-cd.html"&gt;we wprowadzeniu do Matlaba &lt;/a&gt;.

range, można wykorzystać również w inny sposób - do zadawania wymiarów tablicy (w konstruktorze). Zamiast pisać&lt;pre&gt;
boost::multi_array&amp;lt;double, 3&amp;gt; tablica(boost::extents[8][2][15]);
&lt;/pre&gt;można napisać&lt;pre&gt;boost::multi_array&amp;lt;double, 3&amp;gt; tablica(boost::extents[ range(0, 8) ][ range(0, 2) ][ range(0, 15) ]);&lt;/pre&gt;Po co wydłużać kod? Nie wiem, ale ponieważ jawnie podajemy dolną granicę, to nic nie stoi na przeszkodzie aby indeksować od 1 czy od -10. Można zatem dowolnie zmieniać numer pierwszego elementu (osobno dla każdego z wymiarów).

Można również zmienić wymiary tablicy już utworzonej. Wystarczy w tym celu wywołać metodę resize, jako argument podając boost::extents z odpowiednimi wymiarami (jak w konstruktorze).

Zachęcam do przeczytania całej &lt;a href="http://www.boost.org/doc/libs/1_36_0/libs/multi_array/doc/index.html"&gt;dokumentacji&lt;/a&gt; (jest krótka), a w szczególności do zrozumienia przykładów tam podanych.

Od dzisiaj koniec z tablicami z czasów C :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1660091244961174453?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1660091244961174453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/tablice-wielowymiarowe-w-c.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1660091244961174453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1660091244961174453'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/tablice-wielowymiarowe-w-c.html' title='Tablice wielowymiarowe w C++ (boost::multi_array)'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5181504585185684995</id><published>2008-10-24T00:00:00.000-07:00</published><updated>2008-10-24T00:00:01.174-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Jak efektywnie korzystać z GNU/Emacs? Praca z wieloma plikami</title><content type='html'>Wiele osób korzysta z GNU/Emacs tak jak z każdego innego edytora. Za każdym razem gdy mają do otwarcia plik uruchamiają nową instancję edytora. GNU/Emacs jest przystosowany do jednoczesnej pracy z wieloma plikami. Jest to cecha, która go odróżnia. Aby efektywnie wykorzystać możliwości edytora (np. autouzupełnianie) trzeba nauczyć się (a raczej wyrobić sobie nawyk) korzystania z jednej instancji programu.

   Zawsze przy starcie systemu warto uruchomić kopię programu GNU/Emacs. Jeżeli nie jest od razu potrzebna, to można zostawić ją np. na pierwszym pulpicie wirtualnym, a pracę rozpocząć na innym. Jeżeli organizujesz swój czas np. z wykorzystaniem org-mode, to pierwszą aplikacją jakiej użyjesz będzie właśnie GNU/Emacs. Dlatego warto ustawić ten edytor jako jeden z programów startujących razem z system.
   
   Kolejny krok to wykorzystanie polecenia emacsclient zamiast emacs, do edycji plików. Powoduje to, niemal błyskawiczne, otwarcie pliku w programie GNU/Emacs, który działa w trybie serwera. W tym celu możesz utworzyć odpowiedni alias (jeżeli korzystasz z powłoki) lub ustawić emacsclient jako domyślny program dla plików txt (i innych, które Cię interesują) w środowisku graficznym.

   Aby to zadziałało musi działać GNU/Emacs w trybie serwera. Można w odpowiednim oknie wydać polecenie server-start. Ponieważ i tak wykorzystujesz tylko jedno okno na raz, to możesz tę komendę wrzucić do pliku konfiguracyjnego .emacs:&lt;pre&gt;(server-start)&lt;/pre&gt;Ponoć może się tak zdarzyć, że po dłuższym czasie pracy, będzie zbyt dużo otwartych buforów - może być trudno odnaleźć "ten jeden". Warto wtedy pamiętać o komendzie kill-some-buffers.
   
   Przez pierwsze kilka dni trudno się przyzwyczaić do nowego trybu pracy. Po tym okresie wykorzystanie tylko jednego okna programu staje się rzeczą naturalną i nie chce się wracać do pracy z wieloma instancjami programu.

   A co jeżeli trzeba otworzyć plik w nowym oknie? Nie chodzi tu o sytuację, w której wystarcza podzielić okno na dwie czy trzy części. Wtedy wystarczy skorzystać z find-file-other-frame (C-x5f). Jest to jednak tylko inny sposób wyświetlania. Przy wyłączeniu nowej ramki nie traci się informacja o odpowiednim buforze, uzupełnianiu itp. Edytor może zbierać różne, potrzebne do ułatwiania użytkownikowi pracy, informacje na podstawie otwieranych plików. To nie jest możliwe, gdy za każdym razem jest włączana nowa instancja GNU/Emacs&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5181504585185684995?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5181504585185684995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/jak-efektywnie-korzysta-z-gnuemacs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5181504585185684995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5181504585185684995'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/jak-efektywnie-korzysta-z-gnuemacs.html' title='Jak efektywnie korzystać z GNU/Emacs? Praca z wieloma plikami'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-1941047791931465847</id><published>2008-10-21T00:00:00.001-07:00</published><updated>2008-10-21T00:00:01.625-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie czasem'/><title type='text'>Prawo parkinsona - o szacowaniu czasu</title><content type='html'>Jak powszechnie wiadomo większość projektów informatycznych nie mieści się w czasie lub budżecie. Można wymieniać powody dlaczego tak się dzieje, ale jedno jest pewne - nie znamy jeszcze wszystkich. Gdyby było inaczej, gdybyśmy znali dobrze wroga, to można by skutecznie przeciwdziałać porażkom. 

   W literaturze można spotkać się ze stwierdzeniem, że porażki związane z niedokończeniem projektu w czasie lub budżecie biorą się ze złego zarządzania, a nie (jak mogłoby się wydawać) z braku umiejętności czy zaangażowania programistów. Przenosząc to na małe, prywatne projekty, można wysnuć wniosek, że należy dokładnie i sumiennie planować, a potem brać się za kodowanie. Warto też zauważyć, że w przypadku takich projektów walczymy o ukończenie, a dopiero potem o ukończenie w czasie (o budżecie raczej nie ma mowy).

   Czas należy planować sumiennie, a wynik powinien być zbliżony do czasu, który faktycznie poświęcimy. Często można spotkać się z "oszukiwaniem" przy szacowaniu czasu. Zazwyczaj ludzie, którzy to robią, mają ku temu dobre uzasadnienie (ich zdaniem). To oszukiwanie polega na podliczeniu potrzebnego czasu, a następnie pomnożeniu go przez jakiś czynnik (najczęściej 2). 

   Ludzie tak uzasadniający swoje szacunki zazwyczaj nie zdają sobie sprawy z prawa Parkinsona.

   Głosi ono, że praca przeciąga się tak, aby wypełnić dostępny limit. Jest tak, bo każdy z nas chce, aby własne szacunki okazywały się dobre. Właśnie dlatego podświadomie rozciągamy czas pracy przy projekcie do czasu szacowanego. Oczywiście nie działa to w drugą stronę - oszacowując poniżej wymaganego czasu na pewno nie przyspieszymy realicji, a za to wprowadzimy stres.

   Nie zastanawiało Cię nigdy, dlaczego zawsze udaje się coś zrobić na ostatnią chwilę? Wysłać pracę na Compo (na WSoC przyszło 7 prac ostatniego dnia, po przypomnieniu przez Rega, że konkurs się kończy, wcześniej były 2), skończyć własny projekt, czy nauczyć się do egzaminu.

   Jak szacować czas? XP zaleca szacowanie idealnych dni pracy (i stosowanie analizy bottom-up). Taka ilość daje w przybliżeniu czas jaki byśmy potrzebowali gdyby wszystko poszło ok. Dodaje się też czas na zapoznanie się z bibliotekami, których się nie zna (w ten sam sposób), czy ogólnie mechanizmami, których trzeba się nauczyć, aby ukończyć projekt. Jest to dodatkowy czas na zapoznanie się z narzędziami, a efekt tej pracy jest wyrzucany (można to porównać do prototypowania, opisywanego w książce "Pragmatyczny programista").

   Jak wytłumaczyć ewentualnemu klientowi opóźnienia? To proste, należy się z nim często spotykać i pokazywać co już jest zrobione, a prace wykonywać wg. ustalonych priorytetów. Wtedy na bieżąco widać opóźnienia, a przecież czas szacowany jest tylko szacowany. Nikt nie ma prawa oczekiwać, że będzie to dokładny czas realizacji. 

   Tego nauczyłem się pisząc ostatni projekt. Pisałem przez jeden semestr akademicki. Napisałem zbiór 4 bibliotek. Są rozbudowane i dobrze się z nich korzysta, ale... miało być ich 10+, a te 4 nie miały najwyższego priorytetu (wg. mnie, ale się nad tym nie zastanawiałem na początku). Mimo, że pisałem regularnie (wyznaczyłem czas na pisanie), to i tak praca się zbyt rozciągnęła. Nie w tym sensie, że napisałem tyle samo w dłuższym czasie, ale przez tzw. "Feature Crawl" napisałem nie to co, sam chciałem zrobić. Naruszyłem tym samym zasadę YAGNI, projektując bibliotekę, tak żeby była jak najbardziej "cool", a nie tak żeby miała to czego potrzebuję i tylko to.

Ale i tak projekt mi się podoba ;-) Przede wszystkim wyciągnąłem wiele wniosków na przyszłość. Jednym z nich jest ta notka, choć w dużej mierze opiera się na książkach jakie przeczytałem, po tej "porażce".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1941047791931465847?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1941047791931465847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/prawo-parkinsona-o-szacowaniu-czasu.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1941047791931465847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1941047791931465847'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/prawo-parkinsona-o-szacowaniu-czasu.html' title='Prawo parkinsona - o szacowaniu czasu'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3350062175076747803</id><published>2008-10-18T00:00:00.001-07:00</published><updated>2008-10-18T00:00:01.087-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Kalkulator w GNU/Emacs</title><content type='html'>GNU/Emacs udostępnia potężny, programowalny kalkulator (M-x calc). Jest to skomplikowane narzędzie, nadające się do wykonywania trudnych obliczeń. &lt;a href="http://www.xemacs.org/Documentation/packages/html/calc.html"&gt;Tutorial&lt;/a&gt;. Niestety razem z potęgą przychodzi sporo wiedzy jaką trzeba sobie przyswoić aby z niego korzystać. Podczas programowania zazwyczaj trudno obejść się bez kalkulatora, ale z drugiej strony nie potrzeba takiej armaty. Dlatego w GNU/Emacs jest drugi, mniej skomplikowany, program kalkulatora.

 Dostęp do tego narzędzia jest poprzez polecenie calculator (M-x calculator). Pojawia się małe okienko ze znakiem zachęty (calc ==&gt;). W tym momencie można zacząć wpisywać formułę. Formułę można wpisywać w postaci infiksowej (czyli takiej jaka jest nauczana w szkole podstawowej). Można wykorzystać notację naukową. 2e3 oznacza tyle co 2*10^3 czyli 2000. Po wprowadzeniu wyrażenia można nacisnąć enter lub = aby poznać wynik. Wynik można również uzyskać naciskając spację, ale wtedy jest on dodatkowo dodawany wynik na listę wyników. Można przeglądać listę strzałkami (góra / dół), ewentualnie skrótami C-p i C-n. Tę listę można wyczyścić przy pomoc C-del.

 Wyrażenie zazwyczaj jest na bieżąco obliczane (tyle ile się da). Czyli jeżeli napisze się 2 * 3 + to pojawi się 6 +. Jeżeli jednak napisze się 1 + 3 * to wyrażenie nie zostanie nawet częściowo obliczone (bo mnożenie ma wyższy priorytet od dodawania).

 Zazwyczaj trzeba obliczyć wartość pewnego, prostego wyrażenia. Aby efektywnie wykonać tę operację dobrze jest podbindować kalkulator pod jakiś klawisz. Po włączeniu kalkulator wpisujemy wyrażenie i kończymy C-RET. Dzięki temu kalkulator zostaje zamknięty, a w schowku jest wynik.

 W przypadku tego kalkulatora nie trzeba nawet pamiętać, że po wciśnięciu ? pojawia się pomoc. Jest to napisane zaraz po włączeniu tego programu.

 Kalkulator udostępnia podstawowe operacje:
&lt;ul&gt;&lt;li&gt;  Dodawanie (+)&lt;/li&gt;&lt;li&gt;   Odejmowanie (-)&lt;/li&gt;&lt;li&gt;   Mnożenie (*)&lt;/li&gt;&lt;li&gt;   Dzielenie (/)&lt;/li&gt;&lt;li&gt;   Dzielenie całkowitoliczbowe (\)&lt;/li&gt;&lt;li&gt;   Resztę z dzielenia (%)&lt;/li&gt;&lt;li&gt;   Zmianę znaku pierwszego wyrażenia z lewej od kursora (_)&lt;/li&gt;&lt;li&gt;   Odwrócenie liczby (;)&lt;/li&gt;&lt;li&gt;   Podnoszenie do potęgi (^)&lt;/li&gt;&lt;li&gt;   Logarytm (L) (np. 16 L 2 jest równe 4)&lt;/li&gt;&lt;li&gt;   Pierwiastek kwadratowy (Q)&lt;/li&gt;&lt;li&gt;   Silnię (!)&lt;/li&gt;&lt;li&gt;   Sinus (S) (kąt podaje się w radianach, ale można to zmienić wciskając D)&lt;/li&gt;&lt;li&gt;   Cosinus (C)&lt;/li&gt;&lt;li&gt;   Tangens (T)&lt;/li&gt;&lt;li&gt;   Logiczne or (|)&lt;/li&gt;&lt;li&gt;   Logiczne xor (#)&lt;/li&gt;&lt;li&gt;   Logiczne and (&amp;amp;)&lt;/li&gt;&lt;li&gt;   Logiczne not (~)&lt;/li&gt;&lt;/ul&gt;      Można również pracować w różnych systemach liczbowych. Dwójkowym (B), ósemkowym (O), szesnastkowym (H lub X), dziesiętnym (D).

   Kalkulator jest również wyposażony w pamięć (rejestry). Generalnie nazwa rejestru to dowolny znak (niekoniecznie alfanumeryczny), zatem jest ich sporo (np. a oraz A to dwa różne rejestry). Aby zapisać aktualną wartość w rejestrze wystarczy nacisnąć s (jak set) i podać nazwę rejestru. Oczywiście g (od get) + nazwa rejestru wkleja wartość z rejestru.

   Do kasowania wykorzystuje się klawisz delete. Jednokrotne wciśnięcie kasuje ostatnią liczbę, dwukrotne - całe wyrażenie.

   Zalety korzystania z kalkulatora w GNU/Emacs (zamiast wykorzystania zewnętrznego narzędzia) to przede wszystkim:
&lt;ol&gt;&lt;li&gt;   szybko się włącza (natychmiast po wpisaniu komendy widać prompt)&lt;/li&gt;&lt;li&gt;   szybko się wyłącza (nie marnujemy czasu na przełączanie się między programami)&lt;/li&gt;&lt;li&gt;   dwa powyższe powodują, że tzw. context switch time jest zerowy&lt;/li&gt;&lt;li&gt;   wyrażenia wprowadza się w przyjemnej, infiksowej notacji&lt;/li&gt;&lt;li&gt;   integracja ze środowiskiem (przenoszenie do schowka itp.)&lt;/li&gt;&lt;li&gt;   znacznie większe możliwości niż standardowe kalkulatory (pod Windowsem calc, pod Linuksem kcalc (KDE))&lt;/li&gt;&lt;li&gt;   jednocześnie większa ergonomia pracy (koniec bezmyślnego i powolnego wyklikiwania myszą działań)&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3350062175076747803?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3350062175076747803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/kalkulator-w-gnuemacs.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3350062175076747803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3350062175076747803'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/kalkulator-w-gnuemacs.html' title='Kalkulator w GNU/Emacs'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1837994741565764128</id><published>2008-10-15T00:00:00.003-07:00</published><updated>2008-10-15T00:00:01.460-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Matlab'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Wprowadzenie do Matlaba, cz. 3</title><content type='html'>Tym razem będzie o sterowaniu przepływem. Mówi się, że tym, co czyni komputery tak potężnymi jest możliwość wielokrotnego, szybkiego powtarzania czynności. Dlatego zaczniemy od pętli.

Na początku zawsze zaczyna się pętlę (słówko for lub while). Następnie jest ciało pętli. Na końcu jest słówko end. Matlab działa tak, że w momencie dojścia do end wraca do pętli. Następuje ponowne sprawdzenie warunków i ewentualny drugi przebieg pętli. 

Mamy do dyspozycji pętle for i while.&lt;pre&gt;for i=[1,2,3,4]
  y(i) = i^2;
end
&lt;/pre&gt;Przy czym przedział po i= można zadać przy pomocy notacji z dwukropkiem. Oczywiście lepiej napisać&lt;pre&gt;y = [1:4].^2
&lt;/pre&gt;Pętla while jest jeszcze prostsza
&lt;pre&gt;i = 1
while (i &lt; 10)
  i
  i = i + 1;
end
&lt;/pre&gt;Ten kod wypisze liczby z zakresu 1 do 9. Zwróć uwagę na średnik po i+1. Gdyby go zabrakło to w obszarze roboczym pojawiałyby się wartości obliczane w tej linii.

Ponieważ zazwyczaj nie chcemy aby nasze programy były liniowe (chcemy, aby zależały w jakiś sensowny sposób od wejścia), a nie zawsze pętle są konieczne, to wprowadzono instrukcję warunkową.
&lt;pre&gt;a = 1;
b = -4;
c = 2;
if (a == 0)
  if (b == 0)   
    disp "brak rozwiązań";
  else
    disp "jedno rozwiązanie: ";
    disp (num2str(-c/b));
  end
else
  delta = b^2 - 4 * a * c;
  if (delta &lt; 0)
    disp "brak rozwiązań";
  elseif(delta == 0)
    disp "jedno rozwiązanie: ";
    disp (-b/(2*a));
  else
    disp "dwa rozwiązania: ";
    disp ((-b + sqrt(delta))/(2*a));
    disp ((-b - sqrt(delta))/(2*a));    
  end
end
&lt;/pre&gt;Ostatnią sprawą związaną z przepływem danych są funkcje. Jest taka miła zasada: DRY, czyli don't repeat yourself. Ale co zrobić gdy potrzeba dwa razy wykonać tę samą operację? Wydzielić odpowiedni kod w postaci funkcji (procedury), a następnie wywołać tę funkcję.
Tak wygląda wydzielanie kodu do funkcji (tutaj liczymy średnią z ciągu x):&lt;pre&gt;function [mean]=avg(x)
  mean = sum(x)/length(x);
end
&lt;/pre&gt;a użycie funkcji:
&lt;pre&gt;srednia = avg([1,2,3,4,5,6,7])
&lt;/pre&gt;Deklaracja zaczyna się od słowa 'function' następnie jest lista wartości zwracanych (w naszym wypadku jest to [mean], zatem mamy jedną wartość zwracaną). Do tych zmiennych należy przypisać wartości, które mają zostać zwrócone. W przypadku funkcji jednoargumentowej można pominąć nawiasy [ ] wokół wartości zwracanej.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1837994741565764128?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1837994741565764128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba-cz-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1837994741565764128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1837994741565764128'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba-cz-3.html' title='Wprowadzenie do Matlaba, cz. 3'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-6212303618882352488</id><published>2008-10-12T00:00:00.004-07:00</published><updated>2008-10-12T00:00:00.898-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Matlab'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Wprowadzenie do Matlaba c.d.</title><content type='html'>Ten post jest kontynuacją &lt;a href="http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba.html"&gt;mojego wprowadzenia do Matlaba&lt;/a&gt;. Dlatego jeżeli nie przeczytałeś poprzedniej części to możesz to zrobić teraz (ale nie jest to wymagane, bo opisałem tam tylko podstawy podstaw).

Przypomnę (i uzupełnię), że najważniejsze komendy (jedyne, które trzeba zapamiętać) to:
&lt;ul&gt;&lt;li&gt;help foo  Wyświetla pomoc dla funkcji foo. W szczególności help help jest dobrym pomysłem&lt;/li&gt;&lt;li&gt;edit file Wyświetla zawartość pliku file&lt;/li&gt;&lt;li&gt;who Podaje nazwy zmiennych w pamięci roboczej&lt;/li&gt;&lt;li&gt;whos J/W + dodatkowe informacje o typach i rozmiarach tych zmiennych&lt;/li&gt;&lt;li&gt;doc foo Szczegółowa dokumentacja dla funkcji foo&lt;/li&gt;&lt;li&gt;lookfor nazwa Wyszukuje dokumentacji zawierającej słowo nazwa&lt;/li&gt;&lt;li&gt;which foo Gdzie znajduje się definicja funkcji foo&lt;/li&gt;&lt;/ul&gt;W edycji przydatne są komendy:
&lt;ul&gt;&lt;li&gt;clc Czyści ekran&lt;/li&gt;&lt;li&gt;clf Czyści aktualne okno graficzne&lt;/li&gt;&lt;li&gt;quit Wyjście&lt;/li&gt;&lt;/ul&gt;Podczas pisania instrukcje można kończyć średnikiem (;), wielokropkiem (...) lub tylko znakiem nowej linii. Każdy z tych sposobów ma swoje znaczenie. Zakończenie średnikiem spowoduje wykonanie instrukcji (np. przypisania). Zakończenie tylko znakiem końca linii spowoduje wykonanie instrukcji i wypisanie wyniku w miejscu pracy. Wielokropek oznacza, że komenda jest kontynuowana w kolejnej linii.&lt;pre&gt;A = [1 2 3 ...
     4 5 6];&lt;/pre&gt;Dzięki temu można wprowadzać macierze w 'graficzny' sposób.&lt;pre&gt;A = [1 2 3;...
     4 5 6;...
     7 8 9]&lt;/pre&gt;Wielokropek działa w przypadku ogólnym, natomiast dla macierzy można uzyskać identyczny efekt pisząc tak:&lt;pre&gt;A = [1 2 3
     4 5 6
     7 8 9]&lt;/pre&gt;Kolejną rzeczą, którą łatwo osiągnąć w Matlabie, są przedziały. Aby zdefiniować wektor liczb [1 2 3 4 5] wystarczy napisać [1:5]. Można też, oprócz pierwszego i ostatniego elementu, zadać także krok. Aby zdefiniować wektor [1 4 7 10 13 16 19 22 25 28 31] wystarczy napisać [1:3:31]. 

W Matlabie oprócz operacji na całych macierzach, są zdefiniowane operacje na pojedynczych elementach. Są to operacje wykonywane 'elementwise'. Odpowiednie operatory to .* ./ .^2 
Np. [1:5].^2 jest równe [1 4 9 16 25].

Przedziały mają jeszcze jedno zastosowanie. Można przy ich pomocy 'wycinać' kawałki macierzy. Zdefiniujmy macierz:&lt;pre&gt;A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16 ]&lt;/pre&gt;Wówczas możemy wyciąć jej podmacierz w następujący sposób:&lt;pre&gt;B = A(2:3, 1:2:4)&lt;/pre&gt;Podaliśmy dwa przedziały 2:3, który jest rozwijany do [2,3]. Oznacza to, że wycinamy z macierzy A wiersze 2 i 3. Z kolumn wybieramy te o numerach [1 3] (jest to zadane przez przedział 1:2:4). Zatem wynik wygląda tak:&lt;pre&gt;B =

    5    7
    9   11&lt;/pre&gt;Dodatkowo jest specjalna wartość end, która oznacza ostatni element. Aby wyrzucić ostatnią kolumnę z macierzy A można wykorzystać właśnie end:&lt;pre&gt;A = A(:, 1:end-1)&lt;/pre&gt;Widać tu nowy element - sam dwukropek. Oznacza on "wszystko". W naszym przypadku "wszystkie wiersze". Z kolumn wybieramy od 1 do przedostatniej (end-1).

Skoro można dzielić macierze, to można je również łączyć. Jest to kolejna operacja wykonywana w Matlabie w wyjątkowo prosty sposób. Wystarczy aby macierze A i B miały odpowiednie wymiary (tak aby wynik miał sens), aby można było napisać [A,B] co oznacza konkatenację macierzy - czyli napisanie macierzy B po prawej stronie macierzy A i w ten sposób otrzymanie nowej macierzy.&lt;pre&gt;A = [1 2
     3 4]

B = [5 6
     7 8]

[A,B]
ans =

   1   2   5   6
   3   4   7   8

[A,
 B]
ans =

   1   2
   3   4
   5   6
   7   8&lt;/pre&gt;Można również Stworzyć macierz zawierającą m x n kopii macierzy&lt;pre&gt;repmat(A,m,n)&lt;/pre&gt;Na zakończenie napiszę jeszcze, że aby załadować plik z kodem (zinterpretować go) wystarczy wpisać jego nazwę. Zakładając, że na ścieżce z plikami programu Matlab jest plik dane.m wystarczy napisać&lt;pre&gt;dane&lt;/pre&gt;aby Matlab zinterpretował plik dane.m&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6212303618882352488?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6212303618882352488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba-cd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6212303618882352488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6212303618882352488'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba-cd.html' title='Wprowadzenie do Matlaba c.d.'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-6155017611302867096</id><published>2008-10-09T07:00:00.003-07:00</published><updated>2008-10-11T10:41:07.659-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Matlab'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Wprowadzenie do Matlaba</title><content type='html'>Ostatnio zostałem zmuszony do nauczenia się podstaw Matlaba. W pierwszej chwili bardzo mi się to nie podobało, ale po paru minutach z tym środowiskiem zacząłem się do niego przekonywać. To co napiszę powstało z moich notatek, dlatego proszę nie spodziewać się zbyt wiele. Jest to na prawdę elementarne wprowadzenie. Jeżeli jakiś znawca tego środowiska (języka?) znajdzie błąd to prosiłbym o komentarz (lub mail) - poprawię najszybciej jak się da.

W Matlabie jest bardzo przydatna pomoc (Help/Demos).

Nie bez znaczenia jest to, że Matlab nie należy do tanich narzędzi (nie udało mi się znaleźć żadnej darmowej wersji). Na uniwersytecie w pracowniach oczywiście jest odpowiednie, legalne oprogramowanie. Gorzej gdy trzeba coś zrobić w domu. Na szczęście istnieje Octave http://www.gnu.org/software/octave/. Z pewnością nie można powiedzieć, że dorównuje możliwościami Matlabowi (np. liczy mniej dokładnie), ale jest zgodność między tymi środowiskami, jeżeli chodzi o składnię konkretną. To jest najważniejsze&lt;pre&gt;ai octave3.0-emacsen octave3.0-doc octave3.0 octave3.0-info octave3.0-htmldoc&lt;/pre&gt;Instalacja octave i trybu octave dla GNU/Emacs przebiega raczej bez problemów. Teraz włączamy GNU/Emacs i wydajemy komendy octave-mode oraz run-octave. Okno zostanie podzielone na dwie części.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_P9Sn6SUv534/SO4MkuJyY3I/AAAAAAAAABE/v-ORR29lK7w/s1600-h/octave_run.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/SO4MkuJyY3I/AAAAAAAAABE/v-ORR29lK7w/s320/octave_run.jpg" alt="" id="BLOGGER_PHOTO_ID_5255151640191853426" border="0" /&gt;&lt;/a&gt;
Piszemy w górnej części. Warto znać komendy octave-send-X, gdzie X to block, line, region lub defun. Wszystkie są podbindowane pod (dość niewygodne) skróty C-c TAB X, gdzie x to pierwsza litera ostatniej części komendy. Tyle o samym środowisku. Teraz przejdziemy do samego języka.

Podstawowe komendy to: help, whos i edit.
&lt;ul&gt;&lt;li&gt;help foo wyświetla pomoc dla funkcji foo (np. help inv)&lt;/li&gt;&lt;li&gt;edit foo pozwala edytować kod funkcji. Jest to przydatne gdy chce się poznać jak działa dana funkcja&lt;/li&gt;&lt;li&gt;whos foo zwraca informacje o tym, czym jest foo (np. dla zmiennej m.in. wyświetli jej wartość)&lt;/li&gt;&lt;/ul&gt;Komentarze zaczynają się od znaku % (procent).

W Matlabie podstawowym typem jest Macierz. Stwórzmy macierz o nazwie A mającą dwa wiersze i trzy kolumny&lt;pre&gt;A = [1 2 3; 4 5 6]&lt;/pre&gt;Linię zatwierdza się C-c TAB l (w GNU/Emacs).

Na macierzach można robić wiele ciekawych operacji:
&lt;ul&gt;&lt;li&gt;A' jest transpozycją A,&lt;/li&gt;&lt;li&gt;A * B jest iloczynem macierzy&lt;/li&gt;&lt;li&gt;A .* B jest iloczynem 'element po elemencie'&lt;/li&gt;&lt;li&gt;A + x dodaje do każdego elementu macierzy x&lt;/li&gt;&lt;li&gt;inv(A) to odwrotność A. Oczywiście A musi być macierzą kwadratową (dobrze aby jej wyznacznik był niezerowy)&lt;/li&gt;&lt;li&gt;svd(A) to rozkład SVD (singular value decomposition) macierzy A&lt;/li&gt;&lt;li&gt;eig(A) to wartości własne macierzy (i odpowienie wektory) A&lt;/li&gt;&lt;li&gt;poly(A) to wektor współczynników wielomianu charakterystycznego&lt;/li&gt;&lt;li&gt; det(lI - A), gdzie I to macierz jednostkowa, a l to argument wielomianu.&lt;/li&gt;&lt;/ul&gt;W łatwy sposób można też tworzyć macierze 'specjalne'
&lt;ul&gt;&lt;li&gt;ones(n): macierz n x n, wypełniona jedynkami&lt;/li&gt;&lt;li&gt;ones(n,m): macierz n x m, wypełniona jedynkami&lt;/li&gt;&lt;li&gt;zeros: j/w, tylko wypełnione zerami&lt;/li&gt;&lt;li&gt;eye(n): macierz jednostkowa n x n&lt;/li&gt;&lt;li&gt;eye(n,m): macierz n x m, wypełniona jedynkami i zerami tak, aby&lt;/li&gt;&lt;/ul&gt; jej największa podmacierz zawierająca lewy górny róg była
jednostkowa

W Matlabie dość łatwo się tworzy wszelkiego rodzaju wykresy (2D i 3D). Do tworzenia wykresów służy plot. Najprostsze zastosowanie to narysowanie wektora. Należy stworzyć wektor i dać go jako jedyny argument plot&lt;pre&gt; y = [1 4 9 16 25 36 49 64 81 100]
plot(y)&lt;/pre&gt;W powyższym przykładzie zostanie narysowana funkcja f, gdzie f(1) = 1, f(2) = 4, f(3) = 9,... Można też zadać wartości w dziedzinie funkcji. Wystarczy dołożyć drugi argument...&lt;pre&gt;x = [1 4 9 16 25 36 49 64 81 100]
y = [1 2 4 16 32 64 128 256 512 1024]
plot (x,y)&lt;/pre&gt;Teraz f(1) = 1, f(4) = 2, f(9) = 16,...

Można też zmieniać wygląd wykresu (np. dodać tytuł, czy narysować wykres krzyżykami, zmienić kolor itp.) wszystko w help plot. Nie są to istotne rzeczy więc nie będę tego opisywał.

Czasami jest potrzeba narysowania dwóch wykresów. Trzeba wtedy podzielić okienko na wykres przy pomocy subplot. Ta komenda jest znacznie prostsza niż plot. Ma tylko dwie formy. subplot(rows,cols,index) lub subplot(v), gdzie v to wektor [rows cols index]. Jak widać są to izomorficzne formy.

Gdy podzielimy okienko przy pomocy subplot na siatkę, np.&lt;pre&gt; subplot(2,3,1)&lt;/pre&gt;powstanie siatka&lt;pre&gt;        +-----+-----+-----+
        |  1  |  2  |  3  |
        +-----+-----+-----+
        |  4  |  5  |  6  |
        +-----+-----+-----+&lt;/pre&gt;Aby zobaczyć jak się korzysta z subplot w praktyce&lt;pre&gt;x1 = [1 4 9 16 25 36 49 64 81 100]
x2 = [1 2 3 4 5 6 7 8 9 10]
y1 = [1 2 4 16 32 64 128 256 512 1024]
y2 = x1

subplot(2,2,1)
plot (x1,y1)

subplot(2,2,2)
plot (x1,y2)

subplot(2,2,3)
plot(x2,y1)

subplot(2,2,4)
plot(x2,y2)&lt;/pre&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_P9Sn6SUv534/SO4MUEAuTwI/AAAAAAAAAA8/-4N5k9YXmDo/s1600-h/wykresy.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_P9Sn6SUv534/SO4MUEAuTwI/AAAAAAAAAA8/-4N5k9YXmDo/s320/wykresy.jpg" alt="" id="BLOGGER_PHOTO_ID_5255151354001641218" border="0" /&gt;&lt;/a&gt;
Na razie tyle. Są to absolutne podstawy pracy z programem. Jednocześnie pozwalają coś zrobić ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6155017611302867096?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6155017611302867096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6155017611302867096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6155017611302867096'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/wprowadzenie-do-matlaba.html' title='Wprowadzenie do Matlaba'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://1.bp.blogspot.com/_P9Sn6SUv534/SO4MkuJyY3I/AAAAAAAAABE/v-ORR29lK7w/s72-c/octave_run.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1759604635762535831</id><published>2008-10-09T00:00:00.001-07:00</published><updated>2008-10-09T00:00:00.893-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Organizacja kodu</title><content type='html'>Jakiś czas temu ktoś na &lt;a href="http://www.gamedev.pl"&gt;Warsztacie&lt;/a&gt; dał linka do artykułu o organizacji kodu w C++. 

Materiał jest bardzo podstawowy, ale myślę że warty przeczytania. Można go znaleźć &lt;a href="http://www.gamedev.net/reference/articles/article1798.asp"&gt;pod tym adresem&lt;/a&gt;.

Jeżeli tworzysz skomplikowane kody, które długo się kompilują to musisz to przeczytać.

Życzę miłej lektury&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1759604635762535831?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1759604635762535831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/organizacja-kodu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1759604635762535831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1759604635762535831'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/organizacja-kodu.html' title='Organizacja kodu'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2605180034607234441</id><published>2008-10-05T00:00:00.002-07:00</published><updated>2008-10-05T00:00:19.655-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>W poszukiwaniu zaginionych Info-pages</title><content type='html'>Gdy coś zacznie mnie denerwować zbyt bardzo to zawsze szukam rozwiązania, aż znajdę. Tak było tym razem. Od czasu instalacji GNU/Emacs w wersji 22 po wciśnięciu kombinacji C-hr nie pojawiały się strony info. 

Potrzebowałem tych stron, ponieważ irytujące stało się szukanie informacji w internecie. Odpowiedź wyłoniła się szybko. Wystarczy zainstalować pakiet emacs22-common-non-dfsg&lt;pre&gt;$ ai emacs22-common-non-dfsg&lt;/pre&gt;i można się cieszyć pięknymi stronami podręcznika.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2605180034607234441?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2605180034607234441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/w-poszukiwaniu-zaginionych-info-pages.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2605180034607234441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2605180034607234441'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/w-poszukiwaniu-zaginionych-info-pages.html' title='W poszukiwaniu zaginionych Info-pages'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-6427106017720307577</id><published>2008-10-03T00:00:00.002-07:00</published><updated>2008-10-03T00:24:50.298-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>BOOST_FOREACH</title><content type='html'>W wielu językach jest, obok while i for, pętla typu foreach. W C++ taka konstrukcja byłaby szczególnie przydatna. Bardzo uciążliwe jest pisanie pętli w stylu:
&lt;pre&gt;for (std::vector&amp;lt;std::string&amp;gt;::iterator i = my_vector.begin(); i != my_vector.end(); i++) {
   std::cout &lt;&lt; *i &lt;&lt; std::endl;
}&lt;/pre&gt; Dobra, nie jest to uciążliwe, gdy ma się dobry edytor, ale czytanie takich krzaczków również nie należy do najprzyjemniejszych.

Przy pomocy makra BOOST_FOREACH można to zrobić następująco:&lt;pre&gt;BOOST_FOREACH(std::string&amp; str, v)
    std::cout &lt;&lt; str &lt;&lt; "\n";&lt;/pre&gt;Składania jest raczej oczywista. 

Można oczywiście takie pętle zagnieżdżać. Aby móc cieszyć się dobrodziejstwem BOOST_FOREACH należy dołączyć boost/foreach.hpp

Uwaga: Tej pętli powinno się używać gdy jest to uzasadnione, a nie zawsze. Programiści mają tendencję do nadużywania tego typu składni, a potem muszą zmieniać składnię znów na zwykłe for. W takim przypadku na pewno program nie powstaje szybciej. Wiem coś o tym - z doświadczenia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6427106017720307577?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6427106017720307577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/boostforeach.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6427106017720307577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6427106017720307577'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/boostforeach.html' title='BOOST_FOREACH'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-7089125008097591430</id><published>2008-10-01T00:00:00.002-07:00</published><updated>2008-10-01T00:00:01.796-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Ile godzin swojej pracy jesteś w stanie wyrzucić? Czas na backup</title><content type='html'>Pytanie z tematu powinien sobie zadać KAŻDY ustalając jak często będzie tworzył kopię zapasową istotnych danych. Większość ludzi ma prostą odpowiedź: "Mogę sobie pozwolić na utratę wszystkich moich danych". Dlatego nie tworzą kopii bezpieczeństwa. Mają rację - skoro jej nie potrzebują, to po co zawracać sobie głowę?

   Według mnie warto tworzyć kopię zawsze jako ostatnią czynność tuż przed wyłączeniem komputera (i przed wyłączeniem GNU/Emacs). Zawsze. Istnieje wiele narzędzi do tego celu. Ja korzystałem do tej pory z programu tar. Średnio spełniał swoją rolę. Tworzenie kopii bezpieczeństwa nie musi być "wymagające". Gdy przestało mnie zadowalać średnio-automatyczne zarządzanie kopiami przy pomocy tara zacząłem szukać nowego narzędzia. Okazuje się, że jest wiele narzędzi na desktop. Żadne z tych prostych, graficznych cudeniek nie przypadło mi do gustu. Zdecydowałem się na rdiff-backup.&lt;pre&gt;$ ai rdiff-backup&lt;/pre&gt; (ai to alias dla sudo aptitude install). Użycie tego narzędzia jest proste. Napisałem skrypt powłoki, który pamięta za mnie składnię polecenia i umieściłem go w katalogu ~/bin (który jest dodany do PATH). Skrypt nazywa się backup i wygląda tak&lt;pre&gt;#!/bin/sh

TARGET=/media/Milewski
BACKUPDIR=backup/
FILELIST=/home/lmm/.backuprc

if [ -e $TARGET ]; then
    rdiff-backup --include-globbing-filelist $FILELIST --exclude /home/lmm --print-statistics /home/lmm $TARGET/$BACKUPDIR
else
    echo -e "Musisz umieścić pendrive \"Milewski\" w wejściu USB";
fi&lt;/pre&gt;Tworzę kopię zapasową na urządzeniu /media/Milewski (o ile jest zamontowane). Pod taką nazwą jest montowany mój pendrive przeznaczony właśnie na backup (wbrew powszechnemu przekonaniu, jest to trwały sposób przechowywania danych i bezpieczny - o ile nie korzysta się z FAT).

Następnie w pliku /home/lmm/.backuprc umieściłem listę plików i katalogów, które powinny być w archiwum. Format tej listy jest trywialny. W jednej linii jest jedna ścieżka (do katalogu lub pliku). Można używać znaku '*' co oznacza "dowolna ścieżka bez znaku /" oraz ** - dowolna ścieżka. Linie poprzedzone znakiem minus (-) oznaczają, że ta ścieżka nie ma być archiwizowana (np. archiwizujemy katalog dev, ale nie jego podkatalog dev/tmp). Kawałek mojego pliku:&lt;pre&gt;/home/lmm/.bashrc
/home/lmm/.calendar
/home/lmm/bin/
/home/lmm/dev/
/home/lmm/emacs/
/home/lmm/.emacs
/home/lmm/.emacs_abbrevs.el
/home/lmm/.emacs.bmk
/home/lmm/.emacs_tutorial
/home/lmm/.freemind/
/home/lmm/org/
/home/lmm/.xbindkeysrc&lt;/pre&gt;Teraz po skończonej pracy umieszczam pendrive w USB i wydaję polecenie&lt;pre&gt;$ backup&lt;/pre&gt;Po mniej więcej minucie mam na Pendrivie w katalogu backup mirror swoich istotnych plików i katalogów. Na ekranie zostaje wyświetlone krótkie podsumowanie.

Bardzo podoba mi się sposób w jaki rdiff-backup pamięta kopię. Jest to po prostu mirror. Dzięki temu przywracać można np. poleceniem cp -a. Można przeglądać strukturę lub używać do wyszukiwania standardowych narzędzi Uniksa. Dodatkowo w katalogu z kopią jest katalog rdiff-backup-data. W nim są dodatkowe informacje o kopii, w tym katalog incrementals. rdif-backup zapamiętuje kopie również w sposób inkrementalny, co pozwala przywrócić dawną postać pliku (dowolną z jego wersji), lub usunięty plik.

rdiff-backup to bardzo zaawansowane narzędzie, pozwala np. na kopie zdalne itp. Jednocześnie można go w prosty sposób zaprzęgnąć do pracy na desktopie.

Na rdiff-backup powstała nakładka graficzna Keep (dla KDE, jest też wiele innych nakładek na rdiff-backup). Pozwala między innymi określić co jaki czas tworzyć kopię danego katalogu i odpalić program jako usługę. Osobiście wolę kontrolować proces tworzenia kopii, niż mieć usługę. rdiff-backup można zainstalować również na Macu czy Łindołsie.

Pełniejszą wiedzę (podstawową) można znaleźć na stronach
&lt;a href="http://www.nongnu.org/rdiff-backup/examples.html"&gt;Podstawy rdiff-backup na przykładach&lt;/a&gt;
&lt;a href="http://www.nongnu.org/rdiff-backup/FAQ.html"&gt;FAQ
&lt;/a&gt;&lt;a href="http://www.nongnu.org/rdiff-backup/rdiff-backup.1.html"&gt;Strona podręcznika systemowego&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7089125008097591430?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7089125008097591430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/ile-godzin-swojej-pracy-jeste-w-stanie.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7089125008097591430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7089125008097591430'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/10/ile-godzin-swojej-pracy-jeste-w-stanie.html' title='Ile godzin swojej pracy jesteś w stanie wyrzucić? Czas na backup'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1273029245020558361</id><published>2008-09-30T00:00:00.001-07:00</published><updated>2008-09-30T00:00:01.612-07:00</updated><title type='text'>Koniec jest blisko. „To mnie zainteresowało, więc to nie spam”</title><content type='html'>Często wykorzystujesz skrzynkę e-mail? Codziennie??? Wyobraź sobie, że od teraz nie możesz już korzystać z poczty elektronicznej. Co zrobisz? 

To nie są żarty. Każdego dnia spamu jest coraz więcej. Każdy taki list pożera zasoby komputerów, które są niezbędne abyś mógł cieszyć się pocztą e-mail. Każdy dzień może być ostatni dla twojej skrzynki na pocztę elektroniczną!

Przeczytaj jak możesz temu zapobiec. Oto cytat z bloga &lt;a href="http://spam.jogger.pl/"&gt;http://spam.jogger.pl/&lt;/a&gt;. Przeczytaj więcej i dowiedz się dlaczego &lt;a href="http://spam.jogger.pl/2006/06/02/spam-to-nie-moj-problem/"&gt;spam to TWÓJ problem&lt;/a&gt;

"Innym syndromem, z którym się spotykam, jest ocenianie czy dana wiadomość jest spamem czy nie na podstawie własnych zainteresowań. Chodzi mi o sytuacje, w których ktoś otrzymuje niechciany list, ale jego treść okazuje się interesująca. Zdarza się, że taka osoba uważa wtedy, iż mimo że list został rozesłany do tysięcy jeśli nie milionów innych niezainteresowanych internautów, to nadawca listu spamerem nie jest. Bo przy okazji trafił do kilku użytkowników, których temat ten interesuje.

Właśnie na takie osoby najbardziej liczą spamerzy. Takie, które mimo iż wiedzą, że wiadomość została rozesłana do milionów innych, reagują na nią tylko i wyłącznie z osobistych pobudek, dla własnego interesu, zasilając kiesę spamera i w ten sposób zachęcając go do dalszego zarzucania skrzynek niepożądanymi treściami (w nadziei, iż takich osób będzie więcej). Gdyby nie było takich sytuacji, to spamowanie nie miałoby racji bytu, albowiem spamerzy nie zarabialiby na swojej działalności złamanego grosza. Dlatego też każdy, kto w jakikolwiek sposób kupuje od spamerów lub ich świadomie promuje, powoduje iż spam przybiera na sile, ponieważ nadawcom działalność się opłaca."

Pamiętaj &lt;a href="http://spam.jogger.pl/2006/06/02/spam-to-nie-moj-problem/"&gt;spam to TWÓJ problem&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1273029245020558361?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1273029245020558361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/koniec-jest-blisko-to-mnie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1273029245020558361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1273029245020558361'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/koniec-jest-blisko-to-mnie.html' title='Koniec jest blisko. „To mnie zainteresowało, więc to nie spam”'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3576598669059197802</id><published>2008-09-28T00:00:00.002-07:00</published><updated>2008-09-28T03:22:34.455-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Odśmiecanie a inteligentne wskaźniki</title><content type='html'>Jeżeli programowałeś w języku takim jak Java, C#, Haskell czy OCaml, to mogłeś się cieszyć automatycznym odśmiecaniem. Oznacza to, że programista może przydzielić pamięć dla nowego obiektu, ale nie musi pamiętać o zwróceniu tej pamięci gdy obiekt jest niepotrzebny. System sam potrafi wykryć czy dany kawałek sterty nie jest używany przez program i w takim przypadku zwolnić pamięć. OCaml ma najlepszy system odśmiecania jaki znam. Jest to hybryda najbardziej znanych sposobów. Dodatkowo odśmiecanie jest inkrementacyjne (dzięki temu, nie ma sytuacji, w której program zamraża się na kilka sekund, bo rozpoczęło się odśmiecanie). Zaznaczam, że nie wiem jak jest W Haskellu, ale przypuszczam, że też przynajmniej dobrze (jak ktoś może uzupełnić, to było by super - np. w komentarzu).

   Opis odśmiecania w OCamlu jest dostępny tu http://caml.inria.fr/pub/docs/oreilly-book/html/book-ora086.html.

   Moim zdaniem w językach obiektowych odśmiecanie jest nie do końca dobre. Nie dlatego, że potrafi nawet zatrzymać program na dłuższy czas, ale... zwalnia programistę z myślenia. To jest filozofia Javy: "Niech programista nie musi o tym myśleć", "Tu programista może zrobić sobie krzywdę, więc ten mechanizm nie będzie dostępny", "Czy programista na pewno wie co robi?". Wystarczy zapytać programistę Javy, po co określać inwariant klasy. Ciekawe czy będzie widział co to jest. Nie chcę uogólniać i krzywdzić dobrych programistów. Mam na myśli programistów "z przypadku", którzy wybrali Javę, bo nie trzeba myśleć. Są też tacy, którzy ją wybrali, bo mieli powód (istnieje przynajmniej kilka dobrych powodów).

   Jeżeli nie odśmiecanie to co? Pozostaje trochę prostszy i lżejszy system zliczania referencji. Wskaźniki na obiekty są opakowane strukturą, która ma licznik wskaźników pokazujących na dane miejsce w pamięci. Gdy kopiuje się taki inteligentny wskaźnik to licznik referencji wzrasta, gdy się niszczy taki wskaźnik (np. wychodzi z bloku) to licznik maleje. Gdy licznik osiągnie 0, to obiekt jest niszczony.

   Wykorzystanie jest trywialne. Najpierw&lt;pre&gt;#include &amp;lt;boost/shared_ptr.hpp&amp;gt;&lt;/pre&gt;potem zamiast pisać&lt;pre&gt;T* a = new T;&lt;/pre&gt;pisze się&lt;pre&gt;boost::shared_ptr&amp;lt;T&amp;gt; a(new T);&lt;/pre&gt;To wszystko. Jeżeli nie popatrzysz na typ, to nawet nie zauważysz, że to jest inteligentny wskaźnik. Inteligentne wskaźniki zachowują się tak samo jak zwykłe, ale dbają o zwalanianie pamięci. boost::shared_ptr powodują niewielki narzut (czasowy i pamięciowy), dlatego warto je stosować. Nie należy jednak zwalniać się z myślenia. W końcu na myśleniu programowanie polega. Możesz mieć zaufanie do systemu, do języka, ale powinieneś wiedzieć kiedy obiekty są tworzone i niszczone (i dlaczego). 

Jest oczywiście haczyk. Wyobraź sobie obiekt, który wskazuje na siebie, albo strukturę obiektów, których połącznia tworzą cykl. Wówczas każdy z takich obiektów ma zawsze licznik referencji przynajmniej 1. Jest to jawny wyciek. Właśnie dlatego jest specjalny typ boost::weak_ptr. Działa on tak, jak boost::shared_ptr, ale nie zwiększa licznika referencji. Dla bezpieczeństwa nie można go wyłuskać. Można natomiast utworzyć na jego podstawie boost::shared_ptr i wtedy wyłuskać. Stosowanie boost::weak_ptr jest bezpieczne mimo, że nie zlicza referencji. Jest tak bo boost::weak_ptr zostanie powiadomiony, o tym, że obiekt został zwolniony i, w takim przypadku, przy próbie zamknięcia go w boost::shared_ptr zostanie rzucony wyjątek.

Dlatego przy implementacji drzewa, w którym każdy wierzchołek trzyma wskaźnik na synów i ojca, wskaźnik na ojca można pamiętać jako boost::weak_ptr.

To co powinieneś zapamiętać to zastosowanie, fakt że boost::shared_ptr są bezpieczne ze względu na wyjątki, boost::shared_ptr współpracują dobrze z kontenerami biblioteki standardowej i dlaczego mimo wszystko może powstać wyciek (i jak się przed tym bronić).

Jak zawsze zachęcam do lektury dokumentacji 
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/shared_ptr.htm
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/weak_ptr.htm

Jest jeszcze boost::scoped_ptr
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/scoped_ptr.htm
Tych wskaźników nie można kopiować. Właściwie to nie licząc idiomu pimpl (bo o wzorcu ciężko tu mówić), to nie znalazłem zastosowania dla tej klasy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3576598669059197802?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3576598669059197802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/odmiecanie-inteligentne-wskaniki.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3576598669059197802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3576598669059197802'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/odmiecanie-inteligentne-wskaniki.html' title='Odśmiecanie a inteligentne wskaźniki'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-1185092289246383883</id><published>2008-09-26T00:00:00.009-07:00</published><updated>2011-10-23T03:22:30.653-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Piekło rodziny mem_fun* oraz... wybawienie ze strony boost::bind</title><content type='html'>Każdy, kto pisze chwilę dłużej w języku C++ był zmuszony korzystać z funkcji biblioteki standardowej mem_fun lub mem_fun_ref. Wszystkie funkcje, które przyjmują parę iteratorów jako argumenty (np. std::for_each) przyjmują też jako argument funkcję. Tu właśnie jest problem. Funkcję. C++ nie jest językiem funkcyjnym, dlatego funkcje nie są w nim tak ważne jak np. liczby całkowite. W szczególności nie mają swojego typu. Tak! W C++ nie istnieje typ, który mówi: funkcja, która przyjmuje jeden argument typu int i zwraca float. Można utworzyć co prawda wskaźnik na funkcję, ale to coś zupełnie innego. Typ wskaźnika na funkcję oczywiście istnieje (niewiele osób wie, że istnieje też wskaźnik na metodę). 

Każdy kto poznał smak programowania funkcyjnego chce korzystać z wyrażeń lambda, mieć pełną swobodę w manipulowaniu funkcjami, tworzeniu ich w czasie działania programu, zwracaniu funkcji jako wynik, częściowej aplikacji, mieć "closures" itd.

Projektanci biblioteki STL wyszli programistom C++ na przeciw i dali cały arsenał funkcji, które w jakiś sposób aplikują zadaną przez argument funkcję do wszystkich elementów kolekcji. Jest np. std::for_each, std::sort, std::transform itd.

Przed chwilą pisałem, że funkcja nie ma typu i nie można jej przekazać przez argument, a teraz piszę coś zupełnie przeciwnego. Istnieją funkcje, które jako argument biorą funkcje. Właśnie nie. One jako argument przyjmują odpowiedni obiekt (std::unary_function, std::binary_function itp.). To by było dobre w C (ale wtedy nie istniało). W C++ jest bez sensu, bo pisze się obiektowo, a co za tym idzie operuje się metodami, nie funkcjami. 

W C++ typ wskaźnika na funkcję znacznie różni się od wskaźnika na metodę. Co ciekawe większość osób widząc zapis wskaźnika na metodę myśli, że tam jest błąd składniowy ;-) Z tego powodu, nie można korzystać z metod w miejscu, gdzie są potrzebne funkcje. Nie można napisać&lt;pre&gt;std::for_each(v.begin(), v.end(), A::foo);&lt;/pre&gt;Gdzie v to wektor elementów typu A, a A::foo to metoda w klasie A. Nie da się też napisać funkcji for_each tak aby przyjmowała jako argument wskaźnik na metodę (bo trzeba by zadać typ zawierający tę metodę, czyli trzeba by dodać parametr do szablonu takiej funkcji).

Dlatego projektanci STL wpadli na genialny pomysł i stworzyli funkcje, które "zamieniają metodę w funkcję" (to jest nadużycie, ale niech będzie). Te funkcje to właśnie std::mem_fun i std::mem_fun_ref. Są w nagłówku functional. Użycie jest następujące:&lt;pre&gt;struct A {
    A(int i) : _i(i) {
    }
    
    void foo() {
       std::cout &lt;&lt; _i &lt;&lt; "\n";
    }

    int _i;
};

int main() {
    std::vector&amp;lt;A&amp;gt; v;
    std::vector&amp;lt;A*&amp;gt; u;    

    // mem fun ref
    for (size_t i = 0; i &lt; 10; i++)
       v.push_back(A(i));
    std::for_each(v.begin(), v.end(), std::mem_fun_ref(&amp;A::foo));

    // mem fun
    std::cout &lt;&lt; "" &lt;&lt; "\n";
    for (size_t i = 0; i &lt; 10; i++)
       u.push_back(new A(i));
    std::for_each(u.begin(), u.end(), std::mem_fun(&amp;A::foo));

    // zwolnij pamięć
    for (size_t i = 0; i &lt; 10; i++)
       delete u.at(i);
 
    return 0;
}&lt;/pre&gt;Jak widać mem_fun wykorzystuje się w przypadku operacji na kontenerze wskaźników, a mem_fun_ref gdy kontener zawiera wartości. Implementacje mem_fun i mem_fun_ref są bardzo proste (przeciętny programista powinien umieć je sam napisać, podpowiem że warto stworzyć dodatkową, pomocniczą, parametryczną klasę mem_fun_t, która w konstruktorze dostanie wskaźnik na metodą i "przerobi" go na funktor - w razie wątpliwości &lt;a href="http://www.sgi.com/tech/stl/stl_function.h"&gt;zajrzyj w źródło functional&lt;/a&gt;. 

Co jest złego w tych funkcjach? Po pierwsze to, że są dwie!!! Dostrzegasz to, że aby zrobić tę samą czynność należy umieć posługiwać się dwoma różnymi mechanizmami? Po drugie ich zastosowanie jest bardzo ograniczone. Te typy mem_fun_t i mem_fun_ref_t dziedziczą po unary_function. Oznacza to, że można korzystać tylko z funkcji jednoargumentowych. Czasem ma się dostępną funkcję przyjmującą dwa argumenty i chce się jeden z nich ustalić na np. 2, a drugi niech będzie zadany już przez for_each. To jest możliwe, ale trzeba skorzystać z bind1st lub bind2nd. Czyli już są zagnieżdżone funkcje szablonowe! Co gorsze, gdy dostępna jest funkcja o pięciu argumentach i trzeba ustalić pierwszy, drugi, czwarty i piąty, to współczuję każdemu, kto podejmie się tego przy pomocy STL (trzeba mocno zagnieździć funkcje szablonowe, a pomyłka grozi niemałym komunikatem błędu). Jest jeszcze jedna wada...

Tę chcę opisać trochę szerzej bo jest najistotniejsza. Wymaganie dwóch funkcji (a nie jednej) jest stąd, że ich działanie nieznacznie się różni. Otóż gdy dostaje się wartość to wystarczy z niej skorzystać (zaaplikować funkcję), natomiast gdy dostaje się wskaźnik to trzeba go wyłuskać i dopiero na wartości zaaplikować funkcję. Wyobraź sobie co się będzie działo gdy będziesz miał kontener wskaźników na wskaźniki. Wtedy trzeba napisać trzecią funkcję (lub zmienić tę podawaną przez argument tak, aby sama wykonywała wyłuskanie). Dokładnie ten problem powoduje, że wykorzystanie funkcji mem_fun i mem_fun_ptr w kontekście inteligentych wskaźników (boost::shared_ptr) jest niemożliwe. Trzebaby napisać kolejną funkcję (jest to zbyt częsty przypadek, aby pozwolić sobie na pamiętanie o tym w kodzie i modyfikowanie każdej funkcji). Możesz więc napisać kolejną wersję mem_fun i zapamiętać którą kiedy wywołać (ja mam, szczerze mówiąc, z tym problemy. Zawsze wydaje mi się, że mem_fun jest logiczniejsze w kontekście wartości), albo...

Wykorzystać boost::bind. Ta biblioteka do dzisiaj robi na mnie takie wrażenie, że opisuję ją w pierwszej kolejności. Jak nazwa biblioteki wskazuje, służy ona do wiązania argumentu funkcji z wartością. boost::bind jest w pliku nagłówkowym boost/bind.hpp. Powyższy kod mógłby wyglądać tak:&lt;pre&gt;struct A {
    A(int i) : _i(i) {
    }
    
    void foo() {
       std::cout &lt;&lt; _i &lt;&lt; "\n";
    }

    int _i;
};

int main() {
    std::vector&amp;lt;A&amp;gt; v;
    std::vector&amp;lt;A*&amp;gt; u;    

    // mem fun ref
    for (size_t i = 0; i &lt; 10; i++)
       v.push_back(A(i));
    std::for_each(v.begin(), v.end(), boost::bind(&amp;A::foo, _1));

    // mem fun
    std::cout &lt;&lt; "" &lt;&lt; "\n";
    for (size_t i = 0; i &lt; 10; i++)
       u.push_back(new A(i));
    std::for_each(u.begin(), u.end(), boost::bind(&amp;A::foo, _1));

    // zwolnij pamięć
    for (size_t i = 0; i &lt; 10; i++)
       delete u.at(i);
 
    return 0;
}&lt;/pre&gt;Czy zwróciłeś uwagę, że interfejs jest taki sam w przypadku wskaźnika i wartości? Gdyby posługiwać się kontenerem inteligentnych wskaźników, to użycie byłoby dokładnie takie samo. Jak czytać&lt;pre&gt; boost::bind(&amp;A::foo, _1);&lt;/pre&gt;
To jest łatwe. Jako pierwszy argument podajemy funkcję lub metodę. Następnie jest lista argumentów. To co wychodzi to funkcja przyjmująca (w tym przypadku) jeden argument. Ten argument zostanie przekazany jako pierwszy argument metody A::foo. Pierwszym jej argumentem jest oczywiście wskaźnik na obiekt, na rzecz którego jest wywołane foo (niestatyczna metoda jako pierwszy argument zawsze niejawnie dostaje wskaźnik na obiekt). To _1 oznacza pierwszy argument. Są też _2, _3,..., nie wiem do ilu, ale mi na razie zawsze wystarczało. Można też zdefiniować własne, ale należy liczyć się z tym, że można zdezorientować czytających kod.

To w sumie wszystko. Korzystać z boost::bind można nauczyć się, czytając jedno zdanie. Mam zadanie. Co robi&lt;pre&gt;boost::bind(&amp;A::foo, _1, _1)&lt;/pre&gt;Zastanów się. Odpowiedź: definiuje funkcję, która przyjmuje jeden argument (jest _1, ale nie ma _2). Wywołanie tej funkcji z argumentem x jest równoważne z wywołaniem A::foo(x,x). A teraz takie coś:&lt;pre&gt;boost::bind(&amp;A::foo, _2, _1);&lt;/pre&gt;Dokładnie. Ta funkcja robi 'swap' na argumentach. Czyli foo(x,y) jest równoważne z boost::bind(foo, _2,_1)(y,x). Zauważyłeś jak wykorzystać boost::bind do związania argumentów funkcji, a nie metody? Proste prawda? Właśnie to powoduje, że lubię stosować tę bibliotekę.

Nie napisałem jeszcze tego, choć myślę, że to oczywiste. Oczywiście można w bind podawać też wartości a nie tylko te "placeholdery" _1, _2,... np.&lt;pre&gt;void foo(int x) {
   std::cout &lt;&lt; x;
}
foo(5);&lt;/pre&gt;jest równoważne z&lt;pre&gt;boost::bind(foo, 5)(); // funkcja bez argumentów&lt;/pre&gt;Można też część zadać, a części nie. 

Na koniec jeszcze coś ciekawego. Na początku pisałem, że funkcja nie ma swojego typu. Biblioteka boost::function to zmienia!!! Teraz można swobodnie utworzyć funkcję i zapamiętać ją w zmiennej. Można też funkcję przekazać jako argument innej funkcji, czy zwrócić jako rezultat.

boost::function to znacznie więcej. Pozwala wcielić w życie wzorzec "Command", który pozwala odseparować utworzenie akcji od jej wykonania. Pozwala to np. tworzyć mechanizmy typu undo/redo. Ale o tym innym razem. 

Wynik boost::bind można przypisać do boost::function (tak samo wynik boost::lambda). Robi się to tak:&lt;pre&gt;boost::function&amp;lt;void(int&amp;)&amp;gt; f = boost::bind(foo, _1);&lt;/pre&gt;Jak widać najpierw typ. boost::function i specjalizacja szablonu. W szablonie podaje się typ funkcji (nie wskaźnika na funkcję). W tym przypadku jest to &lt;pre&gt;void(int&amp;)&lt;/pre&gt;czyli funkcja, która zwraca void i bierze jako argument referencję do int. Skąd wziąć typ funkcji? Popatrz jeszcze raz. To tak jakbyś ją zadeklarował, a następnie usunął nazwę. Np.&lt;pre&gt;int jakas_funkcja(float x, int y, std::string str);&lt;/pre&gt;ma typ:&lt;pre&gt;int(float,int,std::string)&lt;/pre&gt;i wykorzystuje się to tak:&lt;pre&gt;boost::function&amp;lt;int(float,int,std::string)&amp;gt;&lt;/pre&gt;Po typie występuje nazwa utworzonego obiektu (u nas jest to f), dalej znak równości i definicja funkcji (boost::bind...).

Mam nadzieję, że od teraz będziesz wykorzystywał te mechanizmy we własnych programach. Możesz zacząć od lektury dokumentacji STL, aby się dowiedzieć, które dokładnie funkcje usprawnią pisanie kodu, zapewniając dostęp do opisanych mechanizmów.

Żadna z opisanych bibliotek nie wymaga kompilacji! Obie są intuicyjne w użyciu. Polecam spróbować.
&lt;a href="http://www.boost.org/doc/libs/1_36_0/libs/bind/bind.html"&gt;
Dokumentacja boost::bind&lt;/a&gt;
&lt;a href="http://www.boost.org/doc/libs/1_36_0/doc/html/function.html"&gt;Dokumentacja boost::function&lt;/a&gt;

No ok. Napiszę to. Celowo nie napisałem wszystkiego, aby Ci, którzy przeczytają dokumentację mieli większy zysk z wykorzystania tych bibliotek, bo są mechanizmy (nieskomplikowane), które warto poznać.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-1185092289246383883?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/1185092289246383883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/pieko-rodziny-memfun-oraz-wybawienie-ze.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1185092289246383883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/1185092289246383883'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/pieko-rodziny-memfun-oraz-wybawienie-ze.html' title='Piekło rodziny mem_fun* oraz... wybawienie ze strony boost::bind'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-2902393554303431047</id><published>2008-09-25T16:00:00.000-07:00</published><updated>2008-09-25T16:00:01.390-07:00</updated><title type='text'>Kończą się IP...</title><content type='html'>W Chinach kończą się numer IP. Szacuje się, że przy obecnym tempie zajmowania kolejnych numerów Chińczykom pozostało nieco ponad 800 dni na rozwiązanie problemu.

Więcej na
&lt;a href="http://www.hacking.pl/pl/news-15007-Chinczykom_koncza_sie_IP_.html"&gt;http://www.hacking.pl/pl/news-15007-Chinczykom_koncza_sie_IP_.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2902393554303431047?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2902393554303431047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/kocz-si-ip.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2902393554303431047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2902393554303431047'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/kocz-si-ip.html' title='Kończą się IP...'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3560954936779275645</id><published>2008-09-24T06:00:00.000-07:00</published><updated>2008-09-24T06:00:01.417-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rozrywka'/><title type='text'>Ach jak przyjemnie</title><content type='html'>Dzisiaj na wspaniałym serwisie JoeMonster znalazłem taką oto piosenkę:
&lt;a href="http://www.joemonster.org/filmy/11199/Ach_jak_przyjemnie_wersja_dla_programistow"&gt;Ach jak przyjemnie&lt;/a&gt; w wersji dla programistów.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3560954936779275645?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3560954936779275645/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/ach-jak-przyjemnie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3560954936779275645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3560954936779275645'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/ach-jak-przyjemnie.html' title='Ach jak przyjemnie'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4214065688262246490</id><published>2008-09-23T00:00:00.007-07:00</published><updated>2008-09-23T01:00:14.183-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>brakujący separator. Stop.</title><content type='html'>Postanowiłem jeszcze raz opisać ten problem. Tym razem w oddzielnej notce. Powodem, jest fakt, że wiele osób ma z tym problem.

Oczywiście chodzi o komunikat błędu, pojawiający się podczas pracy z plikami Makefile. W pełnym brzmieniu może wyglądać tak:&lt;pre&gt;Makefile:3: *** brakujący separator. Stop.&lt;/pre&gt; Jak czytać ten komunikat? 

Po pierwsze oznacza on błąd składniowy. W 90% przypadków będzie to użycie spacji zamiast tabulatora. Tak może się zdarzyć nawet w przypadku pliku Makefile, który przyszedł z obcym oprogramowaniem. Jeżeli otworzyłeś plik w edytorze i go zapisałeś, to mogły zostać utracone informacje o odstępach (niektóre, raczej kiepskie, edytory automatycznie konwertują tabulatory na spacje).

Komunikat jest w formacie: nazwa_pliku:numer_linii: *** opis błędu.
Zatem w przypadku tego błędu należy otworzyć plik 'Makefile' w edytorze tekstu (np GNU/Emacs). Następnie przejdź do linii numer 3 (w GNU/Emacs możesz to zrobić np. przez M-gM-g 3 RET). W tym momencie masz kursor w linijce, w której make widzi błąd składniowy. Musisz go poprawić.

Jak wspomniałem, błąd najczęściej polega na użyciu spacji zamiast tabulatora, do zrobienia odstępu od początku linii do pierwszego znaku. Wszystkie linie, w plikach Makefile, muszą zaczynać się tabulatorem, lub być przy lewej krawędzi. Wyjątek dotyczy tylko komentarzy, które zaczynają się znakiem #.

Jeżeli to nie rozwiązało Twojego problemu, to czytaj dalej. Jak już wspomniałem, linie zaczynają się tabulatorem, lub są przy lewej krawędzi. Te zaczynające się tabulatorem to są polecenia. W nich raczej nie ma błędu, bo make nie sprawdza, czy polecenie istnieje.

Te, linie, które są przy lewej krawędzi mogą mieć jedną z dwóch postaci. Są nazwą reguły lub zmienną. Nazwa reguły to ciąg znaków zakończony dwukropkiem, po którym występują nazwy innych reguł (zależności). Sprawdź czy jest dwukropek. Regułę poznasz po tym, że następna linia jest wcięta.

W pliku Makefile mogą istnieć jeszcze zmienne. Przypisania zmiennych mają postać: CIAG_ZNAKOW=WARTOSC. Sprawdź czy po ciągu znaków jest znak =. Czasem zdarzają się puste przypisania (zmienna nie ma wartości). Wtedy składania wygląda tak:&lt;pre&gt;NAZWA=&lt;/pre&gt;Upewnij się, że jest znak równości po nazwie zmiennej.

Jeżeli te wskazówki Ci nie pomogły to dodaj komentarz lub wyślij mi e-mail (login:lmilewski, domena:gmail.com). Postaram się pomóc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4214065688262246490?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4214065688262246490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/brakujcy-separator-stop.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4214065688262246490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4214065688262246490'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/brakujcy-separator-stop.html' title='brakujący separator. Stop.'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6170142766564625282</id><published>2008-09-22T00:00:00.002-07:00</published><updated>2008-09-22T00:00:02.268-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Jak zrobić polskie znaki diakrytyczne w mplayer?</title><content type='html'>Niestety. Napisy do filmów są tworzone w kodowaniu cp1250. Czyli pseudostandardzie Microsoftu. Jest to problem, bo oglądając filmy widzimy zamiast napisów krzaki. O ile w prostych zdaniach to nie przeszkadza, o tyle wyobraź sobie zdanie "Zażółć gęślą jaźń" z krzakami. 

Nawet jeżeli tylko niektóre litery są przekręcane, to i tak przyjemność z oglądania jest trochę mniejsza niż z pięknymi, kształtnymi polskimi ogonkami.

Najprostszy sposób poradzenia sobie z problemem to:
Otwieramy plik z napisami w edytorze tekstu (np. GNU/Emacs)
Zaznaczamy tekst i wycinamy go
Zmieniamy ustawienia kodowania na UTF-8 i zapisujemy pusty plik (w GNU/Emacs C-xRETr utf-8 RET)
Wklejamy napisy
Zapisujemy plik

System powinien automatycznie przekonwertować kodowania. Cała operacja jest szybka. W GNU/Emacs można nawet zrobić do tego makro ;-P. Oczywiście istnieją programy do konwersji, ale taki "domowy" sposób jest wystarczający.

Dalej normalnie odpalamy film. Możemy zadać kodowanie mplayerowi przez wstawienie do pliku ~/.mplayer/config
subcp=utf-8
Można też bezpośrednio z komend: -subcp utf-8
Mój plik ~/.mplayer/config wygląda tak:&lt;pre&gt;subcp=utf-8
vo=sdl
ao=sdl
stop-xscreensaver=true
fs=true
subfont-autoscale=0
subfont-osd-scale=18
subfont-text-scale=22&lt;/pre&gt;Ta metoda oczywiście obchodzi problem, a nie rozwiązuje go. Poprawnie należy zassać z netu odpowiednie fonty i je wrzucić do mplayer'a. Też działa.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6170142766564625282?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6170142766564625282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/jak-zrobi-polskie-znaki-diakrytyczne-w.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6170142766564625282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6170142766564625282'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/jak-zrobi-polskie-znaki-diakrytyczne-w.html' title='Jak zrobić polskie znaki diakrytyczne w mplayer?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-4179457389040615019</id><published>2008-09-21T00:00:00.000-07:00</published><updated>2008-09-21T00:00:05.932-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Do czego służy polecenie tee?</title><content type='html'>Jestem zwolennikiem konsoli. Często wykorzystuję polecenia, które generują tekst i chcę wynik mieć w pliku. Jednocześnie przydaje się podgląd wyjścia. Choćby po to, aby przerwać polecenie, jeżeli wynik nie jest zadowalający. Powstaje potrzeba wysłania wyniku na dwa strumienie jednocześnie (plik i stdout). Do tego służy tee.&lt;pre&gt;$echo "hello world" | tee plik.txt&lt;/pre&gt;Na ekranie pojawi się "hello world". W pliku plik.txt będzie ten sam tekst. Zatem wystarczy wysłać przez potok na wejście tee dowolny plik, a dane zostaną wysłane na dwa strumienie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4179457389040615019?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4179457389040615019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/do-czego-suy-polecenie-tee.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4179457389040615019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4179457389040615019'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/do-czego-suy-polecenie-tee.html' title='Do czego służy polecenie tee?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-5139314149033159364</id><published>2008-09-20T00:00:00.003-07:00</published><updated>2008-11-09T14:23:14.425-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Teoria informatyki'/><title type='text'>Dlaczego warto poznać problemy NP-zupełne? Czy P = NP?</title><content type='html'>Klasa problemów to zbiór problemów, charakteryzujących się pewnymi cechami. Jest wiele klas problemów. Najpopularniejsza to klasa P (lub pełniej PTIME) [P od ang. polynomial - wielomian]. Jest to klasa wszystkich tych problemów, które daje się rozwiązać w czasie wielomianowym. Innymi słowy problem jest w klasie P, jeżeli daje się go rozwiązać w czasie ograniczonym przez pewien wielomian, od wielkości danych wejściowych. Przykładem takiego problemu jest np. znajdywanie najkrótszej drogi w grafie - czas O((V+E)logV) (algorytm Dijkstry), mnożenie macierzy O(n^3), wyszukiwanie wzorca w tekście O(n) (algorytm KMP) czy znajdowanie maksymalnego przepływu O(VE^2) (algorytm Edmondsa-Karpa).

  Klasa P jest interesująca na swój sposób. Poznajemy nowe, szybsze algorytmy na rozwiązanie problemu. Czasem dowodzimy, że nie da się szybciej. Przeprowadzamy dowody poprawności itp. Nie zmienia to faktu, że P jest uznawane za klasę problemów prostych (P jak proste ;-P). Wszystkie te problemy daje się rozwiązać przy pomocy komputera w skończonym czasie.

  Istnieje też klasa problemów nierozstrzygalnych. To zaskakujące, ale są problemy, których nie da się rozwiązać. Nie da się w tym sensie, że niezależnie od mocy obliczeniowej komputera i czasu jaki mu damy (nawet setki lat), nie poda poprawnego wyniku (tzn. może i poda, bo można napisać program losujący odpowiedź i kiedyś pewnie trafi (np. gdy odpowiedzi należą do zbioru {tak, nie}, ale my nie będziemy wiedzieli czy to dobre rozwiązanie). Takim problemem jest np. sprawdzenie czy program wypisuje "hello world". Mamy dany, na wejściu, kod źródłowy i chcemy stwierdzić czy po wykonaniu na ekranie pojawi się "hello world". O ile w prostych przypadkach (sprawdzenie czy jest instrukcja print, a potem krótki tekst) to jest łatwe, o tyle w innych może być gorzej.

  Ogólny program sprawdzający powyższe nie istnieje. Można tego dowieść matematycznie. Jeżeli jednak ustalimy program wejściowy (źródło), to da się napisać program, który da dobrą odpowiedź. Czyli nie musimy rozwiązywać problemu, w ogólności, ale w konkretnym przypadku. Łatwo udowodnić, że da się napisać taki program. Piszemy dwa. Jeden wypisuje "tak", drugi "nie". Któryś wynik na pewno jest poprawny.

  Klasa problemów nierozstrzygalnych jest ciekawa, ale...

  Klasa NP (ang. nondeterministic polynomial - niedeterministycznie wielomianowe) jest jeszcze ciekawsza. O ile w przypadku powyższych klas wiemy wszystko (P - problemy daje się rozwiązać, Nierozstrzygalne - problemów nie da się ozwiązać), o tyle o NP nie wiadomo jeszcze nic.

  NP to te problemy, które daje się rozwiązać w czasie wielomianowym, pod jednym warunkiem. Maszyna jest niedeterministyczna. Tzn. Może zadawać pytania do "wszechwiedzącego głosu z kratki". Oczywiście każdy problem z P jest w NP - po prostu nie zadajemy żadnych pytań. Ale nie o to chodzi. Weźmy np. spełnianie formuł boolowskich. Mamy formuły boolowskie (zbiór zmiennych, stałych prawda/fałsz, i operatorów "i",  "lub", "nie"). Chcemy sprawdzić czy taka formuła jest spełnialna (czyli czy istnieje takie wartościowanie zmiennych w niej występujących, że formuła będzie prawdziwa. Dla przykładu, dla formuły: "x i y" istnieje takie wartościowanie (x = 1, y = 1). Dla formuły (x i nie x) nie istnieje.

  Dowód, że ten problem jest w NP? Mamy następujący algorytm: 1. zgadnij wartościowanie (zapytaj głosu z kratki) 2. sprawdź, czy formuła jest prawdziwa - to oczywiście jest w czasie wielomianowym.

  Jak widać głos z kratki może nas mylić, dlatego nasz algorytm musi umieć sprawdzić poprawność podpowiedzi. Inaczej wystarczyłoby zawsze napisać taki sam algorytm "Hej, głosie z kratki!! Jakie jest rozwiązanie?".

  Klasa NP jest duża. Zawiera całe P, ale też potężną dawkę innych problemów. Wszystkie problemy z NP daje się rozwiązać, deterministycznie w czasie wykładniczym. Po prostu przeglądamy każdą możliwą odpowiedź (tu jest czas wykładniczy). Dla każdej odpowiedzi sprawdzamy poprawność algorytmem, który użylibyśmy w klasie NP (wielomianowo).

  Pojawia się pytanie czy P = NP? Na razie nikt nie znalazł na nie odpowiedzi. Zdarza się, że pewien problem z klasy NP, przechodzi do P. Czy każdy problem z NP, tak na prawdę, jest w P tylko my, mali ludzie, tego nie wiemy. Tu pojawiają się problemy NP-zupełne.

  Jeżeli da się rozwiązać, w czasie wielomianowym, dowolny problem NP-zupełny, to da się rozwiązać, w czasie wielomianowym, wszystkie problemy z NP. Innymi słowy, wszystkie problemy z NP są nietrudniejsze niż dowolny problem NP-zupełny. Problem NP-zupełny po pierwsze jest w NP, a po drugie ma własność zupełności (czyli jest niełatwiejszy od wszystkich innych z NP). Problemów NP-zupełnych jest dużo, więc mówimy o klasie tych problemów. To tak jak z idiotą zupełnym. Idiota zupełny to osoba, która jest takim idiotą, że każdy inny idiota jest nie większym idiotą niż on.

  Jak to możliwe, że rozwiązanie jednego problemu powoduje możliwość rozwiązania wszystkich innych? Chodzi o redukcje. Problem 3-kolorowania grafów jest NP-zupełny (wierz mi lub nie). Wynika z tego, że problem 4-kolorowania grafów też jest NP-zupełny. Jak? Załóżmy, że potrafimy 4-kolorować graf. Chcemy się dowiedzieć jak 3-kolorować.

  Bierzemy graf, który ma być 3-kolorowalny, dodajemy jeden wierzchołek, który łączymy z każdym innym. Sprawdzamy czy graf wyjściowy jest 4-kolorowalny, jeżeli tak, to wejściowy był 3-kolorowalny. Dlaczego? Bo ten dodany wierzchołek ma inny kolor niż każdy z pozostałych - inaczej nie mógłoby być między nimi krawędzi. Jeżeli więc usuniemy ten wierzchołek, wraz z jego kolorem, to zostaną 3 kolory w grafie.

  Twierdzenie odwrotne też jest prawdziwe, jeżeli graf wejściowy był 3-kolorowalny, to wyjściowy jest 4-kolorowalny. Oczywiste. Kolorujemy trzema kolorami wierzchołki z grafu wejściowego, a czwartym kolorem wierzchołki grafu wyjściowego.

  Algorytm zamiany problemu z 4-kolorowania na 3-kolorowanie to właśnie redukcja. Tak samo przejście z dowolnego problemu A do innego B wymaga redukcji. Tak więc funkcja f jest redukcją problemu A do B, jeżeli dla każdego x należącego do A, f(x) należy do B i dla każdego x nie należącego do A, f(x) nie należy do B. Jeżeli chcemy udowodnić, że problem jest NP-zupełny to nie wystarczy redukcja - musi być jeszcze wielomianowa, czyli taka, która nie zajmie więcej niż wielomianowy czas. W naszym przypadku tak właśnie było (dodananie wierzchołka i kilku krawędzi to czas liniowy, więc wielomianowy).

  Co z tego wynika? Że 4-kolorowanie to problem NP-zupełny (przy założeniu, że 3-kolorowanie jest NP-zupełne). Dlatego, że rozwiązując problem 4-kolorowania umiemy rozwiązać 3-kolorowanie. 3-kolorowanie to problem NP-zupełny, więc umiemy rozwiązać wszystkie problemy z NP.

  Problemy NP-zupełne są połączone takimi zależnościami (czasem nawet w dwie strony). 3SAT (spełnialność formuł boolowskich w CNF z &lt;= 3 zmiennymi) jest dodatkowo "połączony" ze wszystkimi problemami z NP. Jeżeli da się go rozwiązać, to da się rozwiązać wszystkie problemy z NP (twierzdenie Cooke'a). Jeżeli uwierzyłeś mi, że 3-kolorowanie grafu jest NP-zupełne to dobrze zrobiłeś. Można 3SAT zredukować do 3-kolorowania grafu. Można też 3-kolorowanie zredukować do 3SAT, więc te problemy są tak samo trudne.

  Często mylone są pojęcia "problem NP-trudny" i "problem NP-zupełny". Problem NP-trudny to taki, który jest niełatwiejszy od wszystkich problemów z NP. Czyli problemy NP-zupełny to problemy z NP, które są NP-trudne. Problemy NP-trudne nie muszą być w NP.

  Istnieje oczywiście więcej klas, jak PSPACE, NPSPACE, co-P (równa P), co-NP, EXPTIME, EXPSPACE,...

  Dlaczego warto poznać problemy NP-zupełne i nauczyć się redukować? Bo zamiast tracić czas na rozwiązanie problemu na który napotkasz, być może uda Ci się udowodnić, że jest NP-zupełny. Co prawda nie wiadomo czy P = NP, ale wiele osób próbowało udowodnić tę równość. Można zdać się na to, że skoro nie udało się to tylu profesorom, to nie warto poświęcać swojego czasu.

  Czy P = NP?. Nie wiem, ale odpowiedź, jeśli istnieje, musi być prosta.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-5139314149033159364?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/5139314149033159364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/dlaczego-warto-pozna-problemy-np-zupene.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5139314149033159364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/5139314149033159364'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/dlaczego-warto-pozna-problemy-np-zupene.html' title='Dlaczego warto poznać problemy NP-zupełne? Czy P = NP?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-8672780552696911066</id><published>2008-09-18T00:00:00.004-07:00</published><updated>2008-09-18T00:00:00.469-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>STL i BOOST</title><content type='html'>Ta notka ma być wprowadzeniem w BOOST. Planuję opisywać skrótowo te najbardziej przydatne biblioteki z BOOST. Przeczytaj, abyś miał rozeznanie "po co?" i "jak?", a nie tylko "jak korzystać?".

Budowa i założenia to jest to, co odróżnia C++ od Javy czy C#. Język nie przychodzi z mnóstwem bibliotek stworzonych przez działy ds. rozwoju danego języka. Powoduje to, że znajomość czystego C++ jest praktycznie bezużyteczna. Moim zdaniem w tym języku został poczyniony krok naprzód. C++ wspiera tworzenie nowych bibliotek na tyle, że można niemalże modyfikować sam język (przykładem niech będzie biblioteka boost::lambda). Dlatego aby w pełni korzystać z tego wspaniałego narzędzia należy nie tylko poznać C++ i bibliotekę STL, ale również bibliotekę BOOST.

Sam język C++, jak i STL mają wiele wad. Z bardziej uciążliwych można wymienić następujące (język):
&lt;ul&gt;&lt;li&gt;Brakuje odśmiecania&lt;/li&gt;&lt;li&gt;Brakuje delegatów&lt;/li&gt;&lt;li&gt;Brakuje wyrażeń regularnych&lt;/li&gt;&lt;li&gt;Zbyt mało wyspecjalizowany system rzutowania - brakuje np. konwersji liczba &amp;lt;-&amp;gt; std::string, czy bezpiecznej konwersji numerycznej (która ostrzega przy stracie cyfr)&lt;/li&gt;&lt;li&gt;Operatory nie są definiowane grupami (np. osobna definicja += i +)&lt;/li&gt;&lt;li&gt;Brakuje wyspecjalizowanych narzędzi (np. do obsługi systemu plików, reprezentacji czy typów matematycznych, ...)&lt;/li&gt;&lt;li&gt;Skomplikowana składnia konkretna języka, co utrudnia tworzenie narzędzi analizujących kod (np. intellisense)&lt;/li&gt;&lt;/ul&gt;oraz z STLa:
&lt;ul&gt;&lt;li&gt;Brakuje wyrażeń lambda - definiowanie funkcji w miejscu, przez co rzadko korzysta się np. z std::for_each&lt;/li&gt;&lt;li&gt;Uciążliwa składnia dla definiowania iteratorów (b. długie i nieczytelne zapisy). Przydałby się typ auto, który będzie w C++0x&lt;/li&gt;&lt;li&gt;Bardzo słabe wsparcie dla funkcji operujących na zakresach (te, przyjmujące parę iteratorów). Trudno jest np. złożyć funkcję, która będzie aplikowana do każdego elementu. Można korzystać tylko z podstawowych operacji. Np. funkcje plus, multiplies, divides, modulus, negate. Ze składania jest tylko bind1st i bind2nd (w SGI jest też compose do składania funkcji).&lt;/li&gt;&lt;li&gt;Niespójny interfejs. Niestety istnietnie idiomu &lt;a href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove"&gt;erase-remove&lt;/a&gt; o czymś świadczy.&lt;/li&gt;&lt;li&gt;Niejasny interfejs. Zobacz co robi operator[] w &lt;a href="http://www.sgi.com/tech/stl/Map.html"&gt;mapie&lt;/a&gt; gdy nie ma szukanego elementu.
Przydałaby się wersja tego operatora ze słówkiem const.
&lt;/li&gt;&lt;li&gt;Jest to biblioteka na dodanie funkcjonalności do języka. Nie dodaje wyspecjalizowanych narzędzi (sieć, system plików)&lt;/li&gt;&lt;/ul&gt;To te, które przyszły mi teraz do głowy. Biblioteka BOOST została stworzona aby uzupełnić braki STLa i samego języka. Jest wygodna, szybka, ale nie jest pozbawiona wad. Niektóre biblioteki (np. od wątków) potrafią bardzo wydłużyć czas kompilacji (chociaż tutaj powołuję się na jednego z uczestników forum www.gamedev.pl) - sam nie sprawdzałem. Wadą, na którą ja napotykam jest złożoność niektórych implementacji. Jest to niewidoczne dla programisty, do czasu aż nie zostanie wygenerowany błąd. Wielokrotnie zagnieżdżone szablony potrafią z 6 linijkowego kodu wygenerować ponad 1000 linii komunikatu o błędzie. Kiedyś może przytoczę ten przykład. Zainteresowanych odsyłam do książki &lt;a href="http://helion.pl/ksiazki/morecp.htm"&gt;Więcej niż C++. Wprowadzenie do bibliotek Boost&lt;/a&gt;. Nie zrażaj się jednak. W tym konkretnym przypadku wystarczy przeczytać ostatnią linię, aby się dowiedzieć o co chodzi (tak, ta linia ma mniej niż 100 znaków). Na codzień komunikaty też są długie, ale da się je czytać (w szczególności, jeżeli miało się styczność z STL).

Kolejną wadą, jest to że biblioteka jest mało znana. Niestety zdarza mi się rezygnować w projektach, z tego potężnego wsparcia, bo wiem, że osoby chcące wykorzystać mój kod mogą sobie nie poradzić. Oczywiście nigdy nie zrobię tego w projekcie mającym więcej niż 5k linii kodu. To samobójstwo. To "nie poradzić" wynika z ich (negatywnego) lenistwa. Po prostu wiem, że jak tylko usłyszą, że trzeba instalować nową bibliotekę to mogą zrezygnować. Mimo, że instalacja boost jest prosta.

BOOST ma jednak więcej zalet niż wad. Są też znacznie istotniejsze. Wbrew ogólnemu przekonaniu BOOST łatwo się instaluje. Na dowód pokażę jak ja to zrobiłem. Wydałem polecenie:&lt;pre&gt;$ sudo aptitude install libboost-dev libboost-doc&lt;/pre&gt;Przy okazji mam pełną dokumentację, która zazwyczaj przybiera raczej formę tutoriali, niż materiału referencyjnego.

Pod mniej przyjaznymi systemami operacyjnymi może być trochę gorzej, ale i tak instalacja sprowadza się do pobrania biblioteki z &lt;a href="http://www.blogger.com/www.boost.org"&gt;www.boost.org&lt;/a&gt; (klikając na taki wielki czerwony przycisk z prawej).

Korzystanie z biblioteki jest również proste. Biblioteki boost, w większości, nie są kompilowane. Oznacza to, że nie musisz ich linkować do swojego projektu. Aby mieć inteligentne wskaźniki piszesz na początku pliku&lt;pre&gt;#include &amp;lt;boost/shared_ptr.hpp&amp;gt;&lt;/pre&gt;I już są. Nie trzeba przekazywać żadnych opcji do linkera. W przypadku bibliotek regex (wyrażenia regularne) i signals (sygnały, delegaty) trzeba przekazać do linkera odpowiednio: &lt;pre&gt;-lboost_regex i -lboost_signals&lt;/pre&gt; Też nie jest to trudne ;-) To są jedyne z ważnych, wykorzystywanych na co dzień bibliotek, które wymagają linkowania.

Kolejna zaleta to wyśmienita współpraca z STL. Biblioteka BOOST powoduje, że na nowo odkryjesz STL. Np. zaczniesz korzystać z for_each czy transform. BOOST wymienia też wadliwe elementy STL (np. mem_fun i mem_fun_ref).

Biblioteki BOOST są pisane przez wyśmienitych programistów i znawców C++ (np. tych pracujących nad standardem C++). Jest to gwaranację dobrej jakości. Wiem, że nie wszyscy się z tym zgodzą. Ale proponuję np. popatrzeć na wymagania na GSoC dla ludzi piszących biblioteki. Zobaczyć jaka tam jest konkurencja. Jeżeli uważasz, że jesteś lepszy i Twój kod jest pewniejszy to zapisz się na przyszły GSoC. Można trochę zarobić.

Biblioteki BOOST, w końcu, dają programistom C++ wyspecjalizowane narzędzie do większości z typowych zadań. Listę można znaleźć &lt;a href="http://www.boost.org/doc/libs/1_36_0"&gt;tu&lt;/a&gt;.

Niestety są również ludzie, którzy w ogóle nie mają pojęcia o tych bibliotekach, ale głoszą wszędzie, że są złe - np. powolne czy zajmują dużo miejsca w wynikowym pliku. Jest to oczywiście bzdura, ale jak wiadomo łatwiej jest mieć zdanie "to jest złe" niż najpierw poznać bibliotekę, a potem się wypowiedzieć.

Szanuję ludzi, którzy świadomie rezygnują z BOOST (sam tak czasem robię), ale nie lubię gdy ktoś wypowiada się na temat, o którym nie ma nawet bladego pojęcia. Aby pokazać o co chodzi, przytoczę pewną historię. Na forum pewnego dużego, polskiego portalu była dyskusja, w której autor wątku pytał jak przekonwertować std::string na liczbę int. Padło wiele rozwiązań. Jedne ładniejsze, inne co najmniej "dziwne". Zasugerowałem boost::lexical_cast. Myślę, że to narzędzie idealnie rozwiązuje problem - w końcu do tego zostało stworzone. Padła odpowiedź, że autor nie widzi potrzeby wykorzystywania do tego wielkiego boosta. OK, jego wybór. Problem w tym, że z jego pierwszego posta wynikało, że jest raczej początkującym programistą (to potwierdziło się później, gdy okazało się, że jego problem polagał zupełnie na czymś innym). Prawdopodobnie padł ofiarą ludzi mówiących źle o BOOST. Gorzej, że takim zdaniem, pewnie nieumyślnie, mógł zrazić do BOOST innych, którzy byli na dobrej drodze (bo dobrzy programiści unikają zbędnego narzutu). W końcu ktoś, kiedyś wpłynął tak na niego. Pomijając już fakt, że rozwiązanie alternatywne, które sam podał jako działające, w ogóle nie robiło tego co chciał (bo jak rzutowanie stringa na unsigned int poprzez operator() może zadziałać, skoro zachodzi tylko reinterpretacja bitów).

Odrobina wyjaśnienia.
boost::lexical_cast jest w osobnym pliku nagłówkowym (jak wszystkie biblioteki boost). Powoduje to włączenie tylko niezbędnego kodu do programu (więc skąd słowo "wielki"). Użycie trudne też nie jest:&lt;pre&gt;#include &amp;lt;boost/lexical_cast.hpp&amp;gt;&lt;/pre&gt;i w kodzie&lt;pre&gt;std::string str = "1612";
int x = boost::lexical_cast&amp;lt;int&amp;gt;(str);&lt;/pre&gt;Ostatecznie. Implementacja lexical_cast jest na tyle prosta (do napisania i do wymyślenia), że każdy, nawet początkujący, znający szablony sobie poradzi. Jest to zwykłe wykorzystanie operatora &amp;lt;&amp;lt; klasy stringstream.

Chciałbym zachęcić do myślenia, a nie wydawania osądów. Sprawdź sam jak działa boost, gdzie Ci pomaga, gdzie przeszkadza. Dopiero wtedy wydawaj osąd.

Podsumowując. BOOST to duża (ogromna) biblioteka (zbiór bibliotek). Ale tak musi być. Z BOOST przychodzi przecież dużo funkcjonalności. Jednocześnie jest lekka. Budowa BOOST powoduje, że do kodu wynikowego jest włączany tylko ten kod, który jest niezbędny. Biblioteki BOOST są łatwe w instalacji i wykorzystaniu. Są dobrze napisane i działają szybko. Świetnie rozszerzają i uzupełniają STL oraz sam język C++. Mają też wady, ale tak jest w przypadku każdego narzędzia. Teraz &lt;a href="http://www.boost.org/"&gt;zainstaluj&lt;/a&gt; boost.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8672780552696911066?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8672780552696911066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/stl-i-boost.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8672780552696911066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8672780552696911066'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/stl-i-boost.html' title='STL i BOOST'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-4380354702046412567</id><published>2008-09-16T00:00:00.004-07:00</published><updated>2008-09-17T14:18:14.824-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grafika'/><title type='text'>Jak, w szybki sposób, zrobić splot? Separacja i FFT</title><content type='html'>Splot jest bardzo często wykorzystywany w grafice komputerowej. Filtry działają na zasadzie splotu. Weźmy na przykład rozmycie. Ten efekt sprowadza się do wygenerowania nowego koloru dla każdego piksela. Ten kolor jest sumą ważoną kolorów pikseli sąsiednich. Możemy brać sąsiedztwo 8 otaczających pikseli, ale równie dobrze może to być 168 otaczających pikseli. Informacje o wagach poszczególnych kolorów możemy zapisać np. w macierzy. I tak tworząc macierz&lt;pre&gt;
1 2 1
2 4 2
1 2 1&lt;/pre&gt;
Otrzymamy rozmycie. Kolor piksela, w obrazie wyjściowym, ma być sumą kolorów:
&lt;ul&gt;&lt;li&gt;piksela o tej samej pozycji, w obrazie wejściowym (niech będzie to piksel główny), z pewną wagą&lt;/li&gt;&lt;li&gt; pikseli przyległych ścianami do głównego z wagą o połowę mniejszą&lt;/li&gt;&lt;li&gt; pikseli przyległych rogami, z wagą czterokrotnie mniejszą niż piksel główny&lt;/li&gt;&lt;/ul&gt;Drobna uwaga:
Jeżeli użyjesz powyższej macierzy do rozmycia, to oczywiste jest, że musisz podzielić wynik, przez 16 (suma wag).

Jak widać dla każdego piksela trzeba wykonać 9 operacji co daje czas 9n, gdzie n to liczba pikseli. Nie trudno zauważyć skąd to 9. Jeżeli oznaczymy wymiar macierzy przez p, a przez n oznaczymy liczbę pikseli w obrazie, to czas działania wynosi O(p^2 * n).

p^2 może być bolesne w przypadku dużych filtrów. Pokażę dwie sztuczki, pozwalające sprowadzić O(p^2) do O(p) i O(plogp).

Pierwsza jest oczywista, jeżeli przyjrzymy się macierzy wspomnianej wcześniej. Jeżeli popatrzysz na nią odpowiednio długo to zauważysz, że jest iloczynem dwóch wektorów: transpozycji [1,2,1] oraz samego [1,2,1]. Zatem wystarczy macierz rozłożyć na iloczyn wektorów, otrzymując filtry jednowymiarowe - jeden poziomy i jeden pionowy. Pozostaje tylko nałożyć oba filtry niezależnie. W naszym przypadku możemy najpierw rozmyć w poziomie (dla każdego piksela sumujemy wartość pikseli wejściowych: poprzedniego, następnego i dwa razy środkowego). Następnie wynik traktujemy filtrem pionowym - analogicznie do poziomego.

Wszystko ok, ale nie każdy filtr jest separowalny (czyt. nie każdy daje się rozłożyć). Oczywiście nawet w przypadku separowalnych filtrów, nie zawsze od razu widać jak tego dokonać. W przypadku filtru dwumianowego (którego przykładem jest powyższa macierz) jest to łatwe.

Wtedy z pomocą przychodzi Szybka Transformata Fouriera. Jeżeli przez F(x) oznaczymy Transformatę Fouriera na obrazie x, przez P i Q dwa obrazy, przez * splot, a przez x mnożenie element po elemencie, to możemy napisać:&lt;pre&gt;F(P * Q) = F(P) x F(Q)&lt;/pre&gt;"Fachowo" możemy powiedzieć (czyt. przynudzić), że Transformata Fouriera to homomorfizm z algebry A w algebrę B. Gdzie algebra A to zbiór obrazów w zwykłej bazie (przestrzennej - obraz zapisany jako wektor pikseli) ze splotem, a B to zbiór obrazów w bazie Fouriera z mnożeniem element po elemencie.

Oczywiście filtr można traktować jako obraz. Co z tego wynika? Tyle, że aby uzyskać splot obrazów w przestrzeni Fouriera nie trzeba robić splotu i potem nakładać transformatę (lewa strona). Można najpierw zrobić osobno transformaty dwóch obrazów, a potem przemnożyć wynikowe obrazy element po elemencie (tzn. element P[i,j] przez Q[i,j] dla każdego i,j) (prawa strona).

Dowód tego faktu, to zwykłe przekształcanie wzorków (o ile zna się definicje transformaty, splotu i twierdzenie o przesunięciu (ang. &lt;a href="http://www.technick.net/public/code/cp_dpage.php?aiocp_dp=guide_dft_shift_theorem"&gt;shift theorem&lt;/a&gt;)).

Nas nie interesuje F(P*Q), dlatego skorzystamy z tego, że potrafimy wrócić z przestrzeni Fouriera. Chcemy znać P*Q, możemy więc policzyć &lt;pre&gt;invF(F(P) x F(Q))&lt;/pre&gt;gdzie invF to transformata odwrotna. Mamy dwie transformaty i jedną odwrotną, co daje czas O(plogp). Mnożenie element po elemencie nie wpływa na koszt asymptotyczny.

Podsumowując:
&lt;ol&gt;&lt;li&gt;Liczymy transformatę obrazu&lt;/li&gt;&lt;li&gt;Zamieniamy filtr na obraz&lt;/li&gt;&lt;li&gt;Liczymy transformatę filtru&lt;/li&gt;&lt;li&gt;Mnożymy wyniki element po elemencie&lt;/li&gt;&lt;li&gt;Liczymy transformatę odwrotną wyniku&lt;/li&gt;&lt;/ol&gt;Transformatę Fouriera (i odwrotną) można policzyć wykorzystując bibliotekę &lt;a href="http://www.fftw.org/"&gt;fftw&lt;/a&gt;.

Pozostaje jeszcze kwestia zamiany obrazu w filtr. Musi on mieć te same wymiary co obraz wejściowy. Tę zmianę robi się poprzez zmianę wymiarów filtru i przesunięcie środka filtru w lewy dolny róg, pozostałe elementy przesuwa się tak, aby nie zmieniły pozycji względem środkowego. Trzeba pamiętać, że lewa krawędź obrazu jest "zlepiona" z prawą, a górna z dolną.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-4380354702046412567?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/4380354702046412567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/jak-w-szybki-sposb-zrobi-splot.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4380354702046412567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/4380354702046412567'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/jak-w-szybki-sposb-zrobi-splot.html' title='Jak, w szybki sposób, zrobić splot? Separacja i FFT'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-7489254062443513052</id><published>2008-09-14T00:00:00.000-07:00</published><updated>2008-09-14T00:00:00.396-07:00</updated><title type='text'>Do czego nie służy kalendarz?</title><content type='html'>Co przychodzi Ci w pierwszej chwili do głowy gdy mowa o zarządzaniu czasem? Kalendarz? Nie do końca dobrze. Pamiętam, że gdy po raz pierwszy potrzebowałem pomocy, z zarządzania czasem, sięgnąłem po kalendarz. Niestety niewiele to pomogło. Kalendarz po kilku dniach wylądował w biurku. Mniej więcej po roku, kupiłem kolejny - zainspirowany pewną książką na temat korzystania z tego cudu techniki. Samo posiadanie kalendarza, nie zrobi z Ciebie dobrze zorganizowanej osoby. Potrzebujesz jeszcze przynajmniej dwóch rzeczy:
&lt;ul&gt;&lt;li&gt;Wiedzy, jak korzystać z kalendarza&lt;/li&gt;&lt;li&gt;Dobrego organizera (i wiedzy jak z niego korzystać)&lt;/li&gt;&lt;/ul&gt;Drugą kwestię pewnie kiedyś postaram się opisać. Co do pierwszej - odpowiedź jest prosta. W kalendarzu powinny się znaleźć _wszystkie_ te zdarzenia, które mają _ustalony_ _termin_ i _żadne_ inne informacje. Proste, ale mój pierwszy kalendarz nie wytrzymał. Kluczowe są słowa: wszystkie, ustalony termin i żadne.

"Wszystkie" oznacza, że możesz w pełni zaufać temu, prywatnemy asystentowi. Musisz nie tyle wiedzieć, co być pewnym, że jeżeli coś znajdzie się w kalendarzu, to na pewno o tym nie zapomnisz i co ważniejsze - jeżeli czegoś nie ma w kalendarzu na dzisiaj, to na pewno nie powinno w nim być.

"Ustalony termin". Ludzie mają tendencję to korzystania, z kalendarza jako spisu telefonów, księgi przychodów i rozchodów, notatek, i co najbardziej bolesne, do zapisywania swojej listy todo w kalendarzu. "Dzięki" takiem zachowaniu te czynności, które mają termin, po wymieszaniu z tymi bez terminu, zostają zapomniane. W kalendarzu ma być MAŁO wpisów na dzień. Tak, aby były dobrze widoczne. Tak, abyś wiedział, że to co jest w kalendarzu na dzisiaj, jest do wykonania dzisiaj. Jeżeli wpisy Ci się nie mieszczą i wszystkie muszą być bezwzględnie wykonane w danym terminie, to... kup większy kalendarz.

Spotkałem się z książką (tę, którą wspomniałem na początku), gdzie autor opisywał system oparty o kalendarz. Wpisywał wszystko (to z terminem i to bez) do kalendarza. Następnego dnia, rano, przepisywał to, czego nie zrobił poprzediego dnia. Mój kalendarz po tygodniu takiej terapii wyglądał jakby małe dziecko dorwało się z długopisem i po nim mazało. Trudno było znaleźć potrzebne informacje. Co gorsze, informacje o ważnej rocznicy ginęły, w gąszczu informacji o projektach czy zakupach.

"Żadne" informacje bez terminu nie powinny być w kalendarzu. Problem z nimi jest taki, że zaciemniają obraz, oraz nie ma co z nimi zrobić gdy się czegoś nie wykona danego dnia. Zazwyczaj powoduje to stres i zniechęcenie.

Z doświadczenia mogę powiedzieć, że po wpisaniu w kalendarz czynności, musi być ona wykonana danego dnia. Jeżeli nie, to jest problem. Co z nią zrobić? Przepisać na kolejny dzień? W moim przypadku już po 4 dniach pojawiał się koszmar. Na dany dzień były wpisane czynności, planowane na "o 4 dni wcześniejszą datę". W ciągu dnia, trudno było wykonać to co przesunąłem - nie wspominając o zaplanowanych działaniach.

Jeżeli nie masz kalendarza, to koniecznie się w niego zaopatrz. Podstawa to mieć go zawsze przy sobie, rano go przeglądać i zapisywać w nim wszystko to, co ma ustalony termin. Jeżeli możesz zaufać swojemu kalendarzowi bezgranicznie, to jesteś na dobrej drodze!

Przyznam się, że mój kalendarz ma jeszcze jedną funkcję. Przeznaczyłem dodatkowe kartki na "listę spraw przychodzących". Jeżeli jesteś ciekawy co to jest, oraz dlaczego NIE NALEŻY korzystać z list TODO, to zapraszam do lektury wspaniałej książki &lt;a href="http://www.empik.com/sztuka-efektywnosci,2420056,p%3Fgclid%3DCIuUhL7x0ZUCFRdatAod8S81Xg"&gt;Getting things done&lt;/a&gt;. Możesz też spojrzeć w &lt;a href="http://pl.wikipedia.org/wiki/Getting_Things_Done"&gt;Wikipedię&lt;/a&gt;, ale informacje tam zawarte są w bardzo skromnej ilości.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7489254062443513052?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7489254062443513052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/do-czego-nie-suy-kalendarz.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7489254062443513052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7489254062443513052'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/do-czego-nie-suy-kalendarz.html' title='Do czego nie służy kalendarz?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-7345562512246739445</id><published>2008-09-13T00:00:00.001-07:00</published><updated>2008-09-13T00:00:00.700-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Magia pierwszej linii -*- GNU/Emacs -*-</title><content type='html'>Załóżmy, że piszesz prosty program i zamierzasz go zmieścić w jednym pliku. Niech wymaga on niestandardowej kompilacji (np. dodanie ustawień dla wxWidgets). Niestety musisz, w takim przypadku utworzyć plik Makefile i zarządzać już dwoma plikami. Tak nie musi być! O ile korzystasz z GNU/Emacs.

GNU/Emacs pozwala, aby pierwsza linia pliku zawierała specjalny znacznik -*- -*-. Między tymi trójznakami umieszcza się ustawienia dla GNU/Emacs, które zostaną włączone tuż po otwarciu pliku w tym edytorze.

Aby rozwiązać przedstawiony problem (na przykładzie języka C++) wystarczy w pierwszej linii umieścić następujący komentarz:&lt;pre&gt;// -*- compile-command: "g++ main.cpp `wx-config --libs --cxxflags`" -*-&lt;/pre&gt;Definiujemy tu zmienną compile-command.

Można także ustawić specjalny tryb przy otwarciu danego pliku. Np. dla elisp będzie to:&lt;pre&gt;; -*- emacs-lisp -*-&lt;/pre&gt;lub&lt;pre&gt;; -*- mode: emacs-lisp -*-&lt;/pre&gt;Można podać więcej niż jedno ustawienie na raz. Np. aby ustawić tryb org-mode i jednocześnie ustawić auto-fill-mode (pilnuje, żeby tekst nie był zbyt długi w linii) należy napisać&lt;pre&gt;# -*- mode: org; mode: auto-fill -*-&lt;/pre&gt; Dość przydatne. Jest to stara sztuczka. Większość osób korzystających z GNU/Emacs wykorzystuje tę specjalną formę pierwszej linii.

Jest jeszcze coś. O ile -*- znam dość długo, to GNU/Emacs zaskoczył mnie ostatnio pewną ciekawą właściwością. Potrafi rozpoznawać typ pliku po pierwszej linii, która mówi jaki program uruchomić. Jeżeli pisałeś w BASHu to pewnie znasz początek skryptu:&lt;pre&gt;#!/usr/bin/sh&lt;/pre&gt;podobnie, w Perlu, pisze się&lt;pre&gt;#!/usr/bin/perl&lt;/pre&gt; W przypadku tego typu skryptów nie trzeba się martwić ani o -*- ani o rozszerzenie (dobrą praktyką, w Perlu, jest rezygnacja z rozszerzenia - z wyjątkiem modułów).

Jak powszechnie wiadomo, krzywa nauki GNU/Emacs jest prawie pionowa. Każdego dnia edytor zaskakuje nas nowymi możliwościami!

I ostatnia dobra wiadomość (same dobre wieści dzisiaj). Wielu twórców oprogramowania dodaje specjalną linię -*- do swoich skryptów. Dla przykładu w .xbindkeysrc pierwsza linia to:
# For the benefit of emacs users: -*- shell-script -*-
Miłego dnia / nocy, zależnie od tego, co masz w planach ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7345562512246739445?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7345562512246739445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/magia-pierwszej-linii-gnuemacs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7345562512246739445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7345562512246739445'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/magia-pierwszej-linii-gnuemacs.html' title='Magia pierwszej linii -*- GNU/Emacs -*-'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-7047379592289445767</id><published>2008-09-12T00:00:00.001-07:00</published><updated>2008-09-12T00:00:00.541-07:00</updated><title type='text'>Lenistwo, niecierpliwość i pycha - trzy cnoty programisty</title><content type='html'>Tak, te trzy cechy są niezwykle cenne podczas programowania.

Wrodzone lenistwo powoduje, że:
&lt;ul&gt;&lt;li&gt;podczas specyfikowania będziesz słuchał swojego klienta, bo nie chcesz poprawiać projekt, który jest "nie do końca dobry"&lt;/li&gt;&lt;li&gt;Nie będziesz duplikował kodu, bo po co później poprawiać kod w 20   miejscach, jeżeli można w jednym&lt;/li&gt;&lt;li&gt;Napiszesz testy do każdego modułu przed rozpoczęciem kodowania, bo dzięki temu będziesz od razu wiedział kiedy projekt jest skończony, co oszczędzi Ci trochę pisania&lt;/li&gt;&lt;li&gt;będziesz pisał dobrą i przydatną dokumentację, dzięki czemu będzie mniej pracy, za miesiąc, przy próbie skorzystania z funkcjonalności&lt;/li&gt;&lt;li&gt;no i jak? jesteś leniwy? to wymyśl kilka kolejnych. Mógłbym tę listę kontynuować bez końca,... ale mi się nie chce ;-P&lt;/li&gt;&lt;/ul&gt;Łatwo zauważyć, że większość zasad "dobrego programowania" została wymyślona przez "leni". Dziwnym zbiegiem okoliczności, wszystkie zasady ograniczają ilość pracy do wykonania.

Niecierpliwość to błogosławieństwo. Dzięki niej, nie będziesz siedział nad projektem w nieskończoność. Zaczniesz kodować jak najszybciej, aby od razu widzieć efekty. Kodowanie to najważniejszy proces. Dzięki niemu powstaje produkt. Pamiętaj, że lenistwo nie pozwoli Ci, zbyt wcześnie, zacząć pisać.

Pycha pozwoli Ci pisać lepszą dokumentację. W końcu, zależy Ci, żeby TWÓJ kod i TWOJA dokumentacja były jasne i łatwe do zrozumienia. Inni programiści na pewno będą Ci wdzięczni. Przecież to, o ich, uznanie Ci chodzi!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-7047379592289445767?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/7047379592289445767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/lenistwo-niecierpliwo-i-pycha-trzy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7047379592289445767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/7047379592289445767'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/lenistwo-niecierpliwo-i-pycha-trzy.html' title='Lenistwo, niecierpliwość i pycha - trzy cnoty programisty'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2801297396381189004</id><published>2008-09-11T00:00:00.001-07:00</published><updated>2008-09-11T00:00:00.724-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Zawijanie wierszy w GNU/Emacs</title><content type='html'>Jeżeli pracujesz już chwilę z tytułowym edytorem, to na pewno zauważyłeś, że może on zawijać wiersze. Dokładniej, chodzi o to, że gdy linia staje się zbyt długa to po prawej stronie pojawia się mała zawinięta strzałka. Tekst jest kontynuowany w kolejnej linii.  Pewnie wiesz, że do zmiany trybów z zawijaniem i bez zawijania, służy komenda toggle-truncate-lines.

To o czym chcę napisać to zawijanie wierszy gdy masz podział na kilka okien.  Podziel okno na dwie części (C-x3). Spróbuj teraz włączyć zawijanie wierszy. I jak? toggle-truncate-lines nie działa?

Pamiętam, że gdy zostałem zapytany, jak zmienić to zachowanie, to nie wiedziałem, co z tym zrobić. Odpowiedź znalazłem oczywiście w systemie pomocy.

C-hf toggle-truncate-lines, enter na podkreślonym linku do źródła i... już widać, że za ustawienie odpowiada zmienna truncate-lines.  Teraz C-hv truncate-lines i widzimy, że zmienna jest nadpisywana przez zmienną truncate-partial-width-windows.

Wszystko jasne. M-: i wpisujemy (setq truncate-partial-width-windows nil)

Czasem dobry system pomocy potrafi, na prawdę, POMÓC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2801297396381189004?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2801297396381189004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/zawijanie-wierszy-w-gnuemacs.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2801297396381189004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2801297396381189004'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/09/zawijanie-wierszy-w-gnuemacs.html' title='Zawijanie wierszy w GNU/Emacs'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-2382374575016434701</id><published>2008-08-24T07:00:00.001-07:00</published><updated>2008-08-24T07:00:01.234-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Mechanizm `hook` w elisp</title><content type='html'>Opowiem czym są hooki i pokażę przykład zastosowania - specjalnie dla programistów ;-)

Czym jest hook? W elisp jest to lista funkcji. Specjalnego znaczenia dostaje dzięki temu, że kod włączający np. major-mode wywołuje każdą z funkcji na tej liście. Dla przykładu, gdy jest włączany c-mode zostają wywołane wszystkie funkcje z listy c-mode-hook. Łatwo to sprawdzić, korzystając z systemu pomocy. C-hf c-mode. Jeżeli zajrzysz to dowiesz się, że jest jeszcze jeden hook wywoływany w c-mode. Cała wartość tego mechanizmu bierze się stąd, że tę listę można modyfikować. Najczęściej dodaje się do niej funkcje. Ponieważ nazwy tych list są w przestrzeni zmiennych to można je podejrzeć poprzez C-hv nazwa-hooka.

Składania jest prosta. Dodanie minor-mode, który sprawdza poprawność tekstu (ortografia) wystarczy napisać&lt;pre&gt;(add-hook 'text-mode-hook 'flyspell-mode)&lt;/pre&gt; w swoim .emacs. Jeżeli czytałeś mój poprzedni wpis o GNU/Emacs, to powinieneś dobrze rozumieć co tu się dzieje. Dla tych co popełniają straszliwy błąd, nie czytając dokumentacji: nowe funkcje są dodawane na początek hooka. Zatem nie jest to kolejka, tylko stos. Można zmienić zachowanie poprzez dodatkowy argument. Zapraszam do lektury C-hf add-hook.

Teraz obiecany przykład. Zamienimy __ na -&gt; w tekście. Strzałkę pisze się źle i raczej wolno.&lt;pre&gt;(defun zamiana (first last len)
 "Zamienia __ na -&gt;"
 (interactive)
 (if (and (boundp 'major-mode)
      (member major-mode (list 'c-mode 'c++-mode)))
     (cond
      ((and (&gt; (point) 2)
        (string-equal (buffer-substring (- (point) 2) (point)) "__"))
   (progn (delete-backward-char 2) (insert "-&gt;")))

      ((and (&gt; (point) 3)
        (string-equal (buffer-substring (- (point) 3) (point)) "-&gt;_"))
   (progn (delete-backward-char 3) (insert "__"))))))

(add-hook 'after-change-functions 'zamiana)&lt;/pre&gt;Mam u siebie trochę więcej tego typu zamian, ale ten obrazuje użycie hook. Inne nie wprowadzają wiele nowego. Kiedyś wprowadziłem te modyfikacje po przejrzeniu &lt;a href="http://infolab.stanford.edu/%7Emanku/dotemacs.html"&gt;tego pliku dot emacs&lt;/a&gt;. Trzeba przyznać, że pomysł jest trafiony.

Co się dzieje powinno być jasne, jeżeli tylko skorzystasz trochę z C-hf. Tworzymy funkcję, która sprawdza czy aktualny tryb jest c-mode lub c++-mode. Jeżeli nie to kończy działanie. Następnie sprawdzane są dwa przypadki. Czy poprzednio było __ oraz czy było -&gt;_. W pierwszym przypadku kasujemy __ i wstawiamy -&gt;, w drugim kasujemy -&gt;_ i wstawiamy __. Jest drobny błąd. Jeżeli napiszesz ___ i dasz kolejny znak _ to zamieni się w __-&gt;. Trzeba sprawdzić w pierwszym przypadku czy nie występuje wzorzec ___ przed znakiem. Pozostawiam to jako proste ćwiczenie. Przyda się funkcja not.

Funkcja point zwraca aktualne położnie w buforze (numer znaku). progn pozwala na wykonanie ciągu funkcji (elisp jest językiem funkcyjnym, a w takich dopuszcza się składanie funkcji, ale nie wykonywanie ich po kolei - bo i tak nie ma skutków ubocznych, a wartością wyrażenia może być jedna wartość).

Po zdefiniowaniu tej funkcji dodajemy ją do hooka after-change-function. To tyle ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2382374575016434701?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2382374575016434701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/mechanizm-hook-w-elisp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2382374575016434701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2382374575016434701'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/mechanizm-hook-w-elisp.html' title='Mechanizm `hook` w elisp'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-8939960141609196791</id><published>2008-08-21T00:00:00.001-07:00</published><updated>2008-08-21T00:00:00.535-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Aliasy</title><content type='html'>Powłoka bash udostępnia bardzo przyjemny mechanizm aliasów. Wystarczy napisać:&lt;pre&gt;$ alias cdcur='cd sciezka/do/aktualnego/projektu'&lt;/pre&gt;aby móc, od tej pory, pisać cdcur zamiast cd dluga_sciezka.

Ta "sztuczka" ma duże znaczenie. Pozwala  skrócić pisanie, ale przede wszystkim pozwala zawsze wpisywać tylko cdcur, żeby przejść do katalogu z aktualnym projektem. Dzięki temu przy rozpoczynaniu nowego projektu wystarczy podmienić jedną zmienną.

Aliasy najczęściej stosuje się właśnie w celu uproszczenia i ujednolicenia dostępu do komend. Dzięki nim (podobnie jak dzięki plikom &lt;a href="http://lmmilewski.blogspot.com/2008/08/czy-make-potrafi-wysprzta-mieszkanie-po.html"&gt;Makefile&lt;/a&gt;) nie trzeba zapamiętywać długich komend. Mniejsza szansa na pomyłkę. Pisania też mniej. Jak widać, wszystko staje się prostsze.

Aliasy można wykorzystywać do:
&lt;ul&gt;&lt;li&gt;Skracania komend (np. alias ec='emacsclient' czy mój ulubiony: alias e='emacs')
&lt;/li&gt;&lt;li&gt;Stworzenia mechanizmu zakładek (aliasy na ulubione katalogi)&lt;/li&gt;&lt;li&gt;Usprawniania komend (np. alias ls='ls -lah')&lt;/li&gt;&lt;li&gt;Zabezpieczania się (np. alias rm='rm -i')&lt;/li&gt;&lt;li&gt;Jest jeszcze wiele innych zastosowań ;-D&lt;/li&gt;&lt;/ul&gt;Na pewno znajdziesz wiele zastosowań dla tego mechanizmu. Dodam jeszcze, że aby aliasy nie zostały zapomniane należy ich definicje wpisać do pliku ~/.bashrc
Plik .bashrc można następnie przeładować poleceniem: &lt;pre&gt;$ . ~/.bashrc&lt;/pre&gt;Dokładnie tak. Poleceniem jest . (kropka).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-8939960141609196791?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/8939960141609196791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/aliasy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8939960141609196791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/8939960141609196791'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/aliasy.html' title='Aliasy'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3049494918959594495</id><published>2008-08-20T05:00:00.002-07:00</published><updated>2008-08-20T05:00:01.739-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>O bindowaniu funkcji pod przyciski.</title><content type='html'>W systemie Linuks można bardzo łatwo podpiąć różne polecenia pod kombinacje klawiszy. Mam np. Alt+1 terminal, Alt+2 GNU/Emacs, Alt+3 firefox, Alt+4 lokalne wiki oraz obsługę klawiszy multimedialnych.

Cała procedura jest trywialna. Wystarczy zainstalować pakiet &lt;a href="http://hocwp.free.fr/xbindkeys/xbindkeys.html"&gt;xbindkeys&lt;/a&gt;. Dalej jest już tylko konfiguracja. Mój plik konfiguracyjny wygląda tak:&lt;pre&gt;
# For the benefit of emacs users: -*- shell-script -*-
###########################
# xbindkeys configuration #
###########################
#
# Version: 1.7.1
#
# If you edit this file, do not forget to uncomment any lines
# that you change.
# The pound(#) symbol may be used anywhere for comments.
#
# To specify a key, you can use 'xbindkeys --key' or
# 'xbindkeys --multikey' and put one of the two lines in this file.
#
# The format of a command line is:
#    "command to start"
#       associated key
#
#
# A list of keys is in /usr/include/X11/keysym.h and in
# /usr/include/X11/keysymdef.h
# The XK_ is not needed.
#
# List of modifier:
#   Release, Control, Shift, Mod1 (Alt), Mod2 (NumLock),
#   Mod3 (CapsLock), Mod4, Mod5 (Scroll).
#

# The release modifier is not a standard X modifier, but you can
# use it if you want to catch release events instead of press events

# By defaults, xbindkeys does not pay attention with the modifiers
# NumLock, CapsLock and ScrollLock.
# Uncomment the lines above if you want to pay attention to them.

#keystate_numlock = enable
#keystate_capslock = enable
#keystate_scrolllock= enable


# APPS

"xterm -background black -foreground white"
 Mod1+1

"emacs"
 Mod1+2

"firefox"
 Mod1+3

"iceape localhost:8000"
 Mod1+4

# / APPS


# AMAROK

"dcop amarok player next"
   m:0x0 + c:153

"dcop amarok player prev"
   m:0x0 + c:144

"dcop amarok player playPause"
   m:0x0 + c:162

"dcop amarok player stop"
   m:0x0 + c:164

# / AMAROK


# KMIX

"dcop kmix Mixer0 increaseVolume 0"
   m:0x0 + c:176

"dcop kmix Mixer0 decreaseVolume 0"
   m:0x0 + c:174

"dcop kmix Mixer0 toggleMasterMute"
   m:0x0 + c:160

# / KMIX



##################################
# End of xbindkeys configuration #
##################################&lt;/pre&gt;Sposób konfiguracji jest umieszczony w komentarzu na początku pliku. W skrócie procedura wygląda tak:
&lt;ol&gt;&lt;li&gt;Odpal xbindkeys --key. Pojawi się małe białe okienko. Naciśnij kombinację klawiszy, która Cię interesuje.&lt;/li&gt;&lt;li&gt;W terminalu, z którego odpalałeś komendę pojawi się fragment, który należy wkleić do pliku konfiguracyjnego (~/.xbindkeysrc). Dla Alt + 5 wygląda to tak&lt;pre&gt;"(Scheme function)"
    m:0x8 + c:14
    Alt + 5&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Teraz należy podmienić fragment w cudzysłowach na komendę, która ma być wykonana.&lt;/li&gt;&lt;li&gt;To tyle. Wystarczy przeładować deamona xbindkeys. (np. killall xbindkeys &amp;amp;&amp;amp; xbindkeys)&lt;/li&gt;&lt;/ol&gt;Jeżeli w kroku 2 plik konfiguracyjny nie istniał, to należy go utworzyć&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3049494918959594495?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3049494918959594495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/o-bindowaniu-funkcji-pod-przyciski.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3049494918959594495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3049494918959594495'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/o-bindowaniu-funkcji-pod-przyciski.html' title='O bindowaniu funkcji pod przyciski.'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-2203920660521232112</id><published>2008-08-19T07:00:00.002-07:00</published><updated>2008-08-19T07:00:01.421-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickTips'/><title type='text'>Jak myślenie o celu może pomóc?</title><content type='html'>Często zdarza się, że utykamy w martwym punkcie. Brakuje już kolejnych pomysłów na rozwiązanie. Co wtedy? Jest prosty sposób. 

Te objawy wskazują na próbę rozwiązania niewłaściwego problemu. Właśnie dlatego wystarczy wziąć kartkę papieru i napisać co się chce osiągnąć. "Sprecyzuj cel i myśl o nim" - tak brzmi porada. Nie wystarczy zastanawianie się, trzeba zapisać. Jeżeli nie masz z tym kłopotów, to nic nie tracisz - poświęcisz dwie minuty na spisanie myśli. W moim przypadku najczęściej jest kłopot (niemały).

Jak można rozwiązać problem, nie rozumiejąc go? To tak, jakby, nauczyciel pomyślał sobie zadania i kazał uczniom je rozwiązywać. Jak mają to zrobić?

W moim przypadku to się sprawdza podczas programowania (zawsze mam pod ręką plik małych karteczek), na różnych egzaminach, a nawet podczas grania w gry. Właściwie, zaobserwowałem to grając w Tetris ;-) Potem jeszcze wielokrotnie odkrywałem tę zasadę "na nowo" w różnych książkach, czy w internecie.

Myślę, że w tym momencie mogę polecić książkę &lt;a href="http://merlin.pl/Pragmatyczny-programista-Od-czeladnika-do-mistrza_Andrew-Hunt-David-Thomas/browse/product/1,299928.html"&gt;Pragmatyczny programista&lt;/a&gt;. Warto też zaglądać na &lt;a href="http://c2.com/cgi/wiki?WelcomeVisitors"&gt;wiki&lt;/a&gt;, jest tam dyskusja o "patrzeniu na cel". Niestety nie mogę teraz jej znaleźć.

Wspomina też o tym &lt;a href="http://en.wikipedia.org/wiki/George_P%C3%B3lya"&gt;George Polya&lt;/a&gt; w swoim &lt;a href="http://en.wikipedia.org/wiki/How_to_Solve_It"&gt;"Jak to rozwiązać?"&lt;/a&gt;. Jeżeli dobrze pamiętam to także Edward de Bono w &lt;a href="http://helion.pl/ksiazki/myslat.htm"&gt;"Myślenie Lateralne"&lt;/a&gt; poświęca chwilę na rozumienie celu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-2203920660521232112?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/2203920660521232112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/jak-mylenie-o-celu-moe-pomc.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2203920660521232112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/2203920660521232112'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/jak-mylenie-o-celu-moe-pomc.html' title='Jak myślenie o celu może pomóc?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-3159064648904695171</id><published>2008-08-19T05:00:00.005-07:00</published><updated>2008-08-19T05:00:00.592-07:00</updated><title type='text'>Czym będzie QuickTips?</title><content type='html'>Wpadł mi do głowy pomysł (ok, ok... nie jest mój. Zajrzyj na &lt;a href="http://perl.about.com/cs/qt.htm"&gt;stronkę, która mnie zainspirowała&lt;/a&gt;). Będę, czasami, zamieszczał na blogu króciutkie porady. Planuję wrzucać skrypty w Perlu, programiki w elisp, programy w C++, krótkie recenzje książek, czy wreszcie porady na temat przyspieszania wykonywania zadań. Mimo niewielkiej długości, wszystkie wpisy mają za zadanie rozwiązywać jeden, konkretny problem (niekoniecznie błahy). 

Myślę, że to dobry pomysł w obliczu, ostatnio "popełnionych" przeze mnie, dłuższych wpisów. Pierwszy, próbny opublikuję dzisiaj o 16.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-3159064648904695171?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/3159064648904695171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/czym-bdzie-quicktips.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3159064648904695171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/3159064648904695171'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/czym-bdzie-quicktips.html' title='Czym będzie QuickTips?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-9133673682753588011</id><published>2008-08-17T00:00:00.005-07:00</published><updated>2008-08-19T05:11:17.058-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNU/Emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Co każdy powinien wiedzieć o GNU/Emacs?</title><content type='html'>GNU/Emacs to edytor tekstu. Bardzo dobrze udokumentowany edytor tekstu. Z tego powodu postanowiłem na początek dać wędkę, zamiast ryby i opisać podstawy działania tego narzędzia oraz wykorzystanie systemu pomocy. Jeżeli uważasz, że w przypadku potrzeby zmiany zachowania edytora korzystanie z pomocy jest bez sensu, to GNU/Emacs stanowczo nie jest dla Ciebie (bez urazy). Jeżeli uważasz, że edytora nie trzeba dostosowywać do własnych potrzeb, to GNU/Emacs również nie jest dla Ciebie. Skorzystaj z VC, NetBeans czy Eclipse do edycji kodu. W ten sposób oszczędzisz czas, potrzebny na konfigurację (bo jej nie będzie). Tyle, że jednocześnie nie zyskasz czasu na dodaniu do Twojego edytora cechy, o której projektanci gotowych środowisk nie pomyśleli. Tak to już jest. Możesz albo zdać się na łaskę panów od klepania środowiska pracy, albo samemu poświęcić nieco czasu i móc zrobić DOSŁOWNIE wszystko. W tym drugim przypadku możesz też oczywiście skorzystać z pracy innych. Dostaniesz nawet kod źródłowy, gdybyś chciał coś zmienić.

Z tym wszystko nie przesadzam. Mówi się, że dobre narzędzie, to takie, które spełnia oczekiwania, nadaje się do tego, co przewidział twórca. Świetne narzędzie to takie, które można zastosować do celów, o których twórca nawet nie marzył. GNU/Emacs to edytor tekstu. Bez trudu można w nim znaleźć menadżer plików (dired-mode), przeglądarkę plików graficznych (tumme), przeglądarkę www (w3m), edytor filmów (!!!) gneve (trzeba doinstalować),  klient poczty,  czytnik newsów,  dwa tryby imitujące inny dobry edytor VI (całkowita zmiana sposobu zachowania),  kalendarz, kalkulator  (taki porządny z algebrą liniową, liczbami zespolonymi,...), organizer, edytor TeX (z debuggerem), zintegrowane środowisko dla języka Java (jdee), tryby dla większości języków programowania, z możliwością uruchomienia interpretera w buforze (np. dla Pythona, OCamla, Haskella, ...), gry (dokładnie tak. Są szachy, jest wężyk, wieże Hanoi i parę innych) i wiele, wiele innych.

Myślę, że jesteś już przekonany, że jeżeli zabraknie jakiejkolwiek cechy środowiska programistycznego to da się ją dorobić. Jak dla mnie, brakuje intellisense dla C++. Widziałem już różne, ale żadne nie jest tak dobre by stosować je zamiast zwykłych dynamic abbreviation obecnych w GNU/Emacs. &lt;span style="font-weight: bold;"&gt;&lt;/span&gt;

GNU/Emacs został zaprojektowany w ciekawy sposób. U podstaw leży jądro edytora, napisane w języku C. Znajduje się w nim interpreter języka elisp (dialekt Lispu) oraz podstawowe funkcje związane z pracą edytora. Drugą warstwę (znacznie większą, jeżeli popatrzymy na objętość) tworzy kod napisany w języku elisp. Tak więc, edytor GNU/Emacs to miliony wierszy linii kodu w języku elisp oraz "core" pozwalający ten kod interpretować.

Jedną z podstawowych funkcji jest self-insert-command. Przyjmuje jeden argument. Jest wywoływana za każdym razem, gdy zostanie naciśnięty klawisz, którego symbol zostanie wprowadzony do edytora. Innymi słowy gdy naciśniesz literę 'k' to zostanie wywołana funkcja self-insert-command z argumentem 'k'. Ta funkcja spowoduje wpisanie 'k' do aktywnego bufora (czyli literka 'k' pojawi się na ekranie). self-insert-command jest jedną z tych funkcji, które są napisane w C.

Bardzo często są także wywoływane takie funkcje jak next-line, previous-line, backward-char, forward-char. Wbrew temu co możesz myśleć, pierwsze dwie nie są napisane w C! Zostały zdefiniowane w pliku simple.el i są napisane w języku elisp. Co może Cię jeszcze bardziej zaskoczyć, wszystkie 4 wymienione funkcje przyjmują argumenty (opcjonalne). next-line i previous-line przyjmują nawet po 2.

Powinieneś już mniej więcej widzieć, że praktycznie wszystko jest sterowane poprzez kod w elisp. Pewnie zastanawiasz się co to daje? Ogromną elastyczność. Większa część GNU/Emacs to kod w elisp. Teraz będzie niespodzianka. Ten Edytor można konfigurować... pisząc w elisp. Spróbuj wyobrazić sobie jakie to daje możliwości konfiguracji i dostosowania do własnych potrzeb. Możesz np. podpiąć wywołanie new-line pod klawisz j. Zawsze gdy naciśniesz j edytor przejdzie do następnej linii (a nie wstawi znaku 'j'). Spróbuj coś takiego zrobić w innym edytorze. W GNU/Emacs wystarczy napisać w pliku konfiguracyjnym&lt;pre&gt;(global-set-key (kbd "j") 'next-line)&lt;/pre&gt;Spróbujmy razem. Włącz GNU/Emacs. Powinieneś zobaczyć okno, wyglądające jak poniższe.
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_P9Sn6SUv534/SKbV-mfJceI/AAAAAAAAAAU/3EKh60WoCis/s1600-h/okno_emacs.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_P9Sn6SUv534/SKbV-mfJceI/AAAAAAAAAAU/3EKh60WoCis/s320/okno_emacs.jpg" alt="" id="BLOGGER_PHOTO_ID_5235106888324575714" border="0" /&gt;&lt;/a&gt;GNU/Emacs właśnie się z Tobą przywitał. Nie czarujmy się. To okno wygląda beznadziejnie. Zaraz się tym zajmiemy. Na początku jednak przeczytaj informacje w tym oknie. Dowiesz się, że wciskając control + g zazwyczaj wydostaniesz się z kłopotów (zostanie wywołana funkcja keyboard-quit) - zostanie przerwana aktualna akcja. Gdyby to nie podziałało to możesz wcisnąć trzy razy ESC (funkcja keyboard-escape-quit).

W GNU/Emacs raczej nie używa się myszy (można, ale przerzucenie ręki na mysz jest czasochłonne - zazwyczaj szybciej można wykonać czynność przy pomocy klawiatury). Wykorzystuje się dwa klawisze modyfikujące. Control - większość ludzi wie gdzie znaleźć ten klawisz i jak go przycisnąć. Meta - tu zazwyczaj jest gorzej, bo mało kto o nim słyszał. W normalnych, dzisiejszych klawiaturach Meta jest oznaczony jako Alt. Skrót od Control to C, od Meta to M. Zatem jeżeli napiszę "wciśnij C-M-a" to znaczy, że musisz nacisnąć control, alt i trzymając je, nacisnąć literę a. Zastanów się co oznacza C-_

...oczywiście control + shift + - ;-) Czasem zdarza się inny zapis. np. C-ht oznacza, że musisz nacisnąć control i trzymając wcisnąć h. Następnie puścić oba klawisze i wcisnąć t.

GNU/Emacs jest dostarczany z tutorialem. Ten tutorial jest także w języku polskim. Koniecznie się z nim zapoznaj. Wybierz z Menu, Help/Emacs Tutorial (choose language)/Polish. Tutorial jest także pod skrótem klawiszowym C-ht.

Ten krótki tutorial jest interaktywny. Gdy go włączysz zobaczysz tekst, który opisuje co masz robić. Będziesz edytował tekst tutoriala. Dowiesz się między innymi, że można używać C-n C-p C-b C-f (od next, previous, back, forward) do poruszania się po tekście (odpowiednio next-line, previous-line, backward-char forward-char). Szczerze zachęcam do korzystania z tych klawiszy zamiast strzałek. Są dwa powody. Po pierwsze jest to szybsze - jak w przypadku myszy, nie trzeba przekładać ręki. Po drugie pod strzałki można podpiąć jakieś inne ciekawe funkcje.

Chciałbym Ci teraz opowiedzieć o buforach. W GNU/Emacs są bufory. Taki bufor może być skojarzony z plikiem (ale nie musi). Bufor to generalnie część, w której piszesz/czytasz tekst. Zawsze gdy otwierasz plik to jest tworzony nowy bufor i do niego jest kopiowana zawartość pliku.
Bufory są bardzo trafionym pomysłem. Możesz jednocześnie konfigurować edytor, modyfikować plik, czytać inny,... wszystko w innych buforach. Możesz się między buforami przełączać itp. Najważniejsze jest to, że nie blokują one okna. W edytorach takich jak VC czy notepad, jeżeli otworzysz menu z opcjami, to nie możesz edytować już tekstu (do czasu zamknięcia tego okna).  Bufory to o wiele więcej, ale nie będę się o tym rozpisywał. Ważne, żebyś teraz wiedział tylko co to jest.  Możesz zmienić aktualny bufor na wiele sposobów. Najczęściej stosowane to otwarcie nowego pliku oraz jawne przełączenie się między buforami (C-xb lub C-xC-b aby dostać listę buforów w nowym buforze). Podstawowy bufor ma nazwę *scratch*. Możesz w nim wpisywać tekst, który jest tylko szkicem. Ten bufor nie jest zapisywany do pliku. Standardowo jest w trybie emacs-lisp-mode co oznacza, że łatwo korzystać z niego do pisania w języku elisp. Z tym tematem jest związany temat okien. Możesz dowolnie dzielić okno GNU/Emacs na mniejsze (C-x2, C-x3), zamykać okno (C-x0 control x a potem zero (nie litera o) ), wybierać jedno okno (C-x1) oraz przełączać się między oknami (C-xo - tu już litera o). W każdym oknie możesz otworzyć inny bufor. Możesz też w dwóch oknach otworzyć ten sam bufor. W tym przypadku zawartość bufora NIE jest kopiowana. Możesz oczywiście w różnych oknach ustawić różne miejsca w pliku.

Jak już napisałem GNU/Emacs jest dobrze udokumentowany. Pomoc jest tak pełna, że można uzyskać nawet pomoc do pomocy. Istotnie C-hC-h (trzymasz control i dwa razy wciskasz h) pokazuje bufor z pomocą dla pomocy (jest wywołana komenda help-for-help). Najważniejsze rodzaje pomocy to:
&lt;ul&gt;&lt;li&gt;C-ha Pozwala wprowadzić zapytanie. Używaj gdy nie znasz funkcji, która robi to co chcesz zrobić. Jeżeli np. szukasz funkcji, która podzieli okno na dwie części to możesz postąpić następująco: C-ha i wpisz (na dole, w minibuforze, pojawi się zapytanie) "window split" (bez  cudzysłowów). Dowiesz się, że możesz podzielić okno w pionie (C-x3) i w poziomie (C-x2). // C-x1 powoduje pozostawienie tylko jednego, aktywnego okna.
&lt;/li&gt;&lt;li&gt;C-hi Otwiera strony podręcznika info.
&lt;/li&gt;&lt;li&gt;C-hk Bardzo często stosowany skrót. Udostępnia informacji o funkcji podpiętej pod dany klawisz. Chcąc dowiedzieć się co oznacza C-/naciskasz C-hk i wprowadzasz interesujący Cię skrót (C-/). Dostaniesz dokładny opis komendy oraz link do miejsca, gdzie jest zdefiniowana. Tak! Dokadnie! Możesz popatrzeć jak jest zaimplementowana.&lt;/li&gt;&lt;li&gt;C-hf Działa tak jak C-hk z tą różnicą, że wpisujesz nazwę interesującej Cię funkcji.&lt;/li&gt;&lt;li&gt;C-hv Jak C-hf tylko, że podaje informacje o zmiennej&lt;/li&gt;&lt;li&gt;C-hm Bardzo ważna komenda. Udostępnia informacje o aktualnym trybie (major-mode).&lt;/li&gt;&lt;li&gt;C-hw Sam sprawdź co robi. Dodam, że to bardzo przydatny skrót.
&lt;/li&gt;&lt;/ul&gt;Jak widać trochę tego jest. Oczywiście nie musisz zapamiętywać tych skrótów. Jeżeli będziesz z nich korzystał to same wejdą w pamięć. Wystarczy zapamiętać, że C-hC-h udostępni Ci listę wszystkich możliwych pomocy.

Jeżeli jeszcze tego nie zrobiłeś to przeczytaj teraz tutorial. Ja chętnie poczekam. Jak już skończysz to czytaj dalej.

To było na serio. Nie będę opisywał rzeczy, które są w tutorialu. Daj sobie szansę. Jeżeli teraz nie przerobisz tego króciutkiego tutoriala, to nigdy do niego nie wrócisz. Zobaczysz jak pracuje się z GNU/Emacs. Będzie Ci łatwiej. Wielu ludzi zraziło się do GNU/Emacs właśnie przez to, że próbowali na własną rękę eksplorować możliwości edytora. Jest tego zbyt dużo i zbyt bardzo różni się od typowej aplikacji takiej, jak MS Word czy OO Writer.

Skoro potrafisz już poruszać się po tekście, to pokażę Ci w jaki sposób konfiguruje się GNU/Emacs. Będzie to też krótki przewodnik po elisp.

GNU/Emacs przy starcie poszukuje pliku .emacs (czyt. "dot imaks"). Jeżeli go znajdzie, to jego zawartość zostanie zinterpretowana jako kod elisp'u. [Do użytkowników systemów, które (z różnych dziwacznych powodów) nie uznają nazw zaczynających się na kropkę (np. Windows): plik można nazwać _emacs - jeżeli jesteś jednym z tych nieszczęśników to koniecznie zapoznaj się z &lt;a href="http://www.gnu.org/software/emacs/windows/ntemacs.html"&gt;Emacs dla użytkowników Windowsa&lt;/a&gt;].

Plik .emacs należy utworzyć w swoim katalogu domowym. Można to oczywiście zrobić bezpośrednio z edytora. Zaczynamy C-xC-f i podajemy ścieżkę ~/.emacs

Teraz będziemy pisali kod w elisp, który zostanie zinterpretowany podczas uruchamiania edytora. Polecam zacząć od:&lt;pre&gt;;; -*- mode: emacs-lisp -*- &lt;/pre&gt;Linia zaczynająca się od ; (pojedynczy średnik, oczywiście może być też podwójny) to komentarz w elisp. Ten komentarz jest specjalny. Gdy w GNU/Emacs zostanie otwarty plik zaczynający się linią (nie ma znaczenia czy w komentarzu czy nie) zawierającą -*- opcje -*- to opcje zostaną zinterpretowane. W tym wypadku mówimy, że  trybem głównym (tzw. major-mode) ma być emacs-lisp. Ten tryb włącza kolorowanie składni dla języka elisp oraz pewne udogodnienia (np. binduje funkcje pod różne klawisze - sprawdź sam jakie. W emacs-lisp-mode naciśnij C-hm aby dowiedzieć się więcej o tym trybie. Możesz także spróbować C-hf i jako nazwę podać emacs-lisp-mode). Oczywiście w naszym przypadku ta linia zostanie zinterpretowana dopiero przy wczytaniu pliku. Możesz go zatem przeładować (C-xC-v) lub po prostu włączyć emacs-lisp-mode wydając polecenie emacs-lisp-mode. W tym celu Naciśnij M-x aby przejść w tryb wydawania komend (w minibuforze - jak pamiętasz, to jest to na dole).  Wpisz komendę (wywołaj funkcję) emacs-lisp-mode i zatwierdź enterem.

Podczas pisania (w szczególności programowania) dobrze jest mieć dużo miejsca. Dlatego wyłączymy menu, toolbar oraz scrollbar.  Dodatkowo GNU/Emacs często pyta o potwierdzenie (np. przy próbie zamknięcia edytora). Wpisywanie yes czy no jest irytujące, dlatego zrobimy tak, aby wystarczyło wpisać y albo n.
&lt;pre&gt;(scroll-bar-mode -1)            ; usun scroll-bar
(menu-bar-mode -1)            ; usuń pasek menu
(tool-bar-mode -1)            ; usun toolbar
(fset 'yes-or-no-p 'y-or-n-p)        ; pytaj y/n zamiast yes/no
(setq confirm-kill-emacs 'yes-or-no-p)&lt;/pre&gt; scroll-bar-mode, menu-bar-mode, tool-bar-mode to funkcje. W języku elisp operuje się listami. Lista to cokolwiek otoczonego parą nawiasów. W kontekście, w którym jest prośba o wartość listy (tak jak tu, bo listy są w zewnętrznym zasięgu), to pierwszy element jest traktowany jako funkcja, a kolejne jako jej argumenty. Tak więc wywołujemy funkcję scroll-bar-mode z argumentem -1. Podobnie z menu-bar-mode i tool-bar-mode. Proponuję poczytać o tych funkcjach.

Powinieneś się domyślić, że fset także jest funkcją. Jeżeli jesteś w emacs-lisp-mode (czyli jeżeli poprawnie wykonywałeś to o co prosiłem) to możesz najechać kursorem na fset (pamiętaj aby używać C-n C-f C-b C-p zamiast strzałek) i naciśnij C-hf. W tym momencie pojawi się zapytanie do jakiej funkcji chcesz pomoc. Domyślnie będzie to fset (bo tam jest kursor). Naciśnij enter.
Jak widać nasze wywołanie fset ustawi symbol 'yes-or-no-p na 'y-or-n-p. yes-or-no-p oraz y-or-n-p to funkcje. Ustawiasz y-or-n-p jako funkcję yes-or-no-p (przypisanie). Funkcja yes-or-no-p jest wykorzystywana przez GNU/Emacs do zadawania pytań. Przeczytaj ich znaczenie w pomocy. Nic nie stoi na przeszkodzie aby stworzyć własną obsługę pytań - wystarczy napisać funkcję i ustawić ją przy pomocy fset.

Muszę jeszcze wytłumaczyć dlaczego przed y-or-n-p jest apostrof ('). Jest tak, bo gdy lista jest obliczana to jej pierwszy argument jest traktowany jak funkcja. Aplikowane są do niej kolejne argumenty, ale już po wyliczeniu. Dla funkcji oznacza to wywołanie. Zatem (setf yes-or-no-p y-or-n-p) wywołałoby funkcje yes-or-no-p oraz y-or-no-p i wyniki ich działania przekazało jako argumenty do setf. Apostrof powoduje, że argumenty NIE są obliczane. Tak, można przed całą listą postawić '. Wtedy taka lista nie zostanie obliczona - będzie traktowana jako lista, a nie wywołanie funkcji. Dla przykładu '("Michał" "Daniel" "Wojtek") to lista trzech imion męskich. () jest listą pustą i jest równoznaczne z nil.

To teraz jeszcze wpisz w .emacs&lt;pre&gt;(global-font-lock-mode 1)        ; kolorowanie składni
(mouse-wheel-mode 1)         ; możliwość korzystania z rolki myszy
(setq transient-mark-mode 1)        ; podświetlanie zaznaczenia
(setq hscroll-global-mode 1)          ; brak zawijania dlugich wierszy
(setq scrool-step 1)            ; przewijanie po jednej lini
(setq delete-old-versions 1)        ; usuń starą wersję pliku
(setq inhibit-startup-message t)      ; nie pokazuj screena tytułowego
(setq tab-width 3)&lt;/pre&gt;W sumie nie powinienem opisywać tego kawałka. Nie powinieneś mieć problemu ze zrozumieniem go (z pomocą pomocy GNU/Emacs oczywiście). Jeżeli uważnie przeczytałeś wpis to powinno Cię zastanowić, dlaczego po setq i przed pierwszym argumentem (np. transient-mark-mode) nie ma apostrofu. Przecież przy wywołaniu funkcji setq pierwszy argument zostanie obliczony (czyli funkcja zostanie wywołana, a za zmienną zostanie podstawiona wartość). Dla przykładu jeżeli tab-width to 8, to (setq tab-width 3) powinno być wykonane następująco:
&lt;ol&gt;&lt;li&gt;tab-width jest obliczane do 8 i ta wartość jest wstawiana&lt;/li&gt;&lt;li&gt;3 jest obliczane do 3. Dokładnie tak. Liczby obliczają się do siebie. Tak samo stałe napisowe.&lt;/li&gt;&lt;li&gt;Zostaje wywołana funkcja setq z argumentami 8 i 3&lt;/li&gt;&lt;/ol&gt;Oczywiście to by nie zadziałało, bo jak wiesz z dokumentacji (przeczytałeś ją, prawda?)  setq ustawia wartość zmiennej (z pierwszego argumentu) na wartość z drugiego argumentu.

Przed tab-width nie trzeba apostrofu, bo setq jest specjalną funkcją. Konieczność przypisywania wartości do zmiennych była tak częsta, że w tym jednym przypadku jest odstępstwo od semantyki języka. Nazwa setq pochodzi od "set quoted". Zapamiętaj, że to jest wyjątek i to nie jest normalne zachowanie. To ważne.

Są jeszcze zmienne line-number-mode i column-number-mode. Ustaw je na t jeżeli chcesz mieć wyświetlaną pozycję w pliku (linia i kolumna).&lt;pre&gt;(set-face-background 'region "DimGrey")

(set-background-color "black")
(set-foreground-color "white")
(set-cursor-color "white")

(make-face 'fl-comment-face)  ; komentarze
(set-face-foreground 'fl-comment-face "green2")
(setq font-lock-comment-face 'fl-comment-face)&lt;/pre&gt;Myślę, że ten kod nie wymaga komentarza. Jest tu podstawowa konfiguracja kolorystyki. Przykłady można znaleźć na &lt;a href="http://www.cs.cmu.edu/%7Emaverick/GNUEmacsColorThemeTest/index-el.html"&gt;stronie z tematami.&lt;/a&gt;

Można ustawić różne kolorystyki dla różnych trybów (np. inaczej wygląda w c-mode i inaczej w perl-mode). Teraz nie będę tego dokładnie opisywał. W skrócie: wystarczy stworzyć funkcję, która ustawia kolorystykę i wpiąć ją w hook dla danego trybu.

Coś dla programistów:
&lt;pre&gt;(setq comment-column 80)
(setq c-default-style "stroustrup") ; wcinanie kodu
(setq compile-command "make -k")     ; kompilacja przy pomocy make -k
(setq compilation-window-height 10)    ; wielkosc okna kompilacji = 10&lt;/pre&gt;Teraz wydaj polecenie load-file (M-x nazwa polecenia enter) i podaj ~/.emacs jako argument. Zostanie załadowany plik .emacs z Twoimi ustawieniami. Jeżeli napisałeś bezbłędnie konfigurację to wszystko powinno być ok, jeżeli był błąd to dostaniesz sensowny komunikat o błędzie.

Zawsze konfiguruję edytor w ten sposób. Znajduję w dokumentacji co odpowiada za zachowanie, które chcę zmienić i robię wpis w .emacs. Alternatywnie można skorzystać z mechanizmu customize. Wydaj polecenie customize i sam sprawdź jak to działa. Jest to okienkowy sposób zmiany ustawień. Ma oczywiście mniejsze możliwości (jak wszystkie okienkowe aplikacje) niż język elisp, ale za to można szybciej osiągnąć cel - tak mi się wydaje, nigdy nie korzystałem z tego mechanizmu.

Nie napisałem jeszcze jak podpinać funkcje pod klawisze. Jest to banalne: wystarczy wywołać odpowiednią funkcję. Jaką? Poszukaj w dokumentacji.

Szukałeś? Można np. C-ha i szukać pod set key. Z pewnością znajdziesz funkcję global-set-key. Teraz C-hf global-set-key i dostajesz opis jak z niej korzystać (tak na prawdę to można po prostu nacisnąć enter po najechaniu na podkreślone słowo command). Przydatna może być funkcja kbd.

Przykład:
&lt;pre&gt;(global-set-key "\r" 'newline-and-indent)
(global-set-key "\M-h" 'woman-follow) ; M-h   Otwórz man'a na zaznaczeniu
(global-set-key "\C-cc" 'lmm-compile) ; M-c   Kopilacja
(global-set-key "\M-`" 'ff-find-other-file-other-window) ; Otwiera oba pliki modułu
(global-set-key (kbd "M-RET") 'ff-find-other-file) ; skacze do drugiego pliku modułu

(global-set-key [(f1)] 'woman)      ; F1  - otwórz man'a
(global-set-key [(f2)] 'speedbar-get-focus) ; F2  - otwórz/zamknij speedbar

(setq prev_buff ())
(global-set-key [(f4)] '(lambda ()
  (interactive)
  (if (and (string-equal (buffer-name) "*shell*") (not (null prev_buff)))
      (progn (switch-to-buffer prev_buff))
    (progn (setq prev_buff (buffer-name)) (shell)))))

(global-set-key "\M-n" 'next-error) ; F4  - następny błąd
(global-set-key "\M-p" 'previous-error) ; shift + F4  - poprzedni błąd

(global-set-key [(f5)] 'gdb)  ; F5  - gdb
(global-set-key [(f6)] 'compile) ; F6  - kompilacja


(global-set-key [(f7)] '(lambda()
  "Otwiera plik Makefile"
  (interactive)
  (if (file-exists-p "Makefile") (find-file "Makefile")
    (message "Brak pliku Makefile"))))

(global-set-key [(f9)] 'gud-tbreak)
(global-set-key [(shift f9)] 'gud-break)

(global-set-key [(f11)] 'webjump)&lt;/pre&gt;To z mojego pliku .emacs. Pominąłem część skrótów - głównie te, których nie chcę teraz omawiać: jak np. flymake.

Ten kod pokazuje jak bindować pod klawisze funkcyjne (F1-F12), jak używać shift, control, meta, wywoływać własne funkcje oraz tworzyć funkcje w miejscu (lambda).

Myślę, że sam potrafisz wypróbować co robią te komendy (zwłaszcza, że możesz sprawdzić to w dokumentacji).

Pod C-cc mam lmm-compile. Jest to funkcja, którą sam napisałem. Oto definicja:
&lt;pre&gt;(setq lmm-compile-command "g++ -g -W -Wall -pedantic ")
(defun lmm-compile ()
"Sprawdza czy istnieje plik Makefile. Jeżeli tak to ustawia compile-command
na wartość odpowiadającą wywołaniu programu make. W przeciwnym wypadku jest ustawiana
wartość g++ -W -Wall -pedantic NAZWA-PLIKU-BUFORA. Następnie jest wywoływane compile.
Przydatne do kompilacji prostych, jednoplikowych programów"
(interactive)
(if (file-exists-p "Makefile")
   (compile "make -k")
 (compile (concat lmm-compile-command (buffer-file-name)))))
&lt;/pre&gt;Ta funkcja najpierw sprawdza czy istnieje Makefile. Jeżeli tak to go uruchamia. Jeżeli nie to odpala komendę, która jest pod zmienną lmm-compile-command. To bardzo przydatny kawałek. Pozwala np. Stworzy plik main.cpp w katalogu tmp, wpisać kod i od razu go kompilować odpowiednim poleceniem. Często tworzę takie kilkulinijkowe przykłady w c++ jak programuję. Głównie żeby coś sprawdzić lub skompilować kod z forum. Jednocześnie gdy jestem w katalogu z moim projektem to kompilacja jest poprzez Makefile. Dla mnie kompilacja to kompilacja. Nie muszę nic wpisywać, ani tworzyć projektu, czy rozwiązania (solution znany z VC - nie wiem jak to tłumaczyć). Piszę kod zapisuję i kompiluję ;-)

Jak widać tworzenie funkcji w języku elisp jest bardzo proste. Najpierw jest nawias otwierający, słowo kluczowe defun, nazwa funkcji, lista parametrów (w tym przypadku pusta). Dalej jest komentarz w cudzysłowach. Następnie opcjonalne słowo (interactive). Jeżeli zabraknie tego słowa, to funkcja będzie mogła być wywołana z innej funkcji, ale nie z interfejsu GNU/Emacs. Więcej informacji pod C-hf. Dalej jest ciało funkcji.

Dalej jest instrukcja warunkowa. Jej pierwszym argumentem jest wartość logiczna oznaczająca warunek. W tym przypadku warunkiem jest istnienie pliku Makefile. Zaraz za warunkiem jest część "then", czyli to co zostanie wywołane w przypadku spełnienia warunku. Kolejnym argumentem (opcjonalnym) jest część "else".

Na końcu zawsze jest dużo nawiasów zamykających.

Pod f4 mam zbindowaną konsolę (shell). Jest to przykład użycia funkcji lambda (definiowanie funkcji w miejscu). Całość jest identyczna ze zwykłym definiowaniem funkcji. Różnica polega na tym, że zamiast słowa defun jest lambda. No i jeszcze fakt, że ta definicja jest w miejscu. W tym przypadku jeżeli nacisnę f4 to wyskakuje mi konsola... o ile w niej nie jestem. Jeżeli otwartym buforem jest *shell* to jest otwierany poprzednio widoczny bufor.

Myślę, że masz dość wiedzy aby rozpocząć przygodę z GNU/Emacs. Pamiętaj, że krzywa nauki jest w tym przypadku pionowa. Będę jeszcze opisywał konkretne moduły dla GNU/Emacs itp. Na początek polecam lekturę: &lt;a href="http://www.gnu.org/software/emacs/emacs-lisp-intro/"&gt;Programowanie w elisp&lt;/a&gt; oraz stronkę z opisem wielu trybów: &lt;a href="http://www.emacswiki.org/cgi-bin/wiki"&gt;Emacs Wiki&lt;/a&gt;.

Najważniejsze: przeczytaj, w końcu, tutorial. Masz ostatnią szansę. Warto. Edytor możesz wyłączyć korzystając z C-xc.

Przykład z youtube. Środowisko dla Pythona
&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/OMi-uN-6O1Q&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/OMi-uN-6O1Q&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-9133673682753588011?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/9133673682753588011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/co-kady-powinien-wiedzie-o-gnuemacs.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/9133673682753588011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/9133673682753588011'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/co-kady-powinien-wiedzie-o-gnuemacs.html' title='Co każdy powinien wiedzieć o GNU/Emacs?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://4.bp.blogspot.com/_P9Sn6SUv534/SKbV-mfJceI/AAAAAAAAAAU/3EKh60WoCis/s72-c/okno_emacs.jpg' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-6511646078599785718</id><published>2008-08-09T00:00:00.007-07:00</published><updated>2008-08-19T05:12:38.652-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Jak wykonać kod już w czasie kompilacji?</title><content type='html'>Wiele osób wie, że przy użyciu mechanizmu szkieletów (templates) można w C++ napisać dowolny algorytm. To samo zdanie, zapisane 'mądrzej' brzmi: "Mechanizm templates w C++ jest zupełny, w sensie maszyn Turinga". Mimo, że większość programistów zdaje sobie z tego sprawę, to zazwyczaj nie wiedzą jak to się robi.

Po przeczytaniu tego tekstu będziesz umiał np. narysować fraktal przy pomocy szkieletów (zamieszczam jako link kod źródłowy odpowiedniego programu).

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_P9Sn6SUv534/SJzNk5DzDgI/AAAAAAAAAAM/htOyvW3LI_c/s1600-h/mandelbrot.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_P9Sn6SUv534/SJzNk5DzDgI/AAAAAAAAAAM/htOyvW3LI_c/s320/mandelbrot.jpg" alt="" id="BLOGGER_PHOTO_ID_5232282900773408258" border="0"&gt;&lt;/a&gt;
Co można zyskać? Trochę szybkości, w czasie wykonania. Kod napisany przy pomocy szkieletów jest wykonywany w czasie kompilacji (co ją oczywiście wydłuża). Dzięki temu nie ma narzutu w czasie  wykonania.

Jakim kosztem? Łatwo nauczyć się metaprogramowania i równie łatwo z niego później korzystać, choć składnia nie należy do najpiękniejszych. Zazwyczaj traci się na czasie kompilacji projektu (bardzo). Dodatkowo plik wynikowy pęcznieje. Co istotne, nie istnieje (przynajmniej ja nie znam) sposób na debuggowanie/profilowanie metaprogramu w czasie rzeczywistym. Powoduje to, że metaprogramy powinny  być proste.

Najpierw definiujemy instrukcję if-then-else, gdyż jest bardzo łatwa i krótka.
&lt;pre&gt;
&lt;font class="cpp_keyword"&gt;template&lt;/font&gt; &amp;lt;bool CONDITION, class THEN_PART, class ELSE_PART&amp;gt;
struct IF {
};

template &amp;lt;class THEN_PART, class ELSE_PART&amp;gt;
class IF&amp;lt;true, THEN_PART, ELSE_PART&amp;gt; {
public:
 static void exec() {
   EXEC::exec();
 }
 
private:
 typedef THEN_PART EXEC;
};

template &amp;lt;class THEN_PART, class ELSE_PART&amp;gt;
class IF&amp;lt;false, THEN_PART, ELSE_PART&amp;gt; {
public:
 static void exec() {
   EXEC::exec();
 }
 
private:
 typedef ELSE_PART EXEC;
}; 
&lt;/pre&gt;Jak widać nic trudnego. Korzystamy z częściowej specjalizacji klas. Jeżeli CONDITION będzie true to zostanie wybrany pierwszy wariant if, jeżeli false to drugi. Te warianty różnią się tym, że pierwszy zapamiętuje THEN_PART jako typ EXEC, a drugi ELSE_PART. Metoda exec() jest w obu przypadkach taka sama. Ten kod jest ogólny, w tym sensie, że teraz wystarczy napisać THEN_PART i ELSE_PART. W projekcie można napisać if parametryzując go tylko warunkiem. Specjalizacje na true i na false będą, w sobie, zawierały odpowiedni kod. Każdy problem ma wiele rozwiązań.

Napiszemy teraz makro, które generuje strukturę wypisującą tekst:
&lt;pre&gt;#define TPL_CREATE_PRINT(NAME, TEXT)  \
  struct NAME {                            \
      static void exec() {                 \
          std::cout &amp;lt;&amp;lt; TEXT;         \
      }                                    \
  }
&lt;/pre&gt;Teraz możemy stworzyć dwie klasy, jedna wypisująca "FAILED",  druga "OK".
&lt;pre&gt;
TPL_CREATE_PRINT (OK, "\tOK\n");
TPL_CREATE_PRINT (FAILED, "\tFAILED\n");
&lt;/pre&gt;Poniższy kod należy wykonać (np. umieścić w main). Prezentuje zagnieżdżoną instrukcję IF.
&lt;pre&gt;
  IF &amp;lt;2 == 2,
   IF&amp;lt;(2 &amp;gt; 3),
     OK,
     FAILED
     &amp;gt;,
   FAILED
   &amp;gt;::exec();
&lt;/pre&gt;Zwróć uwagę na nawiasy (2&amp;gt;3). Gdyby ich zabrakło to znak &amp;gt; zostałby potraktowany jako koniec parametrów szkieletu. Zastanów się jak zrobić instruckję SWITCH. Jeżeli wymyślisz ogólny i ładny (bez setki argumentów) sposób to daj mi znać.

Skoro mamy już instrukcję warunkową to czas na pętlę. Niestety w języku template'ów nie ma stanu (zmiennych). Przez to trudno jest zrobić ogólny sposób definiowania pętli. Pierwsza próba może wyglądać tak:
&lt;pre&gt;
template &amp;lt;int START, int END, class BODY, bool DONE&amp;gt;
struct FOR_UP_AUX {
};

template &amp;lt;int START, int END, class BODY&amp;gt;
struct FOR_UP_AUX&amp;lt;START, END, BODY, true&amp;gt; {
 static void exec() {
 }
};

template &amp;lt;int START, int END, class BODY&amp;gt;
struct FOR_UP_AUX&amp;lt;START, END, BODY, false&amp;gt; {
 static void exec() {
   BODY::exec(START);
   FOR_UP_AUX&amp;lt;START+1, END, BODY, START &amp;gt;= END&amp;gt;::exec();
 }
};

template &amp;lt;int START, int END, class BODY&amp;gt;
struct FOR_UP {
 static void exec() {
   FOR_UP_AUX&amp;lt;START, END, BODY, false&amp;gt;::exec();
 }
};

&lt;/pre&gt;Najpierw tworzymy pomocniczy szablon FOR_UP_AUX. Ma on o jeden argument więcej. Ten argument określa czy należy zakończyć pętlę czy nie. FOR_UP rozwija kod do FOR_UP_AUX. FOR_UP_AUX, gdy ma false jako ostatni argument to wywołuje raz exec() (właściwie to wstawia exec, bo to wywołanie będzie w czasie działania programu) oraz rozwija FOR_UP_AUX, z większym parametrem START. Przy czym, jeżeli START&gt;=END, to ostatni argument FOR_UP_AUX jest true, a to oznacza, że pętla się zakończy. Tak więc przy pomocy rekurencji symulujemy pętlę liczącą w górę.

Wykorzystanie pętli jest proste. Tworzymy typ odpowiedzialny, za wypisywanie informacji.
&lt;pre&gt;struct PRINT_I_II {
  static void exec(int i) {
    std::cout &lt;&lt; i &lt;&lt; " " &lt;&lt; i*i &lt;&lt; "\n";     
  } 
}; &lt;/pre&gt;Oraz wywołujemy pętlę:
&lt;pre&gt;FOR_UP&lt;0, 10, PRINT_I_II&gt;::exec();
&lt;/pre&gt;To rozwiązanie jest dobre gdy trzeba wykonać kod znaną liczbę razy. Wówczas jest on rozwijany. Nie ma narzutu związanego ze zwiększaniem licznika czy testowaniem warunku.

Ponieważ nie ma stanu, to ciężko napisać ogólny szkielet pętli. Zazwyczaj pisze się całość w miejscu. Zróbmy dla przykładu program liczący liczby Fibonacciego.
&lt;pre&gt;template &amp;lt;int N&amp;gt;
struct Fibun {
  enum {
    RESULT = Fibun&amp;lt;N-1&amp;gt;::RESULT + Fibun&amp;lt;N-2&amp;gt;::RESULT
  };
};

template&amp;lt;&amp;gt;
struct Fibun&amp;lt;0&amp;gt; {
  enum {
    RESULT = 1
  };
};

template&amp;lt;&amp;gt;
&lt;font class="cpp_keyword"&gt;struct&lt;/font&gt; Fibun&amp;lt;1&amp;gt; {
  enum {
    RESULT = 1
  }; 
};
&lt;/pre&gt;Jak widać, wcześniej skłamałem. Można mieć stan. Wyrażenia enum w powyższym kodzie służą właśnie za zmienne tymczasowe. W tym wypadku Fibun&lt;45&gt;::RESULT policzy się bardzo szybko w czasie kompilacji, a w czasie uruchomienia odpowiednia wartość będzie tylko odczytywana. Spróbuj samemu napisać inną funkcję rekurencyjną. Np. &lt;a href="http://pl.wikipedia.org/wiki/Funkcja_Ackermanna"&gt;Funkcję Ackermanna&lt;/a&gt;

Tak wygląda programowanie przy pomocy szkieletów. Prawda, że proste? Teraz czas na ciekawy przykład. Wygenerujemy &lt;a href="http://pl.wikipedia.org/wiki/Zbi%C3%B3r_Mandelbrota"&gt;zbiór Mandelbrota&lt;/a&gt;.

Nie będę wklejał tu całego kodu, który napisałem na potrzeby tej notki. Można go znaleźć &lt;a href="http://sites.google.com/site/zbirmandelbrotaszkieletyc/"&gt;tu&lt;/a&gt;. Jeżeli przeczytałeś notkę uważnie to nie powinieneś mieć problemów ze zrozumieniem kodu. Jeżeli chodzi o, nie poruszone w notce, sprawy (które występują w kodzie rysującym zbiór Mandelbrota), to:
&lt;ol&gt;&lt;li&gt;Przy programowaniu szablonami jesteśmy ograniczeni typami. Nie ma double czy string, ani nawet const char*.  Można korzystać z int, char. Ponieważ potrzebowałem arytmetyki zmiennopozycyjnej to każdą liczbę pomnożyłem przez 1000 i obciąłem miejsca dziesiętne. W ten sposób mam dokładność do 3 miejsc po przecinku. Inne podejście może polegać np. na osobnym przechowywaniu części całkowitej i osobno części ułamkowej.
&lt;/li&gt;&lt;li&gt;Warto najpierw napisać wersję bez szkieletów. Wtedy łatwiej napisać program na szkieletach.
&lt;/li&gt;&lt;li&gt;Warto poznać chociaż podstawy programowania funkcyjnego. W języku szkieletów nie ma stanu (jest bardzo ograniczony). Taka sytuacja jest też w językach funkcyjnych. Dlatego warto nauczyć się języka takiego jak OCaml czy Haskell. Pewne wzorce, wyuczone tam, pomagają w pisaniu szkieletami.&lt;/li&gt;&lt;li&gt;Oprator warunkowy ?: jest wyliczany w czasie kompilacji, o ile się da. Nie można zamiast niego w kodzie zastosować instrukcji if.&lt;/li&gt;&lt;li&gt;Czas kompilacji jest długi. U mnie kompilacja zajęła ponad 6m&lt;/li&gt;&lt;li&gt;Należy kompilować z przełącznikiem -ftemplate-depth-100000  (taki jest w g++, w innych kompilatorach należy poszukać odpowiedniego przełącznika). Standardowo g++ ogranicza zagnieżdżenie specjalizacji szablonu do 500. W kodzie rysującym zbiór Mandelbrota, pętle tworzę przy pomocy rekurencji. Zatem zagnieżdżenie będzie takie jak maksymalna liczba wykonanych iteracji.
&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-6511646078599785718?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/6511646078599785718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/jak-wykona-kod-ju-w-czasie-kompilacji.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6511646078599785718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/6511646078599785718'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/jak-wykona-kod-ju-w-czasie-kompilacji.html' title='Jak wykonać kod już w czasie kompilacji?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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://1.bp.blogspot.com/_P9Sn6SUv534/SJzNk5DzDgI/AAAAAAAAAAM/htOyvW3LI_c/s72-c/mandelbrot.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3683853109261477206.post-363898324595199034</id><published>2008-08-06T00:00:00.017-07:00</published><updated>2008-08-06T11:34:11.036-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linuks'/><category scheme='http://www.blogger.com/atom/ns#' term='Narzędzia'/><title type='text'>Czy make potrafi wysprzątać mieszkanie? Po prostu wchodzę w katalog i wpisuję make</title><content type='html'>Make jest niesamowitym narzędziem, choć wielu kojarzy je jedynie jako sposób na kompilację programów. W rzeczywistości ten program pozwala uprościć lub wyeliminować wiele codziennych czynności.

Najprostsze zastosowanie polega na zastąpieniu długiej komendy (lub listy komend)  krótszą. Często tworzę mirrory stron www. Mam na przykład zrzuty stron wykładowców. W tym celu utworzyłem odpowiedni katalog, a w nim plik Makefile:&lt;pre&gt;PROG=/usr/bin/wget
OPCJE= --background --verbose --input-file mirror.list --tries inf --quota 1000m
--no-host-directories --no-parent --mirror --convert-links --page-requisites 

main:
        $(PROG) $(OPCJE)

.PHONY: clean

clean:
        rm -f wget-log*&lt;/pre&gt;Stworzyłem jeszcze jeden plik mirror.list, który zawiera adresy stron do pobrania. Jeden adres na linię. Teraz wydaję polecenie&lt;pre&gt;$ make&lt;/pre&gt; System sam pobiera wszystkie stronki oraz tworzy plik z logiem. Czasem wpisuję &lt;pre&gt;$ make clean&lt;/pre&gt;aby pozbyć się starych logów.

Dlaczego nie stworzyłem skryptu? Pisząc Makefile zawsze w taki sam sposób dostaję jednolity interfejs. Nie muszę pamiętać nazwy skryptu czy jego parametrów. Gdy wchodzę do katalogu, w którym jest Makefile wiem, że jak wydam polecenie make, to zostanie wykonana domyślna akcja. Wiem także, że po make clean w katalogu nie będzie zbędnych plików. Każdy się domyśli co robi make help czy make backup. Kto kompilował programy pod Linuksem (a potem męczył się pod innym systemem), ten doceni, że wystarczy napisać&lt;pre&gt;$ ./configure
$ make
# make install&lt;/pre&gt;Dlaczego nie zrzucić tego na cron (planowanie zadań)? W tym wypadku warto samemu wywołać make, aby dostać raport o zaaktualizowanych stronach. Po co marnować czas na przeglądanie wszystkich?

Jak to działa? W katalogu należy utworzyc plik Makefile. W nim wpisujemy regułki. Następnie wywołujemy make. W tym przykładzie do PROG i OPCJE przypisujemy wartości (są to zmienne). Dalej piszemy reguły. Pierwsza reguła zostanie wywołana, gdy make nie dostanie żadnego argumentu (aby wykonać określoną regułę należy napisać make nazwa_reguły). Reguła składa się z nazwy, dwukropka, listy zależności, znaku nowej linii. Dalej tabulator (nie spacje!) oraz komenda do wykonania. Można podać wiele komend, każdą w osobnej linii. Można oczywiście komentować Makefile. Komentarz zaczyna się wraz ze znakiem # oraz kończy się z końcem linii. 

Zapamiętaj następujący komunikat:
*** brakujący separator. Stop.
Oznacza, że użyłeś spacji zamiast tabulatora. Pewnie popełnisz ten błąd kilka razy na początku.

W powyższym kodzie clean to także reguła. Ma jednak specjalną własność. Występuje na liście .PHONY. Popatrzmy na regułę main. Gdyby w katalogu istniał plik, o nazwie main, oraz miał nie wcześniejszy znacznik czasu niż wszystkie jego zależności (u nas występuje ten przypadek, bo brak zależności, a plik main nie istnieje) to wywołanie 
$ make main 
zwróci komunikat o tym, że plik main jest aktualny i nic nie zrobi. Za chwilę opiszę to dokładniej. Gdyby natomiast istniał plik clean, to dzięki temu, że clean jest na liście .PHONY, make wie, że ma wykonać regułę clean bez względu na znaczniki czasu.
&lt;p&gt;Załóżmy, że budujemy sprawozdanie w TeXu. Mamy katalog z obrazkami (img), oraz plik sprawozdania. Dodatkowo, każdy rozdział trzymamy w osobnym pliku. Wszystkie rozdziały są włączane (include) przez główny plik sprawozdanie.tex. Makefile może wyglądać następująco:&lt;/p&gt;&lt;pre&gt;all: sprawozdzanie.tex zrob_dokumentacje

sprawozdanie.tex: rozdzial1.tex rozdzial2.tex rozdzial3.tex
  # tu komendy składające sprawozdanie.tex z rozdziałów

rozdzial1.tex: img/obrazek1.jpg img/obrazek2.png
  # komendy składające rozdział 1

roadzial2.tex: zlozony_wykres.jpg
  # komendy składające rozdział 2

zlozony_wykres.jpg: wykresy/wyk1.jpg wykresy/wyk2.jpg
  # komendy składające wykres z mniejszych (np. przez ImageMagick)

.PHONY: clean zrob_dokumentacje all

clean:
  rm -f sprawozdanie.pdf # -f bo plik może nie istnieć

zrob_dokumentacje:
  # komendy odpowiedzialne za dokumentacje&lt;/pre&gt;&lt;p&gt;Teraz modyfikujemy dowolny obrazek, wykres, cokolwiek. Wpisujemy make i system sam składa nową wersję sprawozdania. Robi to inteligentnie. Uaktualnia tylko te pliki, które tego potrzebują. Nie będziemy czekali przez skalowanie wielkich obrazków tylko dlatego, że dopisaliśmy pięć zdań.&lt;/p&gt;&lt;p&gt;Kolejność komend nie ma dla make żadnego znaczenia. Jak to działa? Make opiera się na aktualności celów i zależności. Cele to pliki, jakie mają powstać po wykonaniu reguły. Powiemy, że dany cel jest aktualny, jeżeli istnieje i ma datę modyfikacji późniejszą niż wszystkie jego zależności. Cele z .PHONY, make zawsze traktuje, jakby były nieaktualne.&lt;/p&gt;&lt;p&gt;Gdyby, w powyższym przykładzie, wykresy musiały zostać wygenerowane przy każdym składaniu sprawozdania (np. tworzymy opis obciążenia procesora przez ostatnie 30 minut) to należałoby umieścić wykresy/wyk1.jpg i 2 w .PHONY. &lt;/p&gt;&lt;p&gt;Ta podstawowa wiedza wystarcza do tworzenia potężnych plików Makefile. Jest jeszcze jedna technika, którą chciałbym opisać. Często trzeba pracować z systemem zdalnym (np. mamy sieć i chcemy, aby na każdym komputerze była zawsze ta sama, aktualna wersja jekiegoś pliku). Wystarczy na serwerze stworzyć plik Makefile z regułami - po jednej dla każdego komputera. &lt;/p&gt;&lt;pre&gt;komputerXX: wazny_plik komputerXX
  # komendy wysyłające plik
  touch komputerXX # tworzy lokalnie znacznik czasu pliku zdalnego&lt;/pre&gt;i jedną zbiorczą regułkę domyślną, która zależy od powyższych. Dzięki temu mamy lokalnie kopię znacznika czasu pliku zdalnego. Make będzie aktualizował pliki tylko, jeżeli będzie taka potrzeba. Niby oczywiste, ale warto sobie to uzmysłowić.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Przy pracy z katalogami jest pewna pułapka. Załóżmy, że chcemy spowodować, aby make backup tworzyło kopię zapasową drzewa w katalogu backup. Warto zrobić to w następujący sposób:&lt;/p&gt;&lt;pre&gt;backup/: backup
backup:
 tar --exclude backup -cjvf "backup/`date +%Y%m%d%k%M`.tar.bz2" ../$(DIRNAME)&lt;/pre&gt;&lt;p&gt;Nie patrzmy na konkretne polecenie tar. Ważna jest regułka backup/: backup. Gdy będziesz wpisywał make backup to prawdopodobnie użyjesz tab do dopełnienia. Powłoka może dopełnić to do make backup/. Dobrze gdyby reguła backup/ istniała.&lt;/p&gt;&lt;p&gt;Warto również znać wildcard'y. Aby uzyskać listę obrazków jpg w katalogu wystarczy napisać&lt;/p&gt;&lt;pre&gt;OBRAZKI = $(wildcard *.jpg)&lt;/pre&gt;&lt;p&gt;Pliki Makefile bardzo często są do siebie podobne. Warto tworzyć wzorce. Np. ja używam Makefile również do kompilacji programów i bibliotek. Napisałem kiedyś jeden Makefile, do bibliotek, i zawsze wykorzystuję ten sam. Jedynie zmieniam nazwę biblioteki. W tym celu dałem taką deklarację na początku:&lt;/p&gt;&lt;pre&gt;Należy przypisać wartość początkową zmiennej NAME [plik nazwa biblioteki]
(i usunąć tę linię).
# NAZWA biblioteki. Zostanie do niej dodany prefiks lib oraz sufiks .a
#       Plik wynikowy będzie umieszczony w katalogu TARGETDIR
NAME=&lt;/pre&gt;&lt;p&gt;Jak widać jest tu błąd składniowy (nie wolno umieszczać tekstu tak po prostu - pierwsza linia). Dzięki temu, gdy zapomnę ustawić zmienną NAME, make mi o tym przypomni.&lt;/p&gt;&lt;p&gt;Jak wspomniałem na początku make kojarzy się głównie z kompilacją programów. Jest do tego celu dobrze przystosowany. Posiada np. domyślne reguły. Celowo nie opisałem tego zastosowania, bo łatwo znaleźć materiały traktujące o kompilacji przy pomocy make. Bardzo dobry opis można znaleźć &lt;a href="http://wazniak.mimuw.edu.pl/index.php?title=%C5%9Arodowisko_programisty/Automatyzacja_kompilacji_-_make"&gt;tu &lt;/a&gt;oraz w &lt;a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?make"&gt;manualu.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Istotne opcje to: -C (wraz z -w), -B oraz  -f . Prędzej czy później, będziesz musiał z nich skorzystać. Warto przeczytać o nich teraz.&lt;/p&gt;&lt;p&gt;Polecam eksperymenty z tym wspaniałym narzędziem. Zapraszam do dyskusji nad zastosowaniami make.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-363898324595199034?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/363898324595199034/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/czy-make-potrafi-wysprzta-mieszkanie-po.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/363898324595199034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/363898324595199034'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/czy-make-potrafi-wysprzta-mieszkanie-po.html' title='Czy make potrafi wysprzątać mieszkanie? Po prostu wchodzę w katalog i wpisuję make'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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-3683853109261477206.post-9116912734971731288</id><published>2008-08-04T07:12:00.006-07:00</published><updated>2008-08-07T14:18:25.237-07:00</updated><title type='text'>Dlaczego warto czytać ten devblog?</title><content type='html'>Witaj, jest to pierwszy wpis i pełni rolę wstępu. Najpierw się przedstawię. Mam na imię Łukasz. To tyle.

Jest to devblog i nie będę tu umieszczał informacji o sobie czy moich zainteresowaniach. Zamierzam pisać jedynie na tematy związane z informatyką. Planuję teksty o języku C++, projektowaniu obiektowym, planowania pracy i czasu, Linuksie, edytorze GNU/Emacs...

Postaram się opisywać to, co dobrze znam. Myślę, że w ten sposób teksty będą miały wyższą wartość. Planuję ok. dwóch  notatek w tygodniu.

Zachęcam wszystkich do komentowania i zadawania pytań. Wolałbym aby te ostatnie pojawiały się w komentarzach. W ten sposób więcej osób dotrze później do odpowiedzi czy dyskusji.

Będę umieszczał we wpisach linki do interesujących artykułów z sieci. Na początek proponuję dowiedzieć się &lt;a href="http://www.fsf.org/"&gt;czym jest wolne oprogramowanie&lt;/a&gt;. Gorąco polecam także, dostępną za darmo, książkę &lt;a href="http://stallman.helion.pl/"&gt;"W obronie wolności"&lt;/a&gt; (polskie tłumaczenie).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3683853109261477206-9116912734971731288?l=lmmilewski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lmmilewski.blogspot.com/feeds/9116912734971731288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/dlaczego-warto-czyta-ten-devblog.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/9116912734971731288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3683853109261477206/posts/default/9116912734971731288'/><link rel='alternate' type='text/html' href='http://lmmilewski.blogspot.com/2008/08/dlaczego-warto-czyta-ten-devblog.html' title='Dlaczego warto czytać ten devblog?'/><author><name>lmmilewski</name><uri>http://www.blogger.com/profile/03585693709668140159</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></feed>
