<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>tag:plblog.danieljanus.pl,2019:feed</id>
  <title>kod · słowa · emocje: blog Daniela Janusa</title>
  <link href="http://plblog.danieljanus.pl"/>
  <updated>2023-10-17T00:00:00Z</updated>
  <author>
    <name>Daniel Janus</name>
    <uri>http://danieljanus.pl</uri>
    <email>dj@danieljanus.pl</email>
  </author>
  <entry>
    <id>tag:plblog.danieljanus.pl,2023-10-17:post:programowanie-wyborcze</id>
    <title>Programowanie wyborcze</title>
    <link href="http://plblog.danieljanus.pl/2023/10/17/programowanie-wyborcze/"/>
    <updated>2023-10-17T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;A więc już wszystko prawie wiadomo. Kiedy to piszę, jest jeszcze poniedziałek; policzone są głosy z ponad 99% komisji i ostateczny podział mandatów, jeśli jeszcze się zmieni, to minimalnie. W tej chwili Sejm X kadencji zarysowuje się tak:&lt;/p&gt;&lt;figure&gt;
&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Komitet&lt;/th&gt;&lt;th&gt;Mandaty&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Prawo i Sprawiedliwość&lt;/td&gt;&lt;td class="right"&gt;194&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Koalicja Obywatelska&lt;/td&gt;&lt;td class="right"&gt;157&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Trzecia Droga&lt;/td&gt;&lt;td class="right"&gt;65&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lewica&lt;/td&gt;&lt;td class="right"&gt;26&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Konfederacja&lt;/td&gt;&lt;td class="right"&gt;18&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;figcaption class="center"&gt;Prawdopodobny podział mandatów w Sejmie&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Oficjalne wyniki poznamy pewnie dopiero we wtorek, 17 października. Ale już dziś od wczesnego popołudnia można było się emocjonować przybliżeniami podziału sejmowych mandatów, generowanych na podstawie danych cząstkowych spływających z PKW. Te dane co kilka minut aktualizowały się na &lt;a href="https://danieljanus.pl/wybory2023/"&gt;specjalnej stronie&lt;/a&gt;, którą zrobiłem w kilka godzin.&lt;/p&gt;&lt;p&gt;Kłębią mi się w głowie rozmaite myśli na temat tego dnia. Ta notka jest próbą retrospekcji i odpowiedzi na kilka pytań. Jedno z nich brzmi: czy warto było poświęcać czas na napisanie programu, którego czas życia to niecała doba?&lt;/p&gt;&lt;p&gt;Otóż tak.&lt;/p&gt;&lt;p&gt;Kiepsko spałem poprzedniej nocy. Długo nie mogłem zasnąć, czekając na late poll i oglądając na stronie PKW wyniki z pierwszych komisji; po przebudzeniu rzuciłem okiem na wyniki cząstkowe (bardzo wtedy pesymistyczne dla opozycji) i pomyślałem: ciekawe, jak to wygląda w przeliczeniu na mandaty? Strona PKW nie przeliczała tego na żywo, więc stwierdziłem, że skorzystam z okazji i zaimplementuję algorytm d'Hondta. Ile w końcu czasu – myślałem – może to zająć, plus prosty scraping strony PKW? Około 9 rano odpaliłem Emacsa, a o 16 strona była gotowa i udostępniłem ją w social mediach.&lt;/p&gt;&lt;p&gt;Gdybym wtedy wiedział o znakomitym serwisie &lt;a href="https://mkostyk.github.io/wybory2023-client/"&gt;Michała Kostyka&lt;/a&gt;, który robi to samo i jeszcze więcej, to pewnie po prostu wgapiałbym się przez resztę dnia w tamtą stronę. Tym niemniej cieszę się, że nie wiedziałem.&lt;/p&gt;&lt;p&gt;Jak to stwierdził Radek Czajka na byłym Twitterze:&lt;/p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="pl" dir="ltr"&gt;Każdy dorosły obywatel powinien przynajmniej raz napisać własną przeliczarkę głosów na mandaty. &lt;a href="https://t.co/HMVxFr2Tvu"&gt;https://t.co/HMVxFr2Tvu&lt;/a&gt;&lt;/p&gt;&amp;mdash; Radek Czajka @rcz@101010.pl (@RadekCzajka) &lt;a href="https://twitter.com/RadekCzajka/status/1713960281401888792?ref_src=twsrc%5Etfw"&gt;October 16, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;Wokół &lt;a href="https://pl.wikipedia.org/wiki/Metoda_D%E2%80%99Hondta"&gt;metody d’Hondta&lt;/a&gt; przeliczania głosów na mandaty &lt;a href="https://oko.press/metoda-dhondta-rozwiewamy-mity"&gt;narosło wiele mitów&lt;/a&gt;. Wydaje mi się, że jako społeczeństwo mało rozumiemy, jak to w praktyce działa. Po zakończeniu liczenia głosów PKW ogłasza wyniki, wykonuje „magiczne szuru-buru” i ogłasza, kto został posłem. Sam nie byłem pewien, czy dobrze to rozumiem, i miałem ochotę sprawdzić swoje rozumienie w praktyce. Jeśli wyjdą mi takie same wyniki, jak koniec końców ogłosi PKW, to znaczy po pierwsze, że dobrze rozumiem – a po drugie, że proces demokratyczny zadziałał transparentnie i można mieć do niego zaufanie. Gdyby zaś coś się nie zgadzało, to warto przyjrzeć się bliżej.&lt;/p&gt;&lt;p&gt;Na razie wychodzą mi takie same wyniki, jakie widać w serwisie Michała, mimo że nie zaglądałem mu w kod. Wygląda więc na to, że zdałem swój własny osobisty egzamin z WOS-u.&lt;/p&gt;&lt;p&gt;To jedna rzecz. Druga sprawa jest taka, że moja strona od chwili debiutu rozniosła się viralem po internecie i cieszyła się niemałym zainteresowaniem (ponad 300 tys. odsłon i ok. 50 tys. unikalnych użytkowników w ciągu kilku godzin). Różnymi kanałami – w komentarzach w social mediach, w prywatnych wiadomościach – dostałem feedback, którego częścią się tu z Wami podzielę:&lt;/p&gt;&lt;img src="/img/blog/feedback-wybory.png" alt="Dużo podziękowań na ex-Twitterze"&gt;
&lt;p&gt;I wiecie co? Strasznie fajnie jest dostać tyle miłych słów. Ale najbardziej wzrusza mnie to, że one nadeszły ze wszystkich preferencji politycznych, od lewa do prawa. To mi pokazuje, że – cokolwiek byśmy myśleli o tym, w którą stronę chcemy zmieniać kraj i kogo pogonić na cztery wiatry – nie jest nam wszystko jedno. Są dla nas ważne nasze wybory, chcemy wiedzieć, co z nich wyszło.&lt;/p&gt;&lt;p&gt;Mówi się, że wybory są świętem demokracji. Wzruszałem się wczoraj wysoką frekwencją, widząc, że traktujemy serio to święto. A dzięki tej stronie zupełnie nieoczekiwanie okazało się, że ono dla mnie trwa dwa dni.&lt;/p&gt;&lt;p&gt;I wreszcie trzecia rzecz: samo programowanie. Zdarzył mi się jeden z tych rzadkich dni, kiedy praca nad kawałkiem kodu pochłania mnie tak bardzo, że prawie zapominam o bożym świecie. Zacząłem, jako się rzekło, od implementacji d’Hondta w Clojure (pomyślałem sobie: „no tak, fajnie będzie napisać funkcję, która złącza &lt;em&gt;n&lt;/em&gt; posortowanych leniwych sekwencji – potencjalnie nieskończonych – w jedną, również posortowaną, również leniwą i również potencjalnie nieskończoną”). Zajęło mi to może pół godziny. Potem sięgnąłem po &lt;a href="https://github.com/nathell/skyscraper"&gt;Skyscrapera&lt;/a&gt;, żeby pościągać wyniki w poszczególnych okręgach ze stron PKW i powydłubywać je z HTML-i. Jakie było moje zdziwienie, kiedy się okazało, że one ani nie zawierają tych danych już wyrenderowanych, ani kod javascriptowy nie odwołuje się do żadnego API, które by zwracało dane w JSON-ie, no, niechby nawet w XML-u!&lt;/p&gt;&lt;p&gt;Zafrapowany, zajrzałem w zakładkę „Requests” przeglądarkowych devtoolsów. Okazało się, że JS robi requesta AJAX-owego pod adres &lt;a href="https://wybory.gov.pl/sejmsenat2023/data/obkw/pl_po_okr_sejm.blob"&gt;&lt;code&gt;https://wybory.gov.pl/sejmsenat2023/data/obkw/pl_po_okr_sejm.blob&lt;/code&gt;&lt;/a&gt;. Różne detektory typów plików mówiły o tych danych tylko &lt;code&gt;data&lt;/code&gt;. Ewidentnie coś binarnego, ale na bazę SQLite nie wyglądało.&lt;/p&gt;&lt;p&gt;Bliższe przyjrzenie się zdeobfuskowanej paczce javascriptowej rozwiązało zagadkę. Wyszło na to, że nazwy niektórych plików odpowiadają bibliotece &lt;a href="https://github.com/protobufjs/protobuf.js"&gt;protobuf.js&lt;/a&gt;. To były binarne pliki googlowskiego Protocol Buffers! Normalnie do ich przetwarzania potrzebna jest znajomość definicji protokołu (plik &lt;code&gt;.proto&lt;/code&gt;), ale kol. Marcin podsunął online’owe &lt;a href="https://protobuf-decoder.netlify.app/"&gt;narzędzie&lt;/a&gt;, które umie sparsować takie dane bez tej dodatkowej wiedzy. Kod tego narzędzia, lekko zmieniony, trafił koniec końców do mojego repo: udało mi się zmusić go do wygenerowania topornego, ale jednak, JSON-a, w którym metodą zgadywania i przystawiania do wersji wyrenderowanej na stronie PKW wykminiłem, co jest czym czego i jak szukać danych, których potrzebuję.&lt;/p&gt;&lt;p&gt;Zatem fajne ćwiczenie z reverse-engineeringu. Jak już odwaliłem tę robotę, to się okazało, że na stronie PKW wiszą sobie jakby nigdy nic pliki CSV z danymi z poszczególnych komisji… Zgaduję jednak, że dzięki temu, że wystarczyło pobierać cyklicznie tylko jeden plik, miałem zawsze najświeższe i spójne dane (stąd zapewne chwilowe rozbieżności z serwisem Michała, które się zdarzały w ciągu dnia).&lt;/p&gt;&lt;p&gt;Nie mówcie nikomu, ale nie ustrzegłem się kilku błędów. Na przykład, żeby ułatwić sobie życie, kwestię przekraczania progu wyborczego rozwiązałem metodą zahardkodowania komitetów, które spełniły ten warunek albo nie musiały tego robić, na podstawie wyników exit polli. Potem najpierw się okazało, że wpisałem tam za dużo komitetów (z rozpędu dopisałem BS i PJJ), a potem, że ta lista nie była nigdzie używana… Na szczęście, ponieważ metoda d’Hondta stawia komitetom tak czy owak dość wyśrubowane wymagania, nie wpłynęło to na poprawność wyników.&lt;/p&gt;&lt;p&gt;Za to w którymś momencie z wyników zniknęła Mniejszość Niemiecka i ludzie mnie o to zapytali. Zaniepokojony, poszedłem sprawdzać, co robi algorytm. I okazało się, że jest jednak dobrze: wszystkie znaki na niebie i ziemi wskazują, że nadchodząca kadencja Sejmu będzie pierwszą, w której MN nie będzie miała swojego reprezentanta.&lt;/p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="pl" dir="ltr"&gt;Uwzględnia. Dla danych z 18:21 ostatni, dwunasty iloraz w Opolu jest pierwszym ilorazem Konfederacji (30764/1) i jest on większy niż pierwszy iloraz MN (24861/1), który jest dopiero na szesnastym miejscu.&lt;/p&gt;&amp;mdash; Daniel Janus &#127987;️‍&#127752; &#127482;&#127462; @nathell@mastodon.social (@nathell) &lt;a href="https://twitter.com/nathell/status/1713955405460644036?ref_src=twsrc%5Etfw"&gt;October 16, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Dostałem też &lt;a href="https://twitter.com/coffeetea0/status/1713977372553146740"&gt;pytanie&lt;/a&gt; o to, jak zmieniłby się rozkład mandatów, gdyby do przeliczenia zamiast d’Hondta została zastosowana metoda Sainte-Laguë. Sam się nad tym zastanawiałem, więc wyciągnąłem w metrze laptopa i poczyniłem szybko odnośną modyfikację kodu. Istnieje kilka wariantów metody Sainte-Laguë – pierwszy dzielnik bywa równy 1 albo 1,4 – sprawdziłem więc te dwa.&lt;/p&gt;&lt;p&gt;I z tymi tabelkami (aktualnymi na godzinę 00:35 we wtorek) Was zostawiam. Warto się zastanowić, jak na naszą demokrację wpłynąłby ewentualny powrót do ordynacji stosowanej ostatni raz w 2001 roku.&lt;/p&gt;&lt;figure&gt;
&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Komitet&lt;/th&gt;&lt;th&gt;Mandaty&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Prawo i Sprawiedliwość&lt;/td&gt;&lt;td class="right"&gt;171&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Koalicja Obywatelska&lt;/td&gt;&lt;td class="right"&gt;140&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Trzecia Droga&lt;/td&gt;&lt;td class="right"&gt;70&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lewica&lt;/td&gt;&lt;td class="right"&gt;41&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Konfederacja&lt;/td&gt;&lt;td class="right"&gt;37&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mniejszość Niemiecka&lt;/td&gt;&lt;td class="right"&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;figcaption class="center"&gt;Podział mandatów w Sejmie, gdyby obowiązywała oryginalna ordynacja Sainte-Laguë&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Komitet&lt;/th&gt;&lt;th&gt;Mandaty&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Prawo i Sprawiedliwość&lt;/td&gt;&lt;td class="right"&gt;176&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Koalicja Obywatelska&lt;/td&gt;&lt;td class="right"&gt;145&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Trzecia Droga&lt;/td&gt;&lt;td class="right"&gt;71&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lewica&lt;/td&gt;&lt;td class="right"&gt;37&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Konfederacja&lt;/td&gt;&lt;td class="right"&gt;31&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;figcaption class="center"&gt;Podział mandatów w Sejmie, gdyby obowiązywała zmodyfikowana ordynacja Sainte-Laguë (taka jak w wyborach w 2001)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2023-01-08:post:algorytm</id>
    <title>Słowa, których nie lubię: &lt;em&gt;algorytm&lt;/em&gt;</title>
    <link href="http://plblog.danieljanus.pl/2023/01/08/algorytm/"/>
    <updated>2023-01-08T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;h2 id="słowa,-słowa,-słowa"&gt;Słowa, słowa, słowa&lt;/h2&gt;&lt;p&gt;Lubię wszystkie słowa.&lt;/p&gt;&lt;p&gt;Bez słów — mówionych, zapisywanych, pokazywanych, dotykanych — nie bylibyśmy ludźmi. Słowa są ważne i potrzebne. I piękne. Zwłaszcza kiedy używamy ich zgodnie z (prze)znaczeniem.&lt;/p&gt;&lt;p&gt;Czasem jednak wypowiadamy słowa automatycznie, nie zastanawiając się nad ich adekwatnością do tego, co naprawdę mamy na myśli. Bywa, że ujmuje to wagi i wiarygodności naszym wypowiedziom: używamy słów nieprecyzyjnych albo wytartych do tego stopnia, że nie potrafią unieść ciężaru własnego znaczenia. To nie znaczy, że są niepotrzebne i trzeba je wyrzucić z języka — ale myślę, że warto na nie uważać.&lt;/p&gt;&lt;p&gt;Zbieram takie słowa na swój prywatny użytek. I o nich będzie cykl na blogu, którego początkiem jest ten post.&lt;/p&gt;&lt;p&gt;Na pierwszy ogień idzie słowo „algorytm”.&lt;/p&gt;&lt;h2 id="co-to-jest-algorytm?"&gt;Co to jest algorytm?&lt;/h2&gt;&lt;p&gt;Zajrzyjmy najpierw do słownika (&lt;a href="https://wsjp.pl/haslo/podglad/34845/algorytm"&gt;Wielkiego Słownika Języka Polskiego PAN&lt;/a&gt;):&lt;/p&gt;&lt;figure class="image"&gt;&lt;img alt="Słownikowa definicja „algorytmu”" src="/img/blog/algorytm-wsjp.png" title="Słownikowa definicja „algorytmu”" /&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Wszyscy znamy przykłady tak rozumianych algorytmów. Natknęliśmy się na nie, kiedy przyrządzaliśmy potrawę według przepisu (tak, przepisy kuchenne też pasują do tej definicji!) albo kiedy uczyliśmy się w podstawówce dodawania pisemnego (na ogół, mówiąc o algorytmach, mamy na myśli jednak takie operujące na obiektach matematycznych). Informatycy wymyślili mnóstwo algorytmów rozwiązujących najróżniejsze problemy: od sortowania liczb przez kompresję danych po tłumaczenie między językami naturalnymi. Algorytmy są wszędzie tam, gdzie komputery.&lt;/p&gt;&lt;p&gt;Dlaczego więc chcę uważać na słowo opisujące coś tak przydatnego? Dlatego, że ono zupełnie nagle i niepostrzeżenie nabrało nowego znaczenia, którego nie notuje chyba jeszcze żaden słownik. To nowe znaczenie częściowo pokrywa się z dotychczasowym, ale są między nimi znaczące różnice. W dalszej części tekstu będę czasami pisał „algorytm&lt;sub&gt;1&lt;/sub&gt;” i „algorytm&lt;sub&gt;2&lt;/sub&gt;” dla podkreślenia, o który sens chodzi.&lt;/p&gt;&lt;h2 id="algorytm-algorytmowi-nierówny"&gt;Algorytm algorytmowi nierówny&lt;/h2&gt;&lt;p&gt;Popatrzmy na przykłady:&lt;/p&gt;&lt;p&gt;&lt;figure class="image"&gt;&lt;img alt="Nagłówek prasowy: Algorytm TikToka podsuwa filmik o samobójstwie w 3 minuty" src="/img/blog/algorytm-demagog.webp" title="Nagłówek prasowy: Algorytm TikToka podsuwa filmik o samobójstwie w 3 minuty" /&gt;&lt;figcaption&gt;Nagłówek &lt;a href="https://demagog.org.pl/analizy_i_raporty/algorytm-tiktoka-podsuwa-filmik-o-samobojstwie-w-3-minuty/"&gt;artykułu z serwisu Demagog&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;figure class="image"&gt;&lt;img alt="Mem Partii Razem: Algorytm pod kontrolą" src="/img/blog/algorytm-razem.jpg" title="Mem Partii Razem: Algorytm pod kontrolą" /&gt;&lt;figcaption&gt;Grafika z &lt;a href="https://twitter.com/partiarazem/status/1605186510789156864"&gt;tweeta Partii Razem&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;figure class="image"&gt;&lt;img alt="Z Twittera: Netflix ma nadal zepsute algorytmy." src="/img/blog/algorytmy-netflix.png" title="Z Twittera: Netflix ma nadal zepsute algorytmy." /&gt;&lt;figcaption&gt;Losowy &lt;a href="https://twitter.com/piotrmiecz/status/1612017998524518400"&gt;wynik wyszukiwania&lt;/a&gt; frazy „algorytmy” na Twitterze&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Czujemy intuicyjnie, że na tych obrazkach pod hasłem „algorytm” rozumie się coś trochę innego niż „ograniczony ciąg precyzyjnie określonych czynności”. Można wskazać kilka cech charakterystycznych dla algorytmu&lt;sub&gt;2&lt;/sub&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Jego wynikiem jest jakiś rodzaj decyzji dotyczącej człowieka — być może Ciebie! Może to lista wyświetlanych filmów, a może wynagrodzenie za pracę.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Często występuje stadnie, tzn. w liczbie mnogiej. (O ile pojedynczy „algorytm” czasem miewa jeszcze znaczenie pierwotne, o tyle „algorytmy” prawie zawsze wskazują na sens, o którym mowa.)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Jest nieprzejrzysty: nie da się zgadnąć, jaki będzie wynik, ani nawet jakie dane wejściowe mają na ów wynik wpływ. Zasady działania zazdrośnie strzeże korporacja, której nazwa pojawia się przy słowie „algorytm”. Algorytmy Netflixa, algorytm TikToka…&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Prawdopodobnie jest bardzo skomplikowany, ale trudno to orzec na pewno, bo korporacja.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Warto wykazywać się wobec niego daleko idącą nieufnością.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Jak widać — zakres zadań, o których wspomina oryginalna definicja, zostaje w tym znaczeniu znacznie ograniczony; zaś „treść” algorytmu — owe „precyzyjnie określone czynności” — w zasadzie nie ma znaczenia, bo jest niepoznawalna. Algorytmy&lt;sub&gt;1&lt;/sub&gt; postrzegamy przez pryzmat tego, &lt;em&gt;co&lt;/em&gt; i &lt;em&gt;jak&lt;/em&gt; robią; w algorytmach&lt;sub&gt;2&lt;/sub&gt; ważne jest to, jak ich działanie &lt;em&gt;wpływa na ludzi&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Wspominałem, że to nowe znaczenie wdarło się do polszczyzny przebojem i nagle. Jak bardzo nagle? Zapytanie &lt;code&gt;algorytm.*&lt;/code&gt; w korpusie monitorującym &lt;a href="https://monco.frazeo.pl"&gt;Monco&lt;/a&gt; pokazuje skokowy wzrost popularności „algorytmów” w latach 2018–2021:&lt;/p&gt;&lt;img src="/img/blog/algorytmy-monco.png" alt="Frekwencja słowa 'algorytm' w polszczyźnie XXI wieku"&gt;
&lt;p&gt;(Ten ponowny spadek od 2022 daje do myślenia — być może to artefakt wynikający z tego, że korpus zawiera po prostu mniej próbek tekstu dla tego okresu.)&lt;/p&gt;&lt;p&gt;Ten wykres nie pokazuje, ile z tych wzmianek o algorytmach dotyczy rzeczywiście algorytmów&lt;sub&gt;2&lt;/sub&gt;. Aby spróbować odpowiedzieć sobie na to pytanie, spróbowałem ręcznie zaklasyfikować 25 pierwszych z brzegu wystąpień „algorytmu” na Twitterze do jednego z dwóch znaczeń. Wynik: w 19 przypadkach była mowa o algorytmie&lt;sub&gt;2&lt;/sub&gt;; w 4 nie byłem w stanie ustalić na pewno, o które znaczenie chodziło autorowi; i tylko w dwóch tweetach chodziło na pewno o algorytm&lt;sub&gt;1&lt;/sub&gt;. To oczywiście nie ma pozorów reprezentatywności — w polszczyźnie specjalistycznej proporcje byłyby zupełnie inne — ale zarysowuje pewien obraz.&lt;/p&gt;&lt;h2 id="dlaczego-uważać-na-„algorytmy&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;”?"&gt;Dlaczego uważać na „algorytmy&lt;sub&gt;2&lt;/sub&gt;”?&lt;/h2&gt;&lt;p&gt;Rozpisałem się o nowym znaczeniu słowa, ale nie oddałem jeszcze sprawiedliwości tytułowi notki. Zmiana zakresu znaczeniowego słów jest wszak najnaturalniejszym procesem pod słońcem w każdym żywym języku — jaki jest sens się nań boczyć? Zwłaszcza, że zjawisko jest nowe, realne, a potrzeba nazwania go — niezaprzeczalna?&lt;/p&gt;&lt;p&gt;Powody mam dwa.&lt;/p&gt;&lt;p&gt;Pierwszy to taki, że słowo „algorytm” nie do końca trafnie wskazuje winowajcę w sytuacji, gdy coś za nas decyduje, które reklamy nam pokazać, a które ukryć. Owszem, steruje tym implementacja pewnego algorytmu&lt;sub&gt;1&lt;/sub&gt;, tajemnica Złego Korpo. Ale na ogół ten algorytm&lt;sub&gt;1&lt;/sub&gt; ma coś wspólnego z uczeniem maszynowym.&lt;/p&gt;&lt;p&gt;Istnieje więc pewien &lt;em&gt;model&lt;/em&gt; — można o nim myśleć jako o wielkim worku pełnym liczb; miliony, może nawet miliardy liczb, używane jako dane wejściowe dla algorytmu&lt;sub&gt;1&lt;/sub&gt;, ale &lt;em&gt;niebędące jego częścią&lt;/em&gt;. Może to wagi sieci neuronowej, może duże macierze albo inne tensory. To w tym modelu zaszyta jest logika odpowiedzialna za to, że Kowalskiemu pokażemy teraz drapaki dla kotów. To jego Złe Korpo strzeże najzazdrośniej na świecie. Algorytm&lt;sub&gt;1&lt;/sub&gt; sam w sobie opisuje tylko to, w jaki sposób z modelu i danych, które w danej chwili mamy o Kowalskim, wydobyć wynik wskazujący na konkretną reklamę.&lt;/p&gt;&lt;p&gt;Innymi słowy — ujawnienie samego algorytmu&lt;sub&gt;1&lt;/sub&gt; niewiele pomogłoby na jawność całości procesu decyzyjnego, o ile dane wejściowe, w tym model, pozostają tajne.&lt;/p&gt;&lt;p&gt;Drugi powód dotyczy nacechowania emocjonalnego. Obserwujemy tu zjawisko, że termin, początkowo nacechowany neutralnie lub nawet nieco pozytywnie, nabiera w zbliżonym kontekście nowego znaczenia jednoznacznie negatywnego. W takich razach łatwo o nieporozumienia, które biorą się stąd, że emocje związane z nowym, popularnym sensem przenoszone są na użycia słowa w sensie pierwotnym. Znaczenie pierwotne zostaje niejako „zduszone” przez nowe.&lt;/p&gt;&lt;p&gt;Tak było ze słowem &lt;em&gt;hacker&lt;/em&gt; w języku angielskim, które oryginalnie znaczyło mniej więcej „cieślę” — osobę używającą siekiery do wyrobu mebli. Potem zaczęło oznaczać kogoś, kto znajduje przyjemność w dogłębnym rozumieniu działania rozmaitych mechanizmów (w szczególności systemów komputerowych) aż po najdrobniejsze szczegóły; dopiero w latach 80. kultura masowa rozpowszechniła znaczenie „włamywacza komputerowego” i w takim sensie słowo to zostało zaimportowane do polszczyzny. Do dziś &lt;em&gt;hackerzy&lt;/em&gt; w tym pozytywnym sensie oburzają się kojarzeniem ich z działalnością przestępczą.&lt;/p&gt;&lt;p&gt;Inny angielski przykład to &lt;em&gt;crypto&lt;/em&gt;, stare żargonowe określenie kryptografii i jej algorytmów, przejęte bez pytania w ostatnich latach przez społeczność skupioną wokół przekrętów finansowych zwanych dla niepoznaki kryptowalutami.&lt;/p&gt;&lt;p&gt;Wracając do algorytmów: myślę, że potrzebujemy precyzji wypowiedzi. Często z kontekstu wynika, co mamy na myśli, ale warto mieć pod ręką słowo — lub dłuższą frazę — precyzyjnie wskazującą znaczenie, o które chodzi. Sam zamierzam od tej pory mówić o niebezpieczeństwach związanych z &lt;em&gt;algorytmami podejmującymi decyzje&lt;/em&gt;, &lt;em&gt;korpoalgorytmami&lt;/em&gt; lub &lt;em&gt;korpomodelami&lt;/em&gt;.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2022-12-24:post:jak-zostalem-programista</id>
    <title>Jak zostałem programistą</title>
    <link href="http://plblog.danieljanus.pl/2022/12/24/jak-zostalem-programista/"/>
    <updated>2022-12-24T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Nazywam się Daniel Janus, mam 38 lat i od 31 lat jestem programistą.&lt;/p&gt;&lt;p&gt;Czasem się tak przedstawiam i czasem budzi to zaciekawienie, powątpiewanie albo jedno i drugie naraz. Wracam więc myślą do samych początków.&lt;/p&gt;&lt;p&gt;Pamiętam, jechaliśmy z ojcem autobusem. Mogłem mieć pięć, może sześć lat. Tata ni stąd, ni zowąd zaczął opowiadać mi o komputerach. Nowe, nieznane mi wcześniej słowo. Nie mam pojęcia, jaka była treść tej opowieści, za to dokładnie pomiętam żarówki, które zapaliły mi się w oczach. I ten kawałek rozmowy:&lt;/p&gt;&lt;p&gt;– Jakby czarodziejski???&lt;br&gt; – Jakby czarodziejski.&lt;/p&gt;&lt;p&gt;Minęło kilkadziesiąt milionów sekund. Zdaje się, że łaziłem w międzyczasie i mówiłem, że chcę komputer. I w końcu, w 1991, tata przyjechał z saksów w Niemczech i mówi: kupiłem ci komputer! Nazywał się Commodore 64 i pachniał plastikiem, elektroniką i świeżością. Dzisiejsze komputery już tak nie pachną.&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/c64-box.jpg" alt="Pudełko od C64"&gt;&lt;figcaption&gt;To nie jest dokładnie TO pudełko – zdjęcie wygrzebałem z jakiejś aukcji na Allegro – ale tak wyglądało.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Zanim go uruchomiliśmy, upłynęły jeszcze prawie dwa tygodnie. W tym czasie w domu nastał nowy, kolorowy telewizor, a ja siedziałem jak na szpilkach, czekałem na ten telewizor, fantazjowałem o tym, jak to będzie, kiedy całość już podłączymy, obmacywałem zawartość pudełek (było jeszcze drugie, ze stacją dyskietek), zgadywałem, który klawisz do czego służy, i czytałem dołączone materiały. Oczywiście po niemiecku. I oczywiście nic z tego nie rozumiałem – niemieckiego miałem się zacząć uczyć w podstawówce dopiero od piątej klasy.&lt;/p&gt;&lt;p&gt;Były tam też wypisane różne rzeczy, które komputer może wyświetlić na ekranie, i takie, które trzeba do niego wklepać z klawiatury, żeby jakoś zareagował.&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/c64-book.jpg" alt="Commodore 64 Bedienungshandbuch: okładka"&gt;&lt;figcaption&gt;Zdjęcie tym razem z eBaya.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;W końcu nadszedł TEN dzień. Komputer podłączony, telewizor włączony, szukamy odpowiedniej częstotliwości wideo… i wreszcie z telewizyjnego białego szumu wyłonił się obraz, znany doskonale wielu ludziom z mojego pokolenia:&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/c64-start.webp" alt="Ekran startowy C64"&gt;&lt;figcaption&gt;Ekran startowy C64. Kto go pamięta?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Nie pamiętam, co zrobiłem potem. Pewnie nacisnąłem wszystkie klawisze po kolei i nauczyłem się wyłączać i włączać całość. Jakoś intuicyjnie wykminiłem, jak działają klawisze kursora, co robi spacja, a co RETURN. Potem próbowałem przepisywać z tego niemieckiego podręcznika różne polecenia i patrzyć, co się stanie.&lt;/p&gt;&lt;p&gt;W którymś momencie odkryłem, że po wpisaniu &lt;code&gt;LOAD "$",8&lt;/code&gt; (dlaczego akurat 8? dlaczego akurat dolar? strasznie mnie to ciekawiło, ale nie wnikałem w szczegóły) i naciśnięciu RETURN komputer wypisuje na ekranie nowy, nieznany mi komunikat błędu, ale nie robi tego od razu – najpierw stacja dysków wydaje kilka dźwięków i zaczyna mrugać diodą na czerwono. Włożyłem dyskietkę do napędu (z komputerem przyjechało kilka dyskietek demonstracyjnych) i spróbowałem jeszcze raz. Tym razem zamigotało na zielono i komunikat był inny!&lt;/p&gt;&lt;p&gt;Wpisałem &lt;code&gt;LIST&lt;/code&gt;, tak jak kazał podręcznik (nadal nie bardzo rozumiejąc, co właściwie się dzieje). Commodore wyświetlił długą serię komunikatów. Były tam jakieś liczby, skróty i nazwy. Wtedy po raz kolejny wpatrzyłem się w ten kawałek podręcznika:&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/c64-instrukcja.jpg"&gt;W Internecie wbrew powiedzeniu czasem giną rzeczy, ale instrukcja do C64 na szczęście ocalała.&lt;/figure&gt;
&lt;p&gt;Zaraz… &lt;code&gt;PROGRAMMNAME&lt;/code&gt;? NAME? Imię? Nazwa?&lt;/p&gt;&lt;p&gt;Napisałem &lt;code&gt;LOAD "LASER FORCE",8&lt;/code&gt;. Instrukcji o dokładnie takim brzmieniu nie było w podręczniku! Ale &lt;code&gt;"LASER FORCE"&lt;/code&gt; to była jedna z nazw, bodaj ostatnia, w tym długim komunikacie – i pomyślałem, że można wstawić ją zamiast owego enigmatycznego &lt;code&gt;PROGRAMMNAME&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Strzał w dziesiątkę. Komputer odpowiedział komunikatem z podręcznika i zamyślił się na dłuższą chwilę, stacja dysków znowu zaśpiewała zgrzytliwą piosenkę. Czułem, że jestem na dobrym tropie.&lt;/p&gt;&lt;p&gt;Wreszcie wpisałem &lt;code&gt;RUN&lt;/code&gt;. Uruchomiłem grę! Zbiegła się cała rodzina.&lt;/p&gt;&lt;iframe width="100%" height="500" src="https://www.youtube.com/embed/M5tpEAW_zzA" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;Dokonałem wtedy swoich dwóch pierwszych odkryć programistycznych. Być może były to najdonioślejsze odkrycia w całej mojej karierze.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span&gt;Mogę wpisać inną nazwę i załaduje się inny program. A mówiąc ogólniej: różnym parametrom polecenia odpowiadają różne skutki. Inne wejście – inne wyjście.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Warto eksperymentować! Prawdopodobnie nic nie popsuję, najwyżej wyskoczy jakiś błąd i zawsze mogę spróbować jeszcze raz.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Było też odkrycie trzecie, zupełnie niezwiązane z programowaniem:&lt;/p&gt;&lt;ol start="3"&gt;&lt;li&gt;Lubię grać w gry.&lt;/li&gt;&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;W następnych tygodniach wypróbowałem wszystkie programy i gry na tych kilku dyskietkach, które miałem. Ale skąd wziąć następne dyskietki? W roku 1991 w małym miasteczku (które jeszcze wtedy było wsią) nie było łatwo o dostęp do oprogramowania. Równolegle z graniem próbowałem więc przepisywać następne programy z podręcznika.&lt;/p&gt;&lt;p&gt;Niedługo potem kilku innych chłopaków z sąsiedztwa też dostało C64. Byłem jednak jedyną osobą ze stacją dysków i ten fakt zaważył na mojej przyszłości – wszyscy inni mieli magnetofony. O ile łatwo było po prostu wymieniać się kasetami, o tyle w moim przypadku pozyskanie nowych gier wymagało wyprawy do kolegi i zabrania ze sobą stacji. Rodzice mi nie pozwalali. Samemu? Ruchliwą ulicą? Z drogim sprzętem?&lt;/p&gt;&lt;p&gt;W międzyczasie tata wytrzasnął skądś stare numery „Bajtka”. Był tego pokaźny stosik, archiwa z kilku lat. „Bajtek” odegrał ogromną rolę w mojej wczesnej edukacji informatycznej: były tam małe kawałki wiedzy podane w przystępny sposób. Nie wszystko rozumiałem, ale przynajmniej było po polsku. I rubryka „Tylko dla przedszkolaków” tłumaczyła nie tylko to, co robią pomieszczone tam programy, ale również – dlaczego tak, a nie inaczej.&lt;/p&gt;&lt;p&gt;Dostałem też rozmaite książki – o BASIC-u i o samym komputerze, w tym polską biblię C64, „Commodore 64” Bohdana Frelka. Różne kawałki wiedzy zaczęły składać mi się w głowie w całość. Eksperymentowałem. Próbowałem zmieniać różne rzeczy w programach z Bajtka i w końcu pisać własne. Pamiętam, że jeden z moich programików z tamtych czasów nazywał się &lt;code&gt;ABC FIZYKI&lt;/code&gt; i wyświetlał definicje jednostek SI, które przepisałem z jednej z książek ojca.&lt;/p&gt;&lt;p&gt;Kiedy kilka lat później – bodaj w roku 1996 – przesiadłem się na PC, znałem już zupełnie nieźle BASIC i zaczynałem kumać, o co chodzi w asemblerze 6502. Stary komputer jednak poszedł w odstawkę. Rodzice sprzedali go niedługo potem, ale do dziś, po prawie ćwierćwieczu, potrafię bez zaglądania do internetu wyrecytować zgrubną mapę pamięci C64, powiedzieć, do których komórek pamięci trzeba wpisać jaki bajt, żeby tekst wyświetlał się na biało na czarnym tle z czarną ramką (mój ulubiony schemat kolorów z tamtych czasów), jaki jest kod znaku PETSCII odpowiedzialnego za czyszczenie ekranu oraz która procedura Kernala odpowiada za restart systemu, a która za wypisanie znaku na ekran.&lt;/p&gt;&lt;p&gt;Na pececie uczyłem się Pascala. Ale to już zupełnie inny rozdział tej historii.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2021-11-09:post:typografia-banknotow</id>
    <title>Jestem Polakiem, więc mam typografię polską</title>
    <link href="http://plblog.danieljanus.pl/2021/11/09/typografia-banknotow/"/>
    <updated>2021-11-09T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;„Mickiewicz wielki poeta, ale kto go zna, a mnie śpiewa cała Polska i Ukraina” — mawiał o sobie &lt;a href="https://pl.wikipedia.org/wiki/Tomasz_Padura"&gt;Tomasz Padura&lt;/a&gt;, któremu przypisuje się czasem autorstwo znanej wszystkim piosenki „Hej, sokoły”. Błędne to przypisanie — autor słów nie jest znany — ale anegdotkę uważam za przednią.&lt;/p&gt;&lt;p&gt;A kto był najpowszechniejszym polskim malarzem? Albo szerzej — artystą plastykiem? Kto mógłby powiedzieć o sobie „Matejko wielki malarz, ale kto go zna”? Czyich to dzieł reprodukcje oglądamy (ba, dotykamy i z zadowoleniem gromadzimy w wielu egzemplarzach) każdego dnia, w każdym domu w Polsce?&lt;/p&gt;&lt;p&gt;&lt;a href="https://pl.wikipedia.org/wiki/Andrzej_Heidrich"&gt;Andrzeja Heidricha&lt;/a&gt;, projektanta polskich banknotów.&lt;/p&gt;&lt;p&gt;&lt;figure class="image"&gt;&lt;img alt="Banknot 500 zł z Tadeuszem Kościuszką" src="/img/blog/kosciuszko.jpg" title="Banknot 500 zł z Tadeuszem Kościuszką" /&gt;&lt;figcaption&gt;Pierwszy banknot Andrzeja Heidricha, który wszedł do obiegu: 500 zł z Tadeuszem Kościuszką (fot. Piotr Kamionka/REPORTER/East News, ilustracja cyt. za &lt;a href="https://culture.pl/pl/artykul/jak-sie-robi-pieniadze"&gt;culture.pl&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;To z jego pracowni wyszły wzory wszystkich banknotów, którymi płacimy codziennie od 1975 roku. To jego dzieła przetrwały zmianę ustroju, zmiany władzy i denominację. To jemu Narodowy Bank Polski, z okazji półwiecza współpracy, poświęcił &lt;a href="https://www.nbp.pl/AndrzejHeidrich/e-book/files/assets/downloads/files/AndrzejHeidrich.pdf"&gt;specjalny album&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Nie mam kompetencji, żeby wypowiadać się krytycznie (albo bezkrytycznie) o tych projektach, oceniając je. Ale przyglądając się banknotom Heidricha, warto uświadomić sobie coś jeszcze: Heidrich był nie tylko projektantem grafiki użytkowej.&lt;/p&gt;&lt;p&gt;Był również typografem.&lt;/p&gt;&lt;p&gt;Zaprojektował między innymi (w 1971 roku) pismo dziełowe o nazwie „Bona”. Cyfrową wersją tego kroju — zwaną &lt;a href="http://bonanova.wtf/"&gt;Bona Nova&lt;/a&gt; i powstałą w roku 2017 — złożone są słowa, które teraz czytacie, drogie osoby odwiedzające tego bloga.&lt;/p&gt;&lt;p&gt;To taki mój mały, lokalny, typograficzny patriotyzm: ukłon w stronę człowieka, który żył w tym samym zakątku świata co ja, mówił tym samym językiem i tworzył piękne rzeczy. A razem z nim — ukłon w stronę polskiej tradycji typograficznej, sięgającej czasów renesansu, od Jana Januszowskiego przez Adama Półtawskiego i Zygfryda Gardzielewskiego aż po Łukasza Dziedzica, którego &lt;a href="https://www.latofonts.com/"&gt;&lt;em&gt;Lato&lt;/em&gt;&lt;/a&gt; zdobyło wielką popularność w przestrzeni publicznej i w internecie.&lt;/p&gt;&lt;p&gt;Heidrich sam zaprojektował charakterystyczne, unikatowe liternictwo dla polskich banknotów. Tak mówił w 2014 w &lt;a href="https://www.tygodnikpowszechny.pl/mistrz-od-wielkich-pieniedzy-23994"&gt;wywiadzie dla „Tygodnika Powszechnego”&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;W banknotach nie można posługiwać się żadnym istniejącym wzorem pisma. Litery piszę ręcznie za pomocą pędzla.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Andrzej Heidrich zmarł w 2019 roku i nie było mu dane doczekać emisji najnowszego banknotu, zaprojektowanej przez Justynę Kopecką kolekcjonerskiej (!) dwudziestozłotówki z Lechem Kaczyńskim, po którą, kiedy piszę te słowa, pod oddziałami NBP ustawiają się kolejki. Może zresztą dobrze, że tego nie doczekał. Tak jak poprzednio, o samym projekcie nie będę się wypowiadał, ale ta typografia…&lt;/p&gt;&lt;p&gt;Przyjrzyjmy się uważnie:&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/kaczynski20ann.jpg" alt="Banknot 20 zł z Lechem Kaczyńskim"&gt;&lt;figcaption&gt;20 zł z Lechem Kaczyńskim (2021). Strzałkami zaznaczyłem wykorzystane kroje pisma. Tak, ja też nie umiem zrobić tak, żeby było ładnie.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Większość tekstów na banknocie złożono krojem &lt;a href="https://pl.wikipedia.org/wiki/Palatino"&gt;Palatino&lt;/a&gt;, dostarczanym między innymi razem z systemem macOS; napis „Narodowy Bank Polski” — pismem &lt;a href="https://pl.wikipedia.org/wiki/Times_New_Roman"&gt;Times New Roman&lt;/a&gt; (jeśli dobrze identyfikuję tę odmianę Timesa), dostarczanym z systemami macOS i Windows. (Kroju numeru seryjnego nie umiem zidentyfikować.)&lt;/p&gt;&lt;p&gt;Palatino i Times New Roman to piękne, czytelne kroje. W dodatku szeroko stosowane. Ten pierwszy — do składu książek; ten drugi — do ogłoszeń wywieszanych na klatce schodowej.&lt;/p&gt;&lt;p&gt;Tymczasem to jest, na litość wszelaką, &lt;em&gt;banknot&lt;/em&gt;, i to nie dość, że &lt;em&gt;okolicznościowy&lt;/em&gt;, to jeszcze &lt;strong&gt;Odwołujący Się Do Wartości!&lt;/strong&gt; Wiecie, rozumiecie, Ojczyzna, Naród, Tragicznie Zmarły Prezydent i Inne Słowa Pisane Wielką Literą! O projektowaniu wiem bardzo mało, ale nawet ja wiem, że kontekst ma znaczenie zasadnicze. Nie umiem sobie wyobrazić procesu myślowego, który doprowadził do takiego a nie innego wyboru typografii. Za to domyślam się olbrzymiej presji i goniących terminów.&lt;/p&gt;&lt;p&gt;Bo cóż innego mogło przyczynić się do TAKIEGO &lt;a href="https://pl.wikipedia.org/wiki/Kerning"&gt;kerningu&lt;/a&gt; — jeśli nie to, że starczyło czasu na wyklikanie „Dodaj tekst” w programie graficznym, ale na wycyzelowanie odstępów międzyliterowych już nie?&lt;/p&gt;&lt;figure&gt;&lt;img src="/img/blog/kaczynski20kerning.jpg" alt="Błędnie skernowane litery WA w słowie DWADZIEŚCIA"&gt;&lt;figcaption&gt;Wystarczy przeczytać artykuł na Wikipedii, żeby zorientować się, że odstęp między literami W i A w słowie DWADZIEŚCIA jest za duży. Program graficzny powinien był go poprawić, bo w porządnych fontach komputerowych zapisane są dane o właściwym kerningu.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;No i tak. Ten kontrast między pisoojczyźnianym, koniecznie biało-czerwonym patosem a jakością wykonania przypomina &lt;a href="https://naimaonline.wordpress.com/2013/05/02/patriotyzm-balkonowy/"&gt;flagę zatkniętą na brudnym balkonie&lt;/a&gt;. Andrzej Heidrich przewraca się w grobie.&lt;/p&gt;&lt;p&gt;Ale jest jeszcze jeden smaczek. Oto słowa Lecha Kaczyńskiego „Warto być Polakiem” (nie, Panie Prezydencie: biorąc pod uwagę przestępstwa, które funkcjonariusze państwa polskiego w majestacie bezprawia &lt;a href="https://oko.press/straznik-mi-klamie-oczy-takiej-osoby-u-nas-nie-bylo-zostala-zniknieta-rozumiesz-zniknieta/"&gt;popełniają na granicy&lt;/a&gt;, tam zdecydowanie bardziej pasuje inne pięcioliterowe słowo, które wprawdzie zaczyna się na &lt;em&gt;w&lt;/em&gt;, ale kończy na &lt;em&gt;styd&lt;/em&gt;) — więc otóż te słowa złożono, jako się rzekło, krojem Palatino, odwołującym się do tradycji typograficznej…&lt;/p&gt;&lt;p&gt;…niemieckiej.&lt;/p&gt;&lt;p&gt;Bo &lt;a href="https://pl.wikipedia.org/wiki/Hermann_Zapf"&gt;Hermann Zapf&lt;/a&gt;, wybitny typograf i autor tego pisma, był takiej właśnie narodowości.&lt;/p&gt;&lt;p&gt;Co oczywiście samo w sobie nie ma znaczenia. Nie ma znaczenia też, czy się jest Polakiem czy nie, jaką ma się tożsamość płciową, orientację seksualną czy kolor skóry. Tym bardziej więc narodowość twórcy kroju nie powinna mieć wpływu na jego wybór.&lt;/p&gt;&lt;p&gt;Tutaj jednak jest ona subtelną częścią kontekstu. Ten zaś, jako się rzekło, znaczenie ma. Zasadnicze.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2021-10-10:post:jak-placic-za-ksiazki</id>
    <title>Jak płacić za książki</title>
    <link href="http://plblog.danieljanus.pl/2021/10/10/jak-placic-za-ksiazki/"/>
    <updated>2021-10-10T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;h2 id="fortuna-imperatrix-mundi"&gt;Fortuna imperatrix mundi&lt;/h2&gt;&lt;p&gt;Nie nadaję się na imperatora wszechświata.&lt;/p&gt;&lt;p&gt;Czasem zastanawiam się, co bym zmienił, gdybym mógł dowolnie urządzić świat, i zawsze dochodzę do tego samego wniosku: &lt;em&gt;nie wiem&lt;/em&gt;. Widzę mnóstwo problemów ze status quo, ale we wszystkich rozwiązaniach, które mi przychodzą do głowy, widzę następne problemy. I tak to się kręci.&lt;/p&gt;&lt;p&gt;Ale niektóre pomysły wydają mi się sensowne. Na przykład mam całkiem konkretną wizję tego, jak w moim idealnym świecie wygląda płacenie za książki i e-booki (które na potrzeby tej notki też będę uznawać za książki). Zanim jednak do niej przejdę, napiszę parę słów o tym, co mi się nie podoba w tej rzeczywistości, którą mamy.&lt;/p&gt;&lt;h2 id="jak-jest"&gt;Jak jest&lt;/h2&gt;&lt;p&gt;Ustalmy na początku, że potrzeba wynagradzania i incentywizacji autorów książek (a także innych osób mających wpływ na ich powstawanie) jest oczywista.&lt;/p&gt;&lt;p&gt;Pomijając wszystkie niedostatki kapitalizmu, cierpimy jako społeczeństwo — tak sądzę — na pewien rodzaj dwójmyślenia.&lt;/p&gt;&lt;p&gt;Z jednej strony wielbimy (słusznie!) biblioteki jako świątynie kultury i wiedzy. Ich rola jako instytucji kulturo- i socjotwórczych jest nie do przecenienia. Z drugiej strony krzywimy się (też nie bez racji), kiedy ktoś nielegalnie ściąga e-booka z internetu: to złamanie umowy społecznej.&lt;/p&gt;&lt;p&gt;A tymczasem z punktu widzenia autora zysk z takiego czytelnika jest &lt;strike&gt;w obu przypadkach dokładnie taki sam: zero, nic, null, nada, żodyn&lt;/strike&gt; w drugim przypadku zerowy, zaś w pierwszym — na ogół niższy, niż gdyby kupił on książkę w księgarni.&lt;/p&gt;&lt;p&gt;(W tym miejscu w oryginalnym poście był przykład z trzema książkami, tyleż obrazowy, co nieprawdziwy. Od kilku lat funkcjonuje w Polsce &lt;a href="https://copyrightpolska.pl"&gt;system wynagrodzeń dla autorów i wydawców książek&lt;/a&gt;, stanowiący realizację koncepcji &lt;a href="https://pl.wikipedia.org/wiki/Public_Lending_Right"&gt;Public Lending Right&lt;/a&gt;. Ponadto, jak zwraca uwagę &lt;a href="https://twitter.com/koziolek/status/1447274466195738634"&gt;Koziołek na Twitterze&lt;/a&gt;, biblioteki nie płacą za książki cen okładkowych, tylko wyższe, obejmujące biblioteczne opłaty licencyjne. To oczywiście osłabia wagę tej argumentacji, ale sami oceńcie, jak bardzo.)&lt;/p&gt;&lt;p&gt;Pamiętacie akcję &lt;a href="https://web.archive.org/web/20130115023636/http://stratakazika.pl/"&gt;„Strata Kazika”&lt;/a&gt;? Pokazuje ona dość dobitnie, że samo skopiowanie treści cyfrowej (czy to legalnie, czy nie) nie jest samo w sobie szczególnie doniosłym aktem. Pendrive wypełniony trzydziestoma tysiącami książek, zalegający na dnie szuflady, nie jest automatycznie wart kilkaset tysięcy złotych. Jeśli ktoś przeczyta cztery z tych książek, to tylko te cztery będą przedstawiać sobą jakąkolwiek wartość. Książka wnosi wartość w życie czytelnika nie wtedy, kiedy wchodzi w jego posiadanie, tylko &lt;em&gt;w procesie czytania&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Wydaje mi się więc sensowne, żeby płatność za książkę — a mówiąc dokładnie: za jej treść — przypiąć do tego procesu.&lt;/p&gt;&lt;h2 id="jak-to-widzę"&gt;Jak to widzę&lt;/h2&gt;&lt;p&gt;Stąd poniższy pomysł. To jest luźny szkic, szczegóły w wielu miejscach są do dopracowania.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span&gt;W modelu dystrybucji książek papierowych nie zmienia się nic: książkę można kupić albo wypożyczyć z biblioteki.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wszystkie e-booki można ściągnąć z internetu legalnie, za darmo, w nieograniczonych ilościach.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Kupując książkę w księgarni, płaci się za fizyczny przedmiot, nie za treść.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Na końcu każdej książki i e-booka jest numer konta, na który można wpłacać pieniądze. Środki te następnie dzielone są między osoby, które przyczyniły się do powstania książki (autora, rzecz jasna, ale też osoby odpowiedzialne za redakcję, korektę, skład, ilustracje, okładkę i może promocję).&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Istnieje silne &lt;strong&gt;oczekiwanie społeczne&lt;/strong&gt;, żeby płacić za każdą przeczytaną książkę, o ile czytelnika na to stać. Dotyczy to książek i e-booków pozyskanych w dowolny sposób. W szczególności to znaczy, że za książkę kupioną w księgarni płaci się de facto dwa razy — raz przy zakupie i drugi po przeczytaniu — oraz że za książki wypożyczone z biblioteki, pożyczone lub skopiowane od znajomych, przeczytane w czytelni i za e-booki również należy zapłacić.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Taki system działałby tym lepiej, im więcej osób faktycznie płaciłoby za książki po przeczytaniu, wspierając w ten sposób autorów i kulturę słowa. Ale wydaje mi się, że nie sposób wymusić go prawnie: nie będzie przecież Urząd Kontroli Czytelnictwa na wyrywki nachodzić ludzi w domach, żeby sprawdzić, czy właśnie coś czytają i czy aby na pewno zapłacili. To by wymagało zmiany ogólnej mentalności, wykształcenia w ludziach przeświadczenia, że wspieranie autorów jest taką samą moralną powinnością jak płacenie za bilet autobusowy. Stąd wytłuszczone hasło „oczekiwanie społeczne”.&lt;/p&gt;&lt;p&gt;Oczekiwanie silne, ale też miękkie: ważne jest, żeby zorganizować to w sposób niewykluczający. Swego czasu, szczenięciem będąc, spędzałem w bibliotekach czwartą część życia — i doskonale pamiętam towarzyszące tym wizytom wrażenie obcowania z nieprzebranym bogactwem, w którym można nurzać się i tarzać zupełnie za darmo. Nie chciałbym tamtemu sobie odbierać tej frajdy z uwagi na za niskie kieszonkowe. Ani też dostępu do książek ludziom, którzy żyją od pierwszego do pierwszego, a książki wypożyczane z bibliotek są jedną z nielicznych radości. Dlatego wyobrażam sobie hasło „płacisz, &lt;em&gt;jeśli cię stać&lt;/em&gt;”. To czytelnik decydowałby o wysokości wsparcia — bazując na własnych możliwościach i na czerpanej z książki wartości.&lt;/p&gt;&lt;p&gt;A ile to by było w praktyce? Nie mam pojęcia. Pewnie typowo kilka-kilkanaście złotych. Tak wygląda rozbicie ceny typowej książki na składniki (&lt;a href="https://www.granice.pl/news/skad-sie-bierze-cena-ksiazki/5868"&gt;źródło&lt;/a&gt;):&lt;/p&gt;&lt;img src="/img/blog/cena-ksiazki.jpg" alt="Co składa się na cenę książki"&gt;
&lt;p&gt;Wyobrażam sobie, że z takiej opłaty byłyby pokrywane wszystkie te koszty, które &lt;em&gt;nie&lt;/em&gt; wiążą się z fizycznym wytworzeniem książki jako przedmiotu, czyli — wnioskując z powyższego wykresu — jakieś 30% aktualnej ceny typowej książki. Tak się akurat składa, że &lt;a href="https://pl.wikipedia.org/wiki/Koszt_kra%C5%84cowy"&gt;koszt krańcowy&lt;/a&gt; wytworzenia nowego egzemplarza książki nie zawiera akurat tych samych elementów, a koszt krańcowy e-booka jest przyzerowy — dlatego zgaduję, że cena książki w księgarni mogłaby być niższa mniej więcej o tyle samo; wyobrażam sobie też, że e-booki można by ściągać za darmo lub za groszową opłatą, z której utrzymywane byłyby serwisy zajmujące się dystrybucją.&lt;/p&gt;&lt;p&gt;Warto też zadać sobie dwa pytania: „ile złotych miesięcznie mogę przeznaczyć na wspieranie autorów?” i „ile książek czytam miesięcznie?”. Podzielmy jedno przez drugie — i już będziemy wiedzieć, jakie są nasze możliwości.&lt;/p&gt;&lt;h2 id="jaskółki-czynią-wiosnę"&gt;Jaskółki czynią wiosnę&lt;/h2&gt;&lt;p&gt;Jest jeszcze jeden powód, dla którego podoba mi się ta wizja: to nie jest propozycja typu „wszystko albo nic”. Można — i warto, myślę — urzeczywistniać ją stopniowo, oddolnie. W istocie pozbierałem w niej pomysły, które tu i ówdzie funkcjonują już teraz.&lt;/p&gt;&lt;p&gt;Ten tekst dojrzewał we mnie od dawna, a główną pożywką dlań jest książka &lt;a href="https://practicaltypography.com/"&gt;„Practical Typography”&lt;/a&gt; Matthew Buttericka. Autor wprost mówi, że ta książka nie jest za darmo:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Jedynym źródłem przychodu z tej książki są czytelnicy, tacy jak Ty. Jeśli nie zapłacisz, książka umrze.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I ludzie płacą. Nie są to powalające sumy, a odsetek płacących czytelników jest &lt;a href="https://practicaltypography.com/effluents-influence-affluence.html"&gt;jeszcze mniej powalający&lt;/a&gt; (mentalność, mentalność!) — ale też autor sam przyznaje, że książka jest pewnego rodzaju eksperymentem wydawniczo-społecznym. Zwłaszcza że dostępna jest tylko przez przeglądarkę (nie ma wersji papierowej).&lt;/p&gt;&lt;p&gt;Niektórzy współcześni autorzy publikują swoje powieści na wolnych licencjach, tak że można je kopiować bez ograniczeń: Goodreads zna &lt;a href="https://www.goodreads.com/list/show/9437.Free_Creative_Commons_Novels"&gt;42 powieści na licencjach Creative Commons&lt;/a&gt;. Wszystkie te książki są normalnie dostępne w sprzedaży w wersjach papierowych. Cory Doctorow, autor siedmiu z tych 42 książek, przyznaje, że otwartolicencyjność nie tylko nie zmniejszyła, ale wręcz &lt;a href="https://wiki.creativecommons.org/wiki/Case_Studies/Cory_Doctorow"&gt;zwiększyła&lt;/a&gt; przychód z wersji papierowych.&lt;/p&gt;&lt;p&gt;Przykład z mojej branży: pełen tekst książki Petera Seibela „Practical Common Lisp”, wydana przez Apress, jest dostępny najzupełniej legalnie na &lt;a href="https://gigamonkeys.com/book/"&gt;stronie WWW autora&lt;/a&gt; — i to też nie zniechęca ludzi do kupowania egzemplarzy papierowych.&lt;/p&gt;&lt;p&gt;Sformułowanie „oczekiwanie społeczne” (&lt;em&gt;social expectation&lt;/em&gt;) zaczerpnąłem zaś od Marijna Haverbeke, który &lt;a href="https://marijnhaverbeke.nl/fund/"&gt;w ten sposób określa&lt;/a&gt; swój stosunek do przychodów z oprogramowania własnego autorstwa.&lt;/p&gt;&lt;p&gt;Płacenie za książkę po przeczytaniu jest rodzajem mikromecenatu — tworzy pewną więź między czytelnikiem a twórcami książki. Chcę tu więc wspomnieć o innych formułach, które — choć w inny sposób — też przyczyniają się do takich więzi. Chodzi mi o &lt;a href="https://artrage.pl/bookrage"&gt;BookRage&lt;/a&gt;, serwis, w którym to my decydujemy, ile płacimy za książki i jaka część tej wpłaty jest przekazywana autorowi; a także o serwisy typu &lt;a href="https://patronite.pl/"&gt;Patronite&lt;/a&gt;, gdzie możemy ważnych dla nas twórców wspierać cyklicznymi wpłatami.&lt;/p&gt;&lt;h2 id="bądź-zmianą,-którą-chcesz-widzieć-w-świecie"&gt;Bądź zmianą, którą chcesz widzieć w świecie&lt;/h2&gt;&lt;p&gt;Tako rzecze Mahatma Gandhi, a ja nie chcę być gołosłowny.&lt;/p&gt;&lt;p&gt;Przyznaję się otwarcie: nie zapłaciłem do tej pory za przywoływaną już książkę Buttericka. Raczej ją przejrzałem niż przeczytałem od deski do deski, ale gdyby nie jej eksperymentalność, pewnie nie zapamiętałbym jej jakoś szczególnie. Niemniej…&lt;/p&gt;&lt;p&gt;Butterick zauważa, że płacimy za książki jeszcze w trzeci, być może najważniejszy sposób: własnym czasem. Cennym, nieodnawialnym zasobem. Tak to ujmuje:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Każda dobra książka kosztuje za mało; żadna zła nie jest za tania.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;To zdanie, to zdanie! Tym zdaniem, Panie Matthew, zaciągnąłem u Pana lata temu dług; i wraz z publikacją tego artykułu niniejszym ten dług spłacam. Jest to też dowód tego, że czasem decyzja o wsparciu dojrzewa w czytelniku długo.&lt;/p&gt;&lt;img src="/img/blog/splata-dlugu.png" alt="Potwierdzenie wpłaty 39 USD"&gt;
&lt;p&gt;Z mojej korespondencji z &lt;a href="https://www.bjornlarssen.com/"&gt;Bjørnem Larssenem&lt;/a&gt; (zaprawdę powiadam Wam, czytajcie &lt;a href="https://www.bjornlarssen.com/books/"&gt;„Storytellers”&lt;/a&gt;, to dobre jest; mam nadzieję, że Bjørn nie pogniewa się za upublicznienie tego wyimka):&lt;/p&gt;&lt;p&gt;Ja:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Wrzucam Ci trzy kawy na kofi i to nie dlatego, że chcę drugą książkę, jak już będzie (chociaż oczywiście chcę!), ale dlatego, że marzy mi się świat, w którym jak kogoś stać, to wspiera twórców, których czyta/słucha/ogląda/etc i ceni. W moim idealnym świecie jest oczekiwanie społeczne, żeby to robić niezależnie od tego, czy książkę się kupiło, wypożyczyło z biblio czy spiraciło, a link do ko-fi jest na ostatniej stronie "Storytellers". :)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Bjørn:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Niemrawo zbieram się do dodania tip jar na &lt;a href="http://bjornlarssen.com"&gt;bjornlarssen.com&lt;/a&gt;, czyli oddzielnego ko-fi tylko dla czytelników, którzy chcą mnie zachęcić i wesprzeć, albo takich, którzy książkę spiracili, a teraz im głupio. Właśnie dowiodłeś mi, że to nie jest głupi pomysł i może jednak trzeba to po prostu zrobić.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A Wy? Kupujcie książki autorów, których lubicie. Wspierajcie na Patronite i podobnych serwisach. Piszcie do nich (&lt;a href="https://fuse.pl/beton/hello-i-love-you.html"&gt;choćby po to, żeby podziękować&lt;/a&gt;) i namawiajcie na udostępnianie numerów kont. Albo zaproście ich na kawę, jeśli możecie.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2020-10-02:post:nowy-swiat</id>
    <title>Rozwiązanie zagadki Radia Nowy Świat</title>
    <link href="http://plblog.danieljanus.pl/2020/10/02/nowy-swiat/"/>
    <updated>2020-10-02T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Dziś na zamkniętej grupie fejsbukowej dla Patronów &lt;a href="https://nowyswiat.online"&gt;Radia Nowy Świat&lt;/a&gt; pojawiła się zagadka-szyfr.&lt;/p&gt;&lt;img src="/img/blog/rns.jpg"&gt;
&lt;p&gt;Wrzucam rozwiązanie na bloga, bo tak wygodniej i mogę wstawić więcej obrazków, gdzie chcę. Jeśli chcesz, Czytelniku, pogłówkować we własnym zakresie – nie czytaj dalej.&lt;/p&gt;&lt;p&gt;Jeśli interesuje Cię rozwiązanie (i tok myślowy, który mnie do niego zaprowadził), przewiń stronę…&lt;/p&gt;&lt;div style="height: 1000px"&gt;&lt;/div&gt;
&lt;p&gt;Pierwsza myśl: ta wiadomość naprawdę składa się z takich liter. Sprawdzam “na oko”: jest dużo &lt;code&gt;A&lt;/code&gt;, a mało &lt;code&gt;Ź&lt;/code&gt;, czyli tak, jakbyśmy się spodziewali w losowym polskim tekście. Próbuję czytać kolumnami, od tyłu, ruchem konika szachowego – bezskutecznie!&lt;/p&gt;&lt;p&gt;To może przyjrzyjmy się uważniej frekwencji. Akurat mam pod ręką REPL-a Clojurowego:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;rn&lt;/span&gt;ś &lt;span class="hljs-string"&gt;&amp;quot;ABCĆĄEĘIODFÓUYAGĄHJKEĘLŁIMNŃOPÓUYRSAĄŚETĘWZIOÓŹUYŻBACĄEĆĘDFIGHJOÓUKYLŁMAĄNŃEĘIPROSŚÓTWZŹUŻBYCĆADĄEFĘGHJKILŁOÓUMYANĄEŃĘIOÓPRSUŚYAĄTEWĘZIOÓŹŻUBYCĆADFGĄEĘIOHJÓUYKLAŁĄEMĘNI&amp;quot;&lt;/span&gt;)
&lt;span class="hljs-comment"&gt;;=&amp;gt; #&amp;#x27;rnś&lt;/span&gt;

(&lt;span class="hljs-name"&gt;frequencies&lt;/span&gt; rnś)
&lt;span class="hljs-comment"&gt;;=&amp;gt; {\A 10, \Ą 10, \B 4, \C 4,  \Ć 4, \D 4, \E 10, \Ę 10,&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  \F 4,  \G 4,  \H 4, \I 10, \J 4, \K 4, \L 4,  \Ł 4,&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  \M 4,  \N 4,  \Ń 3, \O 9,  \Ó 9, \P 3, \R 3,  \S 3,&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  \Ś 3,  \T 3,  \U 9, \W 3,  \Y 9, \Ź 3, \Z 3,  \Ż 3}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ha, jednak nie. To wygląda bardzo ciekawie: wszystkie samogłoski występują po 10 lub 9 razy, a wszystkie spółgłoski – po 4 lub 3 razy. W takim razie jest bardzo mało prawdopodobne, że znaczenie mają litery jako takie.&lt;/p&gt;&lt;p&gt;Przyglądam się bliżej: to nie jest przypadek, że tekst zaszyfrowany zaczyna się od &lt;code&gt;ABCĆ&lt;/code&gt;. Gdyby skreślić wszystkie spółgłoski, to zostaniemy z &lt;code&gt;AĄEĘIOÓÓUY&lt;/code&gt;… I analogicznie, gdyby skreślić samogłoski, to dostajemy &lt;code&gt;BCĆDFGH&lt;/code&gt;…&lt;/p&gt;&lt;p&gt;Wobec tego to nie litery niosą informację w tym tekście, tylko przeplot spółgłosek i samogłosek. Zobaczmy, jak to wygląda:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;vowel?&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;comp&lt;/span&gt;&lt;/span&gt; boolean #{&lt;span class="hljs-character"&gt;\A&lt;/span&gt; &lt;span class="hljs-character"&gt;\Ą&lt;/span&gt; &lt;span class="hljs-character"&gt;\E&lt;/span&gt; &lt;span class="hljs-character"&gt;\Ę&lt;/span&gt; &lt;span class="hljs-character"&gt;\I&lt;/span&gt; &lt;span class="hljs-character"&gt;\O&lt;/span&gt; &lt;span class="hljs-character"&gt;\Ó&lt;/span&gt; &lt;span class="hljs-character"&gt;\U&lt;/span&gt; &lt;span class="hljs-character"&gt;\Y&lt;/span&gt;}))
&lt;span class="hljs-comment"&gt;;=&amp;gt; #&amp;#x27;vowel?&lt;/span&gt;

(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;comp&lt;/span&gt;&lt;/span&gt; {&lt;span class="hljs-literal"&gt;false&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-literal"&gt;true&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt;} vowel?) rnś)
&lt;span class="hljs-comment"&gt;;=&amp;gt; (1 0 0 0 1 1 1 1 1 0 0 1 1 1 1 0 1 0 0 0 1 1 0 0&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 0 0 0 1 0 1 1 1 0 0 1 1 0 1 0 1 0 0 1 1 1 0 1&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 0 0 1 0 1 1 0 1 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 0 0 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 0 1 0&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 1 0 1 0 0 0 0 1 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 0 0 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 0 1 0 1 0 0&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  1 0 0 0 1 1 1 1 1 0 0 1 1 1 0 0 1 0 1 1 0 1 0 1)&lt;/span&gt;

(&lt;span class="hljs-name"&gt;frequencies&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; vowel? rnś))
&lt;span class="hljs-comment"&gt;;=&amp;gt; {true 86, false 82}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tak, to wygląda lepiej. 86 samogłosek i 82 spółgłoski. Tylko co to za kod? Morse? Może spółgłoski to kropki, a samogłoski – kreski? Próbuję online-owych dekoderów. Kod Morse’a bez odstępów jest niejednoznaczny, więc przewijam się przez różne opcje. Nic nie pasuje. Gdyby kreski i kropki ustawić odwrotnie, też nie działa.&lt;/p&gt;&lt;p&gt;To może to jest ASCII? Przyglądam się bliżej: jest prawie regułą, że co ósmy znak to samogłoska. W takim razie gdyby samogłoski odpowiadały zerom, a spółgłoski jedynkom, i gdyby pogrupować je po 8 (tak jak na obrazku), to…&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;to-number&lt;/span&gt; [x] (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; + (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; * (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;reverse&lt;/span&gt;&lt;/span&gt; x) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;iterate&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; % %) &lt;span class="hljs-number"&gt;1&lt;/span&gt;))))
&lt;span class="hljs-comment"&gt;;=&amp;gt; to-number&lt;/span&gt;

(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; to-number (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;partition&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;vowel?&lt;/span&gt; %) &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt;) rnś)))
&lt;span class="hljs-comment"&gt;;=&amp;gt; (112 97 115 116 101 98 105 110 46 99 111 109&lt;/span&gt;
&lt;span class="hljs-comment"&gt;;=&amp;gt;  47 98 72 116 84 107 112 99 74)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ha! Ciepło, ciepło!&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; str (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; char *1))
&lt;span class="hljs-comment"&gt;;=&amp;gt; &amp;quot;pastebin.com/bHtTkpcJ&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Gorąco! Jest adres strony!&lt;/p&gt;&lt;p&gt;Wchodzimy na tego pastebina i…&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ALILMRD = SLWDWASWY SLWDWADZLWSLĄY PLĘĆ ALILNOÓE SLDWASWY
DZLWELĘYOMŚCLW YTSLĘCT SLWDWASWY DEMDZLWŚCLM
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Miliard ŻE CO plęć tysięcy? To są jakieś liczby, napisane przez kogoś, kto sepleni. Albo zamienia litery miejscami. Można by zgadnąć, według jakiego klucza, ale on akurat jest podany w tytule strony. I na obrazku. MALINOWE BUTY.&lt;/p&gt;&lt;p&gt;To teraz sięgnijmy po &lt;code&gt;tr&lt;/code&gt;, shellową zamieniarkę znaków:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs shell"&gt;tr &amp;#x27;MALINOWE BUTY&amp;#x27; &amp;#x27;AMILONEW UBYT&amp;#x27; &amp;lt; plik.txt
MILIARD = SIEDEMSET SIEDEMDZIESIĄT PIĘĆ MILIONÓW
SIDEMSET DZIEWIĘTNAŚCIE TYSIĘCY SIEDEMSET DWADZIEŚCIA
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I to już? To ma być rozwiązanie zagadki? Jaki miliard? Jakie 775719720? Pal sześć literówkę, ale przecież się nie równa!&lt;/p&gt;&lt;p&gt;Wrzucam to na grupę. I dostaję ostateczną podpowiedź od pani Oliwii.&lt;/p&gt;&lt;img src="/img/blog/rns2.png"&gt;
&lt;p&gt;Z minusem wychodzi 224280280. I to jest rozwiązanie: &lt;a href="https://nowyswiat.online"&gt;skądinąd&lt;/a&gt; znany numer telefonu.&lt;/p&gt;&lt;p&gt;Dzwońcie tam, gdy Wam smutno albo źle w życiu. A jeszcze lepiej &lt;a href="https://patronite.pl/radionowyswiat"&gt;zostańcie patronami&lt;/a&gt;, to też będziecie mogli rozwiązywać zagadki!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2018-11-11:post:niepodleglosc</id>
    <title>Święto niepodległości</title>
    <link href="http://plblog.danieljanus.pl/2018/11/11/niepodleglosc/"/>
    <updated>2018-11-11T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Wychowałem się w przemocowym domu.&lt;/p&gt;&lt;p&gt;Przemoc fizyczna należała w nim do rzadkości. Jednak przemoc nie musi być fizyczna: przemocą jest każdy akt narzucania własnej woli siłą drugiemu człowiekowi. W istocie na to, co dla mnie ważne, było w tym domu miejsce o tyle, o ile było akceptowane lub przynajmniej tolerowane przez inną osobę. Kiedy zaś rzeczy ważne dla mnie nie pasowały do jej wizji świata, spotykały się ze zdecydowanym sprzeciwem, a czasem jawną pogardą. Dobrze pamiętam wrażenie, że muszę najdosłowniej wyszarpywać dla siebie przestrzeń po kawałku i kurczowo się jej trzymać, i towarzyszące temu wrażeniu uczucie sfrustrowanej bezsilności.&lt;/p&gt;&lt;p&gt;Właśnie ze względu na ową bezsilność ominął mnie klasyczny okres buntu młodzieńczego: trudno się buntować, kiedy nie ma się siły. Pozbawiano mnie możliwości dokonywania wyborów, podejmowania decyzji i mierzenia się z ich konsekwencjami. Jeszcze długo po usamodzielnieniu się, kiedy zniknęły fizyczne ograniczenia w postaci opresyjnego środowiska, nie potrafiłem znaleźć w sobie wiary, że mogę różne rzeczy, a moje wybory polegały na ogół na pójściu ścieżką najłatwiejszą albo na biernym siedzeniu w miejscu.&lt;/p&gt;&lt;p&gt;Takie (i jeszcze gorsze) rzeczy robi ludziom przemoc.&lt;/p&gt;&lt;p&gt;Trzeba było wielu lat i wielomiesięcznej terapii, żeby to się zaczęło zmieniać. I żeby pojawiła się we mnie ta iskra, to coś w środku, co sprawia, że idziemy przez życie z podniesionym czołem, robimy swoje, a świat nam nie wchodzi w paradę.&lt;/p&gt;&lt;p&gt;Nazywamy to wewnątrzsterownością. Wolnością samostanowienia. Niepodległością?&lt;/p&gt;&lt;p&gt;Dzisiejsze święto jest dla mnie okazją do refleksji, w jakim kraju i w jakim świecie chcę żyć. Jestem przekonany, że przemoc, narzucanie innym własnego zdania, nieliczenie się z drugim człowiekiem – fundamentalnie kłóci się z wartością, która dała temu świętu nazwę.&lt;/p&gt;&lt;p&gt;Marzę o tym, żebyśmy wszyscy rozmawiali ze sobą; żebyśmy byli gotowi słyszeć, co jest ważne i potrzebne innym osobom, i żebyśmy sami byli słyszani; i żeby na każdą i każdego z nas było miejsce.&lt;/p&gt;&lt;p&gt;Jestem też przekonany, że nie ma czegoś takiego jak wrodzona potrzeba wywierania przemocy. Przemoc to zaklęty krąg, który potrafi propagować się przez pokolenia i zarażać całe zbiorowości; ale ten krąg można i trzeba rozrywać, neutralizować. Uważam to za swój obowiązek, w takim zakresie, w jakim mogę to robić. I chcę, żeby instytucje w kraju, w którym żyję, potrafiły sprawnie gasić przemoc, pomagać jej ofiarom i skutecznie chronić osoby, które są szczególnie na nią narażone.&lt;/p&gt;&lt;p&gt;Bo kraj – to ludzie. A niepodległy kraj to kraj niepodległych ludzi.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2015-04-29:post:zawalanie</id>
    <title>Zawalanie 101</title>
    <link href="http://plblog.danieljanus.pl/2015/04/29/zawalanie/"/>
    <updated>2015-04-29T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Dziś będzie o tym, jak zawalać sprawy i przeżyć.&lt;/p&gt;&lt;p&gt;Tekst na ten sam temat popełniła ostatnio &lt;a href="http://snafu.evil.pl/2014/12/jak-byc-doroslym-3-czyli-jak-cos-spieprzyc/"&gt;Kya&lt;/a&gt;; zachęcam do przeczytania, ponieważ są tam same dobre i prawdziwe rzeczy. Tym niemniej czuję się upoważniony do napisania postscriptum. To jedna z niewielu dziedzin, w których mam bogate, długoletnie i udokumentowane doświadczenie, więc nie muszę silić się na besserwisserstwo. Jednocześnie, &lt;code&gt;#include &amp;lt;stddisclaimer.h&amp;gt;&lt;/code&gt;: całość jest pisana wyłącznie z mojej perspektywy. Będę pisał o sprawach trudnych dla mnie, bazując na moich własnych odczuciach i opiniach. U mnie działa, ale jakkolwiek wydaje mi się to sensowne, niekoniecznie musi działać dla kogokolwiek innego. Ludzie są różnie skonstruowani.&lt;/p&gt;&lt;h2 id="zawalamy-sprawę"&gt;Zawalamy sprawę&lt;/h2&gt;&lt;p&gt;Istotą zawalania jest zaszkodzenie komuś. Świadome czy nie, bardziej lub mniej brzemienne w skutki – spieprzyłeś, jeśli czyjaś sytuacja jest gorsza niż była albo powinna być. Tym kimś możesz być Ty sam albo ktoś inny. Pół biedy, jeśli to Ty sam: na przykład przez lenistwo oblałeś egzamin albo dostałeś zupełnie niepotrzebny, bezsensowny mandat za niezdjęcie w porę nogi z gazu w zabudowanym. Wtedy jest łatwiej powiedzieć „trudno” i wziąć to na klatę. Wtedy masz pełny obraz sytuacji i uczucia nikogo innego nie wchodzą w zakres problemu. Możesz podziękować tylko samemu sobie i tylko z samym sobą to przepracować.&lt;/p&gt;&lt;p&gt;Zatem reszta notki będzie poświęcona wyłącznie przypadkowi B: zaszkodziłeś komuś innemu. To mogą być rzeczy zupełnie różnego kalibru: może na przykład nastąpiłeś komuś na stopę, przez co druga osoba odczuwa ból fizyczny. Może zgubiłeś czyjąś własność. Może zapomniałeś o umówionym spotkaniu. Może powiedziałeś coś, co sprawiło drugiej osobie ból. Może nie wywiązałeś się z danej komuś obietnicy. Może wprowadziłeś kogoś w błąd. Może nadużyłeś czyjegoś zaufania. Może to zaufanie nadszarpnąłeś albo zrujnowałeś zupełnie.&lt;/p&gt;&lt;p&gt;Przychodzi wtedy taki moment, kiedy sobie z całą mocą uświadamiasz konsekwencje tego, co zrobiłeś (albo czego nie zrobiłeś). W takich chwilach w umyśle pojawia się emocja, którą można zwerbalizować w kilku słowach: &lt;em&gt;o kurwa. O żeż kurwa. KURWA MAĆ&lt;/em&gt;.&lt;/p&gt;&lt;h2 id="emocje"&gt;Emocje&lt;/h2&gt;&lt;p&gt;Spoglądasz na świat i na siebie oczyma drugiej osoby i dociera do ciebie, co mniej więcej musi czuć i myśleć na Twój temat. Przecież to nie jest tak, że &lt;em&gt;chciałeś&lt;/em&gt; spieprzyć, że to wynika ze złej woli. Nie powinno być źle, a jednak jest. Twój mózg podsuwa Ci obrazy sprzed dnia, sprzed godziny, kiedy wszystko było po staremu, kiedy wszyscy byli spokojni i kiedy nie czułeś jeszcze tej guli, która teraz rośnie Ci w gardle.&lt;/p&gt;&lt;p&gt;Dochodzimy teraz do momentu, w którym należy tę gulę nazwać po imieniu. Ona się nazywa „poczucie winy”.&lt;/p&gt;&lt;p&gt;Poczucie winy jest bodaj najbardziej bezsensowną emocją, jaka istnieje. Trzeba to sobie uświadomić co najmniej kilka razy. Poczucie winy łatwo eskaluje, otwierając ślepą uliczkę wiodącą stromo w dół na łeb na szyję i kończącą się zimnym, twardym murem, z którego trudno wdrapać się z powrotem tam, gdzie się było, a przecież trzeba, bo życie toczy się dalej. Poczucie winy do niczego nie prowadzi, nie sprawia, że komukolwiek jest od niego lepiej. Ani Tobie, ani osobie, której zaszkodziłeś. Zwłaszcza tej drugiej osobie.&lt;/p&gt;&lt;p&gt;Bardzo łatwo jest o tym zapomnieć. Paradoksem jest, że poczucie winy jest emocją niezwykle &lt;em&gt;egoistyczną&lt;/em&gt;, skupiającą uwagę głównie na sobie. Częścią poczucia winy jest chęć zaprowadzenia sprawiedliwości i ukarania osoby odpowiedzialnej, czyli Ciebie. Organizm wprowadza to w życie natychmiast, obniżając poziom serotoniny i produkując duże ilości kortyzolu. Równocześnie zaczynasz zastanawiać się, jak sobie zaszkodzić. W patologicznych sytuacjach przybiera to postać zadawania sobie fizycznego bólu, który z Twojego punktu widzenia ma tę zaletę, że ściąga uwagę na sobie i odciąga ją od stricte psychicznej części poczucia winy.&lt;/p&gt;&lt;p&gt;I w całym tym autodestrukcyjnym mechanizmie druga osoba gdzieś się gubi. Trzeba to przepracowywać, zamieniając poczucie winy na &lt;em&gt;świadomość&lt;/em&gt; tego, że się spieprzyło. Sama bezemocjonalna świadomość jest w porządku, bo dowodzi istnienia jakiegoś kręgosłupa moralnego, sprawia, że nie olewasz sprawy, ale przyjmujesz ją i mówisz: „OK, przejdźmy do następnego kroku”.&lt;/p&gt;&lt;h2 id="działanie"&gt;Działanie&lt;/h2&gt;&lt;p&gt;A do następnego kroku trzeba przejść jak najszybciej. Jest nim naprawienie szkody, jeśli to możliwe. Tu pojawia się kolejna pułapka, w którą łatwo wpaść, a mianowicie działanie pod wpływem impulsu i bez zastanowienia. Nigdy nie wolno pozwolić sobie na luksus niemyślenia. Na ogół ma się wystarczająco dużo czasu, żeby uspokoić się i trzeźwym okiem ocenić sytuację i dalsze perspektywy jej rozwoju, i to najlepsze, co można wtedy przedsięwziąć. Odpowiedzieć sobie na pytania: co możesz zrobić? Co powinieneś zrobić?&lt;/p&gt;&lt;p&gt;Jeżeli jednym ze skutków sytuacji jest wywołanie u drugiej osoby negatywnych emocji, jak złość, smutek albo rozgoryczenie, to trzeba wczuć się w oczekiwania danej osoby. Czy oczekuje natychmiastowych wyjaśnień? Czy nie ma ochoty na rozmowę? Często, wbrew chęci działania, dobrym pomysłem jest odsunąć się i odczekać. Natężenie emocji maleje hiperbolicznie z czasem. Kiedy da się okrzepnąć odczuciom i przemyśli sprawę na spokojnie (kluczowe pytania: co doprowadziło do takiej sytuacji? co można było zrobić, żeby jej uniknąć? co można zrobić, żeby to się nie powtórzyło?), szczera rozmowa jest potem znacznie łatwiejsza. Co nie znaczy, że nie jest trudna.&lt;/p&gt;&lt;p&gt;W ogóle jeśli ma się problem z wkręcaniem się w poczucie winy, to wyplątanie się z tego jest szalenie trudne. Nawet jeśli jest się świadomym wszystkich tych mechanizmów, o których wyżej, umysł łatwo wpada w znane od lat koleiny i aktywne przeciwdziałanie im wymaga dużo wysiłku. Trudne jest nawet radzenie sobie z reminiscencjami. Użyłem wcześniej metafory hiperboli: ona jest asymptotyczna do zera, ale nigdy tego zera nie osiąga. Emocje przygasają, by w końcu przejść w stan uśpienia, ale zapamiętujemy je i potrafimy je wyindukować z powrotem. Takie wspomnienie potrafi po wielu latach niespodziewanie wylecieć zza krzaka jak szerszeń i boleśnie użądlić. To się może zdarzyć zawsze i jest częścią długofalowych konsekwencji naszych działań. Warto wtedy pamiętać, że Ty-z-przywołanego-czasu i Ty-teraz to nie jest ta sama osoba; jesteś teraz w innym punkcie czasoprzestrzeni, z nowym bagażem doświadczeń, już to przerobiłeś i wystarczy.&lt;/p&gt;&lt;h2 id="recydywa"&gt;Recydywa&lt;/h2&gt;&lt;p&gt;Jeżeli zdarza Ci się zrobić coś głupiego i stwierdzasz: „nie mam pojęcia, dlaczego zachowałem się tak a tak”, to można to uznać za mentalny &lt;em&gt;glitch&lt;/em&gt;, niewłaściwy sygnał przetransportowany przez sieć neuronów w Twoim mózgu, wywołany promieniowaniem kosmicznym albo czymś równie incydentalnym. Zdarza się, trudno, nie ma się czym przejmować. Ale jeżeli podobna sytuacja ma miejsce po raz drugi, trzeci, piąty, dziesiąty, to hipotezę o przypadkowości należy odrzucić. Mamy do czynienia z pewnym schematem. W sytuacji, kiedy za dziesiątym razem nadal nie wiesz, co powoduje takie Twoje zachowanie, to rada jest jedna: trzeba szukać głębiej. Rozwiązanie istnieje, być może zakopane gdzieś głęboko w podświadomości, ale jest.&lt;/p&gt;&lt;p&gt;Znalezienie go w takiej sytuacji bywa cholernie trudne. Oczywiście, że nie jest łatwe: gdyby było, to dokopałbyś się do niego już dawno. Efektem ubocznym dodatkowo utrudniającym zadanie jest, że poziom trudności gry „zwalcz poczucie winy” zostaje podkręcony do poziomu &lt;em&gt;extremely hard&lt;/em&gt; (a znajoma gula w gardle, za każdym razem większa, przybiera werbalną postać „kurwa, &lt;em&gt;znowu&lt;/em&gt;”). Ale nawet na takim poziomie ta gra jest wygrywalna. Nie znam żadnego kodu w rodzaju &lt;code&gt;IDDQD&lt;/code&gt;, który by znacznie ułatwiał zadanie, ale mogę się podzielić pewnymi zaobserwowanymi wzorcami.&lt;/p&gt;&lt;p&gt;„Nie wiem” bywa podświadomym skrótem myślowym na „nie chcę wiedzieć” albo „boję się wiedzieć”. Często jest tak, że pod spodem kryją się niezaspokojone potrzeby albo niespełnione pragnienia, z których nie zdajemy sobie sprawy. To właśnie w nich ma źródło wiele naszych działań, jeśli nie wszystkie. Jesteśmy znacznie mniej racjonalnymi zwierzętami, niż nam się wydaje. &lt;a href="http://chatolandia.pl/2015/04/14/o-falszywych-pragnieniach/"&gt;Chata Wuja Freda&lt;/a&gt; zilustrowała to lepiej, niż jestem w stanie wyrazić.&lt;/p&gt;&lt;img src="/img/blog/false2.png"&gt;
&lt;p&gt;Przekładając to na konkretny sposób myślenia: wróć jeszcze raz pamięcią do chwili sprzed popełnienia głupoty. Ale tym razem, zamiast na owej głupocie, skup się na sobie. Jak przebiegał Twój proces myślowy w tamtym momencie? Jak się czułeś? Czy to, jak się czułeś, miało wpływ na Twoje zachowanie? Jeśli tak, to co doprowadziło do tego, że czułeś się tak, a nie inaczej? Jakie były Twoje oczekiwania wobec świata? Jeżeli przypomnisz sobie te rzeczy, zrekonstruujesz sytuację, przeanalizujesz ją i przystawisz do poprzednich wystąpień problemu, to jest szansa, że zarysuje się jakaś hipoteza. To już jest coś, z czym możesz dalej pracować: obmyślać sposoby weryfikacji, planować i wprowadzać w życie zmiany.&lt;/p&gt;&lt;p&gt;Dodatkowym, kluczowym warunkiem powodzenia tego podejścia jest bycie zupełnie szczerym ze sobą. To samo w sobie jest bardzo trudne: jesteśmy ekspertami w oszukiwaniu samych siebie i robimy to tym częściej, im bardziej przyznanie się do czegoś przed samym sobą zmieniłoby naszą rzeczywistość. Jest to temat-rzeka, na który można by napisać nie tylko osobną notkę, ale całe tomy.&lt;/p&gt;&lt;p&gt;Pomaga też przegadanie sprawy z drugą osobą. Jeżeli masz tendencje do recydywy, to najprawdopodobniej za każdym razem ta sama osoba obrywa od Twojego zachowania, a w takim razie zapewne nie jest to ktoś dla Ciebie zupełnie obcy, zna Cię i może powiedzieć pewne rzeczy na temat Twojego zachowania. Znowu, trzeba podchodzić do tego z wyczuciem, taktem i we właściwym czasie, żeby nie pogarszać. Tym niemniej pytanie „słuchaj, to już któryś raz, a ja zupełnie nie wiem, co się ze mną dzieje, może masz jakiś pomysł?” jest zasadniczo bardzo pozytywnym sygnałem. Choćby dlatego, że pokazuje, że masz świadomość problemu i aktywnie starasz się coś na niego poradzić. A to już bardzo dużo.&lt;/p&gt;&lt;h2 id="feedback"&gt;Feedback&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Feedback&lt;/em&gt;, który można dostać z takiej rozmowy, może być niesamowicie pomocny. Spojrzenie na siebie z pozycji kogoś innego jest świeże i nieobciążone schematami, których koleiny mamy wydrążone w mózgu na tyle głęboko, że potrafią zupełnie przesłonić oczywistości. Więcej: można poznać oczekiwania drugiej strony. Jeżeli to się uda, to znika element niepokoju spowodowany niepewnością o to, czy podejmujemy właściwe akcje naprawcze.&lt;/p&gt;&lt;p&gt;Odwróćmy teraz kamerę. Gdyby to mnie czyjeś działanie wyrządziło krzywdę, to jak chciałbym rozmawiać z tą osobą? Jak pomóc komuś rozwiązać problem, a jednocześnie nie zmuszać się do nienaturalnie miłego zachowania, zamiatania własnych emocji pod dywan i udawania, że nic się nie stało?&lt;/p&gt;&lt;p&gt;Podpatrzyłem to u Johna Lee w „Facing the Fire” (książka jest bardzo, hm, taka sobie, ale ta konkretna wskazówka jest warta zapamiętania i stosowania): unikałbym wyrzutów. Zamiast tego wolałbym szczerze i bez ogródek przedstawić obiektywny obraz sytuacji, po czym opowiedzieć o &lt;em&gt;swoich własnych&lt;/em&gt; odczuciach. Schemat jest mniej więcej taki: „postąpiłeś tak a tak, co miało następujące skutki. Mnie jest z tym źle, bo czuję się zraniony / zagrożony / smutny / niepewnie w Twoim towarzystwie / odczuwam inne negatywne emocje”. I jeszcze, jeżeli podejrzewam, że druga osoba może zacząć wpędzać się w poczucie winy (w roli winowajcy znacznie pewniej się czuję, kiedy słyszę coś takiego): „nie mówię Ci tego po to, żebyś się gorzej poczuł, tylko żebyś miał świadomość, co było źle w Twoim zachowaniu i jak ja się z tym czuję”.&lt;/p&gt;&lt;p&gt;Takie postawienie sprawy ma tę zaletę, że przedstawia problem bez wysuwania oskarżeń i bez krytykowania kogokolwiek z jakiegokolwiek powodu. Jedyne emocje, które zostały przedstawione, to te odczuwane przez wypowiadającego, a więc siłą rzeczy opis ten jest prawdziwy. Nie ma „a ty zawsze jesteś taki”, nie ma podtekstów, nie ma niedopowiedzeń. To jest zdrowa podstawa, na której można budować rozwiązania zadowalające obie strony.&lt;/p&gt;&lt;h2 id="spacer-po-linie"&gt;Spacer po linie&lt;/h2&gt;&lt;p&gt;Wrócę jeszcze na chwilę do naprawiania fakapów. Można tu przyjąć najrozmaitsze strategie, w zależności od tego, co się zawaliło, ale jest jeszcze jedna nieoczywista rzecz: trzeba bardzo uważać z używaniem pewnego przychodzącego na myśl słowa. Tym słowem jest „przepraszam”.&lt;/p&gt;&lt;p&gt;Z przepraszaniem jest trochę jak ze spacerowaniem po linie: nadmierne przechylenie się w którąkolwiek stronę może spowodować utratę równowagi i negatywne konsekwencje. Zacznę może od mniej oczywistego przegięcia, czyli od nadużywania przeprosin.&lt;/p&gt;&lt;p&gt;„Przepraszam” to słowo-wytrych. Uchodzi za grzeczne i właściwe, więc istnieje pokusa, żeby go nadużywać. A im częściej się go używa, tym bardziej ono się wyciera, stając się bezwartościowym, nic nieznaczącym dźwiękiem. Osoba, która za często słyszy „przepraszam” pod swoim adresem, prawdopodobnie ma go po dziurki w uszach. Co gorsza, łatwo wpaść w mylne przeświadczenie, że powiedzenie „przepraszam” załatwia sprawę albo przynajmniej czyni pierwszy krok w tym kierunku. Tymczasem nie czyni. W najlepszym razie, w swojej niewytartej postaci, słowo to niesie mniej więcej taki przekaz: „uznaję się winnym zaistniałej sytuacji i nie czuję się dobrze z tym, że tak się stało; przykro mi i chciałbym, żebyś mi wybaczył”. Problem z tym jest dokładnie taki sam, jak poprzednio: egoistyczność. To się skupia na emocjach wypowiadającego, zamiast na rozwiązaniu problemu.&lt;/p&gt;&lt;p&gt;Tak więc pierwsza wersja niniejszej notki stwierdzała kategorycznie, że „nie należy przepraszać za to, że się coś spieprzyło”. I w ten sposób wpadłem w drugą skrajność. Bo zaskakująco wiele jednostek nie potrafi lub nie chce przyznawać się do błędu, w związku z czym taka umiejętność jest uważana za cenną. Jak w tym blablerowym &lt;a href="http://blabler.pl/s/Ceuq"&gt;wątku&lt;/a&gt;, który wpadł mi w oko zupełnym przypadkiem:&lt;/p&gt;&lt;img src="/img/blog/blab.png"&gt;
&lt;p&gt;Pokrzywdzonemu jest łatwiej, kiedy wie, że traktujesz sprawę serio; oszczędza to niepotrzebnej frustracji i rozgoryczenia. Wniosek jest, jak sądzę, taki, że przepraszanie jest ważne i potrzebne, ale trzeba mieć świadomość niebezpieczeństw, jakie się z nim wiążą, zachowywać je na sytuacje, w których obie strony czują się z nim lepiej, i stosować oszczędnie, aby uniknąć dewaluacji.&lt;/p&gt;&lt;p&gt;Jeśli masz nawyk irytującego przepraszania za każdą duperelę, to z moich obserwacji wynika, że znacznie łatwiejsze od wykorzenienia jest zastąpienie go innym, pożytecznym nawykiem. Otóż zamiast „przepraszam” warto nauczyć się mówić „dziękuję”; niekoniecznie w sytuacjach fakapowych, ale ogólnie w życiu. Wdzięczność można wyrażać za najdrobniejsze rzeczy i bardzo często, a mimo to jest to potężne i prawdziwe uczucie. Nie trzeba nawet adresować jej do konkretnej osoby – można emitować ją w nieokreślonym kierunku, rozciągając na cały świat, i też działa.&lt;/p&gt;&lt;h2 id="go-forth"&gt;Go forth&lt;/h2&gt;&lt;p&gt;I tu kończy się krótki kurs zawalania. Wszyscy zawalamy coś od czasu do czasu. Dobra wiadomość jest taka, że jeśli wszyscy żyją i nikt nie doznał trwałego uszczerbku na zdrowiu, to sytuacja prawdopodobnie jest naprawialna.&lt;/p&gt;&lt;p&gt;Powodzenia w naprawianiu!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2014-05-06:post:kod-gietki</id>
    <title>Kod giętki</title>
    <link href="http://plblog.danieljanus.pl/2014/05/06/kod-gietki/"/>
    <updated>2014-05-06T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Ludzie dziwnie na mnie patrzą, kiedy powiadam, że programowanie i poezja mają ze sobą wiele wspólnego. A jednak uważam, że tak właśnie jest, i to co najmniej na kilku poziomach.&lt;/p&gt;&lt;p&gt;Jaki jest cel poezji? Na ten temat napisano morze: parafrazując stary dowcip – gdzie dwóch poetów, tam co najmniej trzy odpowiedzi na to pytanie. Spróbuję jednak uogólnić: poezja ma za zadanie &lt;em&gt;wzbudzić&lt;/em&gt; w odbiorcy określone &lt;em&gt;emocje&lt;/em&gt; lub przemyślenia, używając do tego celu &lt;em&gt;słów&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Warto zwrócić uwagę na tę drugą część, oczywistą przecież, poprzedniego zdania. Język, słowa, to najważniejszy i jedyny środek, jaki poezja ma do dyspozycji do osiągnięcia swojego celu. Dobór słów, kolejność słów, ułożenie słów – wszystko to ma w poezji kolosalne i fundamentalne znaczenie. Pojedyncze słowa są na wagę złota. Kilka celnie dobranych słów potrafi wyindukować mocny i poruszający obraz; i odwrotnie – jedno niepasujące słowo może ów obraz zaprzepaścić.&lt;/p&gt;&lt;p&gt;Kojarzycie ten wyimek z Norwida? Pewnie, że kojarzycie, w końcu zdobił pracownię języka polskiego w co drugiej szkole.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Ponad wszystkie wasze uroki,&lt;br&gt; Ty! Poezjo, i Ty, Wymowo,&lt;br&gt; Jeden – wiecznie będzie wysoki:&lt;br&gt; Odpowiednie dać rzeczy – słowo!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Zastanówmy się nad tym chwilę. Norwid próbuje tu wyrazić właśnie to: owo właściwe poetom maksymalistyczne dążenie do przekazania myśli w sposób możliwie najbardziej oddziałujący na odbiorcę przy użyciu &lt;em&gt;odpowiednich słów&lt;/em&gt;. Tak i tylko tak.&lt;/p&gt;&lt;p&gt;Spójrzmy na kilka przykładów poszukiwania takich słów. Oto przykład zaczerpnięty z „Ocalonego w tłumaczeniu” Barańczaka, Gerard Manley Hopkins w liście do Roberta Bridgesa z 7 września 1888:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;By the bye you misquote the same modern author in writing ‘airy between towers’: what is not so? it should be ‘branchy between towers’.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Gwoli wyjaśnienia: „the same modern author” to sam Hopkins, a wiersz, o którym mowa, to „&lt;a href="http://www.bartleby.com/122/20.html"&gt;Duns Scotus's Oxford&lt;/a&gt;”, którego incipit adresat listu błędnie cytuje gdzie indziej. Poeta czepia się jednego jedynego słowa, uznając jego zamianę za niefortunną i istotnie osłabiającą przekazywany obraz, w  znamiennym proteście: „powietrzny pomiędzy wieżami – a cóż takie nie jest?” W domyśle: jeśli nad jakimś krajobrazem górują wieże, to jasne jest, że pomiędzy ich najwyższymi partiami jest dużo przestrzeni, nie ma sensu tego &lt;em&gt;explicite&lt;/em&gt; mówić w wierszu; ale Oksford z jego strzelistymi drzewami i rozłożystymi konarami jest w umyśle Hopkinsa bardzo charakterystyczny.&lt;/p&gt;&lt;p&gt;A teraz zacytuję fragment wiersza, o którym Theodore Roosevelt powiedział, że „nie jest pewien, czy go rozumie, ale z całą pewnością go lubi”: „&lt;a href="http://www.poetryfoundation.org/poem/175762"&gt;Luke Havergal&lt;/a&gt;” E. A. Robinsona. Wersy 4-5 w pierwszej redakcji tego wiersza, z roku 1896, brzmiały:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The wind will moan, the leaves will whisper some,— &lt;br&gt; Whisper of her, and strike you as they fall; &lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Obraz jest sugestywny: niemal czuje się przenikliwy wiatr i  słyszy szelest liści układający się w szept. Ale czytając ostateczny, późniejszy o ćwierć wieku wariant wiersza, widzimy, że Robinson ten fragment nieznacznie przerobił:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The leaves will whisper there of her, and some, &lt;br&gt; Like flying words, will strike you as they fall; &lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;W tej wersji wiatr obecny jest tylko w sposób dorozumiany: skoro mamy szept liści, to, jak uznał Robinson, wiadomo, że wieje i wspominanie o tym wprost jest redundantne; pominięte jest też powtórzenie „szeptu”, którego efekt wzmacniający przekaz był wątpliwy. Wyrzucając te dwie rzeczy, poeta robi miejsce na intrygującą i mocną metaforę „smagających słów w locie”, którą można interpretować na rozmaite sposoby i która interesująco współgra z dalszymi częściami wiersza („God slays Himself with every leaf that flies” w strofie drugiej).&lt;/p&gt;&lt;p&gt;Mieliśmy więc działający, mocny tekst; dzięki przeróbce i znalezieniu &lt;em&gt;odpowiedniego słowa&lt;/em&gt; działa on &lt;em&gt;lepiej&lt;/em&gt;, efektywniej. Punkt dla tych, którym przychodzi do głowy słowo „refactor”.&lt;/p&gt;&lt;p&gt;Zastanawialiście się, co by było, gdyby Mickiewicz używał gita? „Stepy akermańskie” były refaktorowane wielokrotnie w karkołomnych próbach uchwycenia balansu między wzburzonym oceanem a wysuszoną, jałową pustynią. Jesteśmy niemal w stanie zrekonstruować wynik działania &lt;code&gt;git log -p&lt;/code&gt; na tym hipotetycznym repozytorium.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;commit 90023f50e466ecf9339c1348f92bd855ccd58750
Author: Adam Mickiewicz &lt;adam@mickiewicz.lt&gt;
Date:   Wed Mar 12 19:42:12 1828 +0100

    pierwszy wers - wersja ostateczna

diff --git a/01-stepy-akermanskie.txt b/01-stepy-akermanskie.txt
index 019e416..0c741b8 100644
--- a/01-stepy-akermanskie.txt
+++ b/01-stepy-akermanskie.txt
@@ -1 +1 @@
-Okrążyły mię stepy na kształt oceanu
+Wpłynąłem na suchego przestwór oceanu

commit 1fee11618a8373ae8b8a435c6ff0c7009e3c4554
Author: Adam Mickiewicz &lt;adam@mickiewicz.lt&gt;
Date:   Fri Jun 2 17:15:26 1826 +0400

    poprawka do pierwszego wersu

diff --git a/01-stepy-akermanskie.txt b/01-stepy-akermanskie.txt
index 4b7d669..019e416 100644
--- a/01-stepy-akermanskie.txt
+++ b/01-stepy-akermanskie.txt
@@ -1 +1 @@
-Wjechałem na suchego stepy oceanu
+Okrążyły mię stepy na kształt oceanu

commit 416f8d15382b805def5e9e80928216bc86d56eec
Author: Adam Mickiewicz &lt;adam@mickiewicz.lt&gt;
Date:   Fri Jun 2 11:44:52 1826 +0400

    kolejna przeróbka pierwszego wersu

diff --git a/01-stepy-akermanskie.txt b/01-stepy-akermanskie.txt
index d3b53c2..4b7d669 100644
--- a/01-stepy-akermanskie.txt
+++ b/01-stepy-akermanskie.txt
@@ -1 +1 @@
-Żeglowałem i w suchych stepach oceanu
+Wjechałem na suchego stepy oceanu

commit c196b9483f501992ed9ce887b5ab4f1279c1b946
Author: Adam Mickiewicz &lt;adam@mickiewicz.lt&gt;
Date:   Thu Jun 1 18:03:31 1826 +0400

    przeróbka pierwszego wersu

diff --git a/01-stepy-akermanskie.txt b/01-stepy-akermanskie.txt
index d490bdf..d3b53c2 100644
--- a/01-stepy-akermanskie.txt
+++ b/01-stepy-akermanskie.txt
@@ -1 +1 @@
-Poznałem stepowego podróż oceanu
+Żeglowałem i w suchych stepach oceanu
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To, co próbuję pokazać na tych przykładach, to to, że dążenie do takiego użycia języka (polskiego, angielskiego, C, Javy...), żeby możliwie klarownie &lt;em&gt;wyrazić&lt;/em&gt; w nim to, o co chodzi, i żeby – cytując C.A.R. Hoare'a – rezultat miał „obviously no deficiencies”, jest wspólną cechą poetów i programistów.&lt;/p&gt;&lt;p&gt;Gdyby było inaczej, nie toczylibyśmy długich dyskusji na temat wyższości paradygmatu obiektowego nad funkcyjnym albo odwrotnie, nie wymyślalibyśmy takich rzeczy jak &lt;em&gt;dependency injection&lt;/em&gt; albo &lt;em&gt;fluent APIs&lt;/em&gt;, nie mielibyśmy tak bogatego katalogu wzorców projektowych, nie mielibyśmy indywidualnych upodobań stylistycznych, nie pisalibyśmy dokumentów o nazwie &lt;code&gt;CodingStyle&lt;/code&gt;, nie rozróżnialibyśmy pomiędzy kodem obleśnym i ładnym, wreszcie – widząc ten pierwszy – nie mielibyśmy odruchu, żeby go natychmiast przepisać.&lt;/p&gt;&lt;p&gt;„Chodzi mi o to, aby język giętki powiedział wszystko, co pomyśli głowa”, pisał Słowacki. Kiedy o tym myślę, przychodzi mi do głowy Lisp i często powtarzane zdanie, że to programowalny język programowania. Homoikoniczność i makra sprawiają, że pisząc program w Lispie, tworzymy nie tylko kod odpowiedzialny za rozwiązanie naszego problemu w z góry istniejącym języku, ale również rozwijamy i naginamy język, używając neologizmów: nowych, powstałych &lt;em&gt;ad hoc&lt;/em&gt; konstrukcji składniowych, o ile tylko pozwalają naturalniej, klarowniej wyrazić sens danego fragmentu kodu. Programowanie w Lispie jest jak bycie Leśmianem.&lt;/p&gt;&lt;p&gt;Czasem efekt estetyczny pojawia się w kodzie niejako mimochodem. Przypomina mi się wywiad, jakiego Guy Steele udzielił Peterowi Seibelowi w jego świetnej książce „Coders at Work” (polecam!) Steele wspomina tam o jednym z najbardziej satysfakcjonujących momentów w jego karierze programisty, kiedy po dwudziestu latach udało mu się skrócić napisaną przez Billa Gospera jedenastosłowową procedurę obliczającą sinusy i cosinusy liczb rzeczywistych o jedno słowo maszynowe. Osiągnąć, wydawałoby się, nieosiągalne, dzięki właściwej poezji zwięzłości i nadorganizacji języka: oto czysta radość programowania.&lt;/p&gt;&lt;p&gt;Warto się rozejrzeć po GitHubie. Poezja tam się czai.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2013-05-22:post:bandsmen</id>
    <title>Szaleniec ze zdalnym detonatorem</title>
    <link href="http://plblog.danieljanus.pl/2013/05/22/bandsmen/"/>
    <updated>2013-05-22T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Dzisiejszy &lt;em&gt;Evening Standard&lt;/em&gt; przywitał mnie wielkim tytułem na okładce:&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;a href="http://www.standard.co.uk/news/crime/ira-hyde-park-bombing-john-anthony-downey-61-is-charged-with-murders-of-four-soldiers-in-1982-8626847.html"&gt;MAN IS CHARGED WITH 1982 HYDE PARK BOMBINGS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;W lipcu 1982 roku w londyńskich parkach Hyde Park i Regent's Park odbywały się uroczystości wojskowe. Doszło do dwóch eksplozji, wywołanych, jak później ustalono, wybuchami zdalnie zdetonowanych bomb domowej roboty. W Hyde Parku zginęło czterech kawalerzystów z regimentu Blues &amp; Royals, a w Regents Parku bomba umieszczona pod sceną zabiła siedmiu muzyków wojskowych z Royal Green Jackets. Osoba podejrzana o zdetonowanie pierwszej bomby została właśnie zatrzymana na lotnisku Gatwick.&lt;/p&gt;&lt;p&gt;Gdyby chodziło o inny incydent z powojennej historii Wielkiej Brytanii, prawdopodobnie niewiele by mi to powiedziało. Bardzo mało wiem o północnoirlandzkich dążeniach zjednoczeniowych, a prawie nic o IRA – i na pewno nie byłbym w stanie wymienić innego zamachu. Tymczasem o tym słyszałem wcześniej. Skąd?&lt;/p&gt;&lt;p&gt;Jakiś czas temu próbowałem zmierzyć się z tłumaczeniem tekstu utworu „The Gunner’s Dream” Pink Floyd, o którym już tu kiedyś &lt;a href="http://plblog.danieljanus.pl/blog/2011/02/28/sen-elektryka/"&gt;pisałem&lt;/a&gt;. Zmusiło mnie to do dokładnego przeczytania tekstu i zastanowienia się nad znaczeniem tych fragmentów, nad którymi do tej pory się prześlizgiwałem. Zacząłem więc drążyć i odkrywać liczne Watersowe odniesienia literackie, kulturowe i historyczne.&lt;/p&gt;&lt;iframe width="100%" height="500" src="https://www.youtube.com/embed/g9mhU1mm5P0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;W ten sposób dowiedziałem się, że końcówka frazy „...space between the heavens and the corner of some foreign field” to nawiązanie do sonetu &lt;em&gt;&lt;a href="http://www.famousliteraryworks.com/brooke_the_soldier.htm"&gt;The Soldier&lt;/a&gt;&lt;/em&gt; Ruperta Brooke’a, poety, którego można nazwać „angielskim Baczyńskim” ze względu na jego śmierć w I wojnie światowej w niecały rok po napisaniu tego wiersza, który w ten sposób stał się czymś w rodzaju samospełniającej się przepowiedni. Motyw pól pierwszowojennych powraca zresztą na tej płycie kilkakrotnie, jak w „Southampton Dock”, gdzie „mute reminder of the poppy fields and graves” przywodzi na myśl sztuczne czerwone maki, jakie widzi się na londyńskich ulicach wpięte w klapy marynarek w Remembrance Day.&lt;/p&gt;&lt;p&gt;Tak dowiedziałem się też, o co chodzi w owym fragmencie mówiącym dosłownie o „szaleńcach, którzy za pomocą zdalnie sterowanych urządzeń przedziurawiają muzyków”, o którym przypomniał mi dzisiejszy artykuł.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;you can relax on both sides of the tracks, &lt;br&gt; and maniacs don't blow holes in bandsmen by remote control&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;To byłoby bardzo trudne do dosłownego oddania po polsku. Z jednej strony mamy tu efekt akustyczny przywodzący na myśl eksplozję, wywołany przez instrumentację głoskową w „dOn't blOw hOles iN BandsMeN By”, a z drugiej – niedostatek ekwiwalentów: „bandsman” to przede wszystkim „muzyk wojskowy”; nie ma też zwięzłego, jednosłowowego odpowiednika „remote control” innego niż „pilot”, ale jego użycie wywołałoby niepożądane skojarzenie ze sprzętem RTV.&lt;/p&gt;&lt;p&gt;Ze względu na te trudności, a także mając świadomość niezrozumiałości tego odwołania sprzed trzydziestu lat dla nie-Brytyjczyka, zdecydowałem się na zastąpienie w mojej wersji zamachu w Regents Parku innym zamachem, którego echa wciąż jeszcze brzmią silnie na całym świecie: „...gdzie spokojnie trwać możesz w wierze, że / w wieże dwie samolot żaden nie wbije się...”&lt;/p&gt;&lt;p&gt;Oto efekt próby (oryginalny tekst do znalezienia na przykład &lt;a href="http://www.azlyrics.com/lyrics/pinkfloyd/gunnersdream.html"&gt;tutaj&lt;/a&gt;) -- jestem boleśnie świadom jego niedostatków i niedorastania do pięt oryginałowi, ale skoro, jak wieść niesie, istnieją wersje &lt;a href="http://www.redcoon.pl/Z35504301184KS-In-Rock-Roger-Waters-PINK-FLOYD-Antologia-tekst%C3%B3w-i-przek%C5%82ad%C3%B3w-1968-2002_Sztuka"&gt;jeszcze gorsze&lt;/a&gt;, myślę, że warto próbować.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;SEN KANONIERA&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;wspomnień kłąb&lt;br/&gt; przez mgłę chmur&lt;br/&gt; strumieniem spływa na spotkanie ze mną w dół —&lt;br/&gt; lecz gdzieś w przestrzeni między niebem a małym skrawkiem obcych ziem —&lt;br/&gt; tam miałem sen,&lt;br/&gt; tak, miałem sen.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;żegnaj, Max,&lt;br/&gt; Mamo, pa...&lt;br/&gt; po nabożeństwie, w chłodzie listopadowego dnia,&lt;br/&gt; do samochodu kroczysz z nią, a pasemka jej włosów srebrzy szron,&lt;br/&gt; słyszysz bijący dzwon, na przypasaną zerkasz broń,&lt;br/&gt; i gdy spływają łzy na przepaskę, co jej zdobi skroń,&lt;br/&gt; ujmujesz jej dłoń i chwytasz za ten sen.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;miejsce, gdzie jest&lt;br/&gt; co jeść, gdzie spać,&lt;br/&gt; gdzie stary wiarus może na ulicy stać,&lt;br/&gt; gdzie możesz głośno mówić o wątpliwościach i obawach,&lt;br/&gt; i gdzie nie znika nikt, i żaden ciężki but wojskowy nie kopie w twoje drzwi,&lt;br/&gt; i gdzie spokojnie trwać możesz w wierze,&lt;br/&gt; że w wieże dwie samolot żaden nie wbije się,&lt;br/&gt; i gdzie nie trzeba martwić się o wikt,&lt;br/&gt; i nikt już nie zabija dzieci, nikt,&lt;br/&gt; nikt już nie zabija dzieci, nikt.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;noc w noc i wciąż, krążąc po umysłu dnie,&lt;br/&gt; ten sen w szaleństwo wpędza mnie________.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;na małym skrawku obcej ziemi kanonier dzisiaj śpi,&lt;br/&gt; już; stało się&lt;br/&gt; i nie da się wymazać ostatnich scen —&lt;br/&gt; pamiętaj ten sen,&lt;br/&gt; pamiętaj.&lt;/p&gt;&lt;/p&gt;
&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2013-05-17:post:homofobiczny-brak-umiejetnosci</id>
    <title>Homofobiczny brak umiejętności czytania ze zrozumieniem</title>
    <link href="http://plblog.danieljanus.pl/2013/05/17/homofobiczny-brak-umiejetnosci/"/>
    <updated>2013-05-17T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Takie coś mnie zaatakowało na Facebooku:&lt;/p&gt;&lt;img src="/img/blog/homofobia.jpg"&gt;
&lt;p&gt;W skrócie: autor grafiki zestawia dane o pobiciach na tle homofobii z danymi o liczbie zarejestrowanych w Polsce pobić i odsetku osób homoseksualnych wśród całej populacji i wychodzi mu, że coś się nie zgadza, ergo &lt;em&gt;geje ściemniają&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Czy te wyliczenia są słuszne?&lt;/p&gt;&lt;p&gt;Byłyby, gdyby liczby przytoczone przez autora znaczyłyby właśnie to, co autorowi wydaje się, że znaczą. W szczególności, jeśli dobrze rozumiem, autor zakłada, że owe 17,7% jest odsetkiem osób homoseksualnych w społeczeństwie, które zostały pobite ze względu na swoją orientację; innymi słowy – że to estymacja stosunku liczby homoseksualnych ofiar pobić do liczby wszystkich homoseksualistów w społeczeństwie.&lt;/p&gt;&lt;p&gt;Byłoby tak, gdyby owa próbka 423 osób była losową, reprezentatywną próbą populacji homoseksualistów, niezależnie od wartości zmiennej „bycie ofiarą przemocy”. Tymczasem sięgając do źródła, czyli &lt;a href="http://www.kph.org.pl/publikacje/raport_przemoc.pdf"&gt;raportu KPH&lt;/a&gt; w części dotyczącej metodologii badania czytamy:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;W związku z powyższym zasadne stało się zdefiniować grupę respondentów, &lt;em&gt;zawężając ją do osób, które spotkały się z przemocą motywowaną homofobią&lt;/em&gt;, ale nie ograniczać jej jedynie do osób nieheteroseksualnych.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;(Podkreślenie moje).&lt;/p&gt;&lt;p&gt;Innymi słowy, ten raport jest zupełnie o czymś innym. To nie są statystyki mówiące o tym, &lt;em&gt;jak często&lt;/em&gt; ludzie (niekoniecznie homoseksualiści!) padają ofiarą przemocy na tle homofobicznym. Te liczby mają za zadanie oddać &lt;em&gt;charakter&lt;/em&gt; owej przemocy i dana, na którą powołuje się autor grafiki, oznacza w istocie, że &lt;em&gt;co piąty akt przemocy&lt;/em&gt; w badanej próbie miał charakter pobicia.&lt;/p&gt;&lt;p&gt;W ten sposób wykorzystanie źle zinterpretowanych danych doprowadziło do wniosków, które można określić jako, ekhm, OKDP (O Krztynę za Daleko Posunięte).&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Zatrzymajmy się na chwilę. Zastanówmy się, jak łatwo było takiego błędu uniknąć: wystarczyło przeczytać ze zrozumieniem cały tekst, a nie tylko wyłapywać fragmenty pasujące do z góry założonej tezy. Internet jednak rozleniwia. Łatwo jest czytać pobieżnie, a jeszcze łatwiej bezkrytycznie, bez weryfikacji, repostować na fejsie obrazki pasujące do naszej linii światopoglądowej, dając wiarę, że ktoś inny wykonał za nas czarną robotę krytyka.&lt;/p&gt;&lt;p&gt;Brzmi znajomo?&lt;/p&gt;&lt;p&gt;To nie jest grzech tylko korwinistów. Ja sam go popełniam znacznie częściej, niżbym chciał, widywałem podobne błędy w wykonaniu wielu naprawdę inteligentnych ludzi, i zdarza się po obu stronach dowolnej światopoglądowej barykady: uciekamy od krytycznego oceniania własnych poglądów i konfrontowania ich z danymi.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Tymczasem oto jakie wnioski, nie sprawdziwszy źródeł, wyciąga osoba, która udostępniła tę grafikę w moim strumieniu:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Co za klapki na oczach :D&lt;/p&gt;&lt;p&gt;Przecież pierwszy wniosek, jaki powinien się narzucić po przeczytaniu tych liczb, jest taki, że szacunek mówiący o 5-7%-owej reprezentacji homoseksualistów w społeczeństwie jest jakieś dziesięć razy zawyżony. Tym bardziej, że pochodzi z najmniej wiarygodnego źródła: z tego, co sami mówią o sobie.&lt;/p&gt;&lt;p&gt;Tja... A wiecie, jaki odsetek Polaków stanowią żarliwi katolicy? Nie wiecie? To spytajcie kilku żarliwych katolików. I niech Wam nie przyjdzie do głowy kwestionować ich odpowiedzi rzędu 95%, bo przecież pochodzi prosto ze środowiska :D&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Widzicie subtelną ekwiwokację?&lt;/p&gt;&lt;p&gt;Wyrażenie „to, co sami mówią o sobie” można rozumieć na dwa sposoby:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;to, co &lt;em&gt;każdy z nich&lt;/em&gt; (tj. homoseksualistów) mówi &lt;em&gt;o sobie samym&lt;/em&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;to, co &lt;em&gt;każdy z nich&lt;/em&gt; mówi &lt;em&gt;o nich jako całości&lt;/em&gt;.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Nie sposób wyobrazić sobie badań naukowych opartych na pytaniu sformułowanym: „Jak wiele Pana/Pani zdaniem jest w Polsce osób homoseksualnych?” zamiast „Czy uważa Pan/Pani siebie za osobę homoseksualną?” Gdyby ktoś miał wątpliwości, znowu: łatwo sprawdzić metodologię &lt;a href="http://www.esri.ie/UserFiles/publications/20061016131112/BKMNEXT084_Main%20Report.pdf"&gt;pierwszego z brzegu badania&lt;/a&gt;. Tymczasem to właśnie sugeruje komentator w prześmiewczym trzecim akapicie swojej wypowiedzi.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Klapki na oczach&lt;/em&gt;, indeed.&lt;/p&gt;&lt;p&gt;Smutno mi. Niezależnie od wszystkiego uważam, że zacietrzewienie, zadufanie, zapatrzenie w swój jedynie słuszny punkt widzenia i chęć udowodnienia go światu za wszelką cenę – nigdy nie sprzyjają konstruktywnej dyskusji.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2013-03-22:post:zbiorkom</id>
    <title>Porównanie prostych taryf zbiorkomu w Warszawie i Londynie</title>
    <link href="http://plblog.danieljanus.pl/2013/03/22/zbiorkom/"/>
    <updated>2013-03-22T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;h2 id="ile-kosztuje-przejazd-komunikacją-miejską-z-punktu-a-do-punktu-b-w-warszawie?"&gt;Ile kosztuje przejazd komunikacją miejską z punktu A do punktu B w Warszawie?&lt;/h2&gt;&lt;p&gt;To bardzo proste.&lt;/p&gt;&lt;p&gt;Obszar obsługiwany przez warszawską komunikację miejską podzielony jest na dwie strefy, z których pierwsza odpowiada z grubsza granicom administracyjnym miasta, a druga podwarszawskim miejscowościom. Bilet jednorazowy ważny w jednej strefie kosztuje 4,40 zł, a w obu 7 zł.&lt;/p&gt;&lt;p&gt;Taki bilet ważny jest tylko w pojeździe, w którym został skasowany, więc jeśli podróż obejmuje przesiadki, to bardziej opłaca się skasować bilet czasowy: 20-minutowy kosztuje 3,40 zł, 40-minutowy – 4,60 zł, a 60-minutowy – 6,40 zł. Wszystkie bilety czasowe są ważne w obu strefach.&lt;/p&gt;&lt;h2 id="ile-kosztuje-przejazd-komunikacją-miejską-z-punktu-a-do-punktu-b-w-londynie?"&gt;Ile kosztuje przejazd komunikacją miejską z punktu A do punktu B w Londynie?&lt;/h2&gt;&lt;p&gt;To bardzo proste.&lt;/p&gt;&lt;p&gt;&lt;em&gt;(głęboki wdech)&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Zaczniemy może od autobusów i tramwajów, bo tu sprawa jest &lt;em&gt;prawie&lt;/em&gt; tak prosta jak w Warszawie. W całym mieście obowiązuje jedna taryfa za pojedynczy przejazd (no dobrze, dwie). Tak jak w Warszawie, bilet jest ważny tylko w pojedynczym pojeździe.&lt;/p&gt;&lt;p&gt;Jeśli płacimy za przejazd gotówką (w autobusie u kierowcy lub w automatach przystankowych, których jest coraz mniej), to bilet kosztuje £2.40. Taniej jest płacić specjalną zbliżeniową kartą przedpłaconą, zwaną &lt;em&gt;Oyster card&lt;/em&gt;. Opłata wynosi wtedy £1.40, a płatność odbywa się poprzez przyłożenie karty do żółtego czytnika-kasownika umieszczonego przy kabinie kierowcy autobusu lub na przystankach tramwajowych.&lt;/p&gt;&lt;p&gt;Londyn nie ma odpowiednika warszawskich biletów czasowych. Zamiast tego istnieje dzienna granica opłat, która dla autobusów i tramwajów wynosi £4.40. Oznacza to, że jeżeli poruszamy się tylko autobusami i tramwajami, to czwarty przejazd danego dnia kosztuje tylko 20 pensów, a wszystkie następne są bezpłatne.&lt;/p&gt;&lt;p&gt;Jeśli jedziemy metrem lub pociągiem (podmiejskim, DLR lub Overground), to obowiązują inne zasady.&lt;/p&gt;&lt;p&gt;Obszar obsługiwany przez te środki transportu podzielony jest na &lt;del&gt;sześć&lt;/del&gt; &lt;del&gt;dziewięć&lt;/del&gt; &lt;del&gt;trzynaście&lt;/del&gt; &lt;strong&gt;szesnaście&lt;/strong&gt; stref taryfowych. Strefy od 1 do 6 odpowiadają z grubsza obszarowi Londynu (1 to centralny Londyn), choć część stacji w skrajnych strefach leży już poza granicami miasta. Strefy od 7 do 9 to stacje w podlondyńskich miejscowościach obsługiwane przez Overground i linię metra Metropolitan. Pozostałe strefy są nienumerowane, czasami (nieoficjalnie) oznaczane literami, a leżące w nich stacje są obsługiwane tylko przez kolej podmiejską, z wyjątkiem Watford Junction, do której dojeżdżają również pociągi London Overground.&lt;/p&gt;&lt;p&gt;Dla każdej pary stref od 1 do 9 oraz Watford Junction (stacja leżąca w swojej własnej strefie) obowiązują &lt;a href="http://www.tfl.gov.uk/tickets/14416.aspx"&gt;osobne opłaty&lt;/a&gt;.  Tak jak przy autobusach, cena zależy od tego, czy płacimy gotówką (dużo drożej), czy za pomocą Oyster. W tym drugim przypadku obowiązują różne ceny w szczycie i poza szczytem.  Szczyt zdefiniowany jest jako godziny od 6:30 do 9:30 i od 16:00 do 19:00 w dni robocze, przy czym decyduje godzina rozpoczęcia podróży.&lt;/p&gt;&lt;p&gt;Z Euston (leżącego nominalnie w strefie pierwszej) do pozostałych stref obowiązują osobne (niższe) ceny w szczycie, a do strefy 8 i Watford Junction również w godzinach pozaszczytowych. Wszystkie te kombinacje razem dają około 150 możliwych cen biletów, zależnie od sytuacji.&lt;/p&gt;&lt;p&gt;Z pociągami jest jeszcze trochę inaczej. Można kupić na nie bilety bezpośrednio u operatorów (co, jak łatwo zgadnąć, wychodzi drożej), natomiast cena biletu przy płatności kartą Oyster zależy od tego, czy którakolwiek część trasy jest oznaczona na czerwono na &lt;a href="http://www.tfl.gov.uk/assets/downloads/tickets/national-rail-map.pdf"&gt;tej mapce&lt;/a&gt; – obejmuje to w szczególności wszystkie pozostałe strefy nienumerowane. Jeśli nie (co dotyczy większości tras kolejowych na północ od Tamizy), to ceny przejazdu są takie jak opisane powyżej dla metra, DLR i Overground. Jeśli tak, to mają zastosowanie &lt;a href="http://www.tfl.gov.uk/tickets/14414.aspx"&gt;osobne taryfy&lt;/a&gt;, inne w zależności od tego, czy nasza trasa obejmuje wyłącznie odcinki kolei zaznaczone na wspomnianej mapce na czerwono, czy również odcinki zielone lub metro.&lt;/p&gt;&lt;p&gt;Tak jak w przypadku autobusów, istnieje dzienna granica opłat, odpowiadająca z grubsza cenie biletu dobowego. Jej wysokość zależy od najdalszej strefy, do której dojeżdżamy. Dla każdej ze stref zdefiniowane są dwie granice, szczytowa i pozaszczytowa, przy czym szczyt jest w tym wypadku zdefiniowany inaczej: jako wszystkie podróże rozpoczęte przed 9:30. Daje to pięć możliwości w dni robocze:&lt;/p&gt;&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Godzina rozpoczęcia&lt;/th&gt;&lt;th&gt;Cena biletu&lt;/th&gt;&lt;th&gt;Liczy się do limitu&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="center"&gt;04:30 — 06:30&lt;/td&gt;&lt;td&gt;Pozaszczytowa&lt;/td&gt;&lt;td&gt;Szczytowego&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="center"&gt;06:30 — 09:30&lt;/td&gt;&lt;td&gt;Szczytowa&lt;/td&gt;&lt;td&gt;Szczytowego&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="center"&gt;09:30 — 16:00&lt;/td&gt;&lt;td&gt;Pozaszczytowa&lt;/td&gt;&lt;td&gt;Pozaszczytowego&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="center"&gt;16:00 — 19:00&lt;/td&gt;&lt;td&gt;Szczytowa&lt;/td&gt;&lt;td&gt;Pozaszczytowego&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="center"&gt;19:00 — 04:30&lt;/td&gt;&lt;td&gt;Pozaszczytowa&lt;/td&gt;&lt;td&gt;Pozaszczytowego&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Jesteście jeszcze ze mną?&lt;/p&gt;&lt;p&gt;Bilet musi być ważny na wszystkie strefy, przez które przejeżdżamy. Nasuwa się pytanie: skąd System wie, którędy pojechaliśmy? Na przykład z Canada Water (strefa 2) do Willesden Junction (też strefa 2) możliwe są dwa warianty podróży:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;szybszy – metrem linii Jubilee do West Hampstead i dalej pociągiem Overground (przejeżdżający przez strefy 1 i 2);&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;wolniejszy – metrem linii Jubilee do Stratford, po czym pociągami Overground przez Gospel Oak (przejeżdżający przez strefy 2 i 3).&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Przy czym z tabeli opłat widać, że drugi wariant jest tańszy (£1.60 w szczycie wobec £2.80 w wariancie pierwszym).&lt;/p&gt;&lt;p&gt;Otóż System zakłada zawsze, że pojechaliśmy najszybszą drogą (czyli w danym wypadku naliczyłby opłatę według wariantu pierwszego). Aby wybrać wariant drugi, należy &lt;em&gt;explicite&lt;/em&gt; zaznaczyć naszą trasę. W tym celu na kilkudziesięciu kluczowych stacjach przesiadkowych zainstalowane są specjalne czytniki Oyster, które wyglądają jak zwykłe kasowniki, ale są różowe, a nie żółte. Przekliknięcie się kartą Oyster przez różowy kasownik mówi Systemowi, żeby na końcu naliczył niższą cenę. Istnieją jednak trasy, gdzie System zawsze nalicza cenę przejazdu jak przez strefę pierwszą mimo istnienia tras alternatywnych omijających tę strefę. Ich lista nie jest jawna.&lt;/p&gt;&lt;p&gt;Co do zasady pojedyncza podróż zdefiniowana jest jako czas „od wkliknięcia do wykliknięcia” (inaczej niż w Warszawie, w Londynie trzeba się wkliknąć i wykliknąć kartą Oyster na początku i na końcu każdej podróży; trzeba o tym pamiętać, bo zapomnienie o wykliknięciu skutkuje naliczeniem opłaty jak za najdroższy możliwy przejazd ze stacji źródłowej, przy czym opłata ta nie liczy się do limitu dobowego -- można to wyprostować, ale trzeba zadzwonić na infolinię Oyster). Jednak na niektórych stacjach, jak np. London Bridge, metro i kolej są od siebie oddzielone osobnymi zestawami bramek. Zdefiniowana jest więc lista par stacji, nazywanych OSI (Out of Station Interchange), gdzie wykliknięcie się z jednej stacji i bezpośrednio potem wkliknięcie w określonym terminie do drugiej traktowane jest jako kontynuowanie podróży, a nie rozpoczęcie nowej. Lista takich par również oficjalnie nie jest jawna, choć niektóre nieoficjalne strony poświęcone londyńskiej komunikacji miejskiej publikują jej &lt;a href="http://www.oyster-rail.org.uk/out-of-station-interchange-osi/"&gt;przybliżenie&lt;/a&gt;. Lista ta jest zmienna i często podczas np. weekendowych robót, gdzie część linii jest czasowo wyłączona, dodawane są do niej na czas prac nowe stacje, również niejawnie.&lt;/p&gt;&lt;p&gt;Tak jak w Warszawie, zdefiniowany jest maksymalny czas pojedynczej podróży. Inaczej jednak niż w Warszawie (gdzie wynosi on po prostu dwie godziny), londyński maksymalny czas uzależniony jest od stref, przez które przejeżdżamy, i od dnia i godziny rozpoczęcia podróży. Na stronie TfL dostępna jest &lt;a href="http://www.tfl.gov.uk/tickets/14872.aspx"&gt;odpowiednia tabela&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Obowiązują również specjalne reguły dotyczące wkliknięcia się i wkrótce potem wykliknięcia na &lt;em&gt;tej samej&lt;/em&gt; stacji. To, co się wtedy dzieje, zależy od czasu przebywania na stacji. Jeśli ten czas jest krótszy niż 2 minuty, to naliczana jest maksymalna opłata z danej stacji (ale jeśli w ciągu 45 minut od tego wykliknięcia nastąpi kolejne wkliknięcie, niekoniecznie na tej samej stacji, to zostaje ona anulowana). Jeśli od 2 minut do 30 minut, to naliczana jest minimalna opłata. Natomiast powyżej 30 minut naliczana jest maksymalna opłata w podwójnej wysokości.&lt;/p&gt;&lt;p&gt;Kartą Oyster można płacić również za przejazd niektórymi innymi londyńskimi środkami komunikacji publicznej (jest akceptowana na części promów i w kolejce powietrznej Emirates Air Line) i obowiązują wtedy specjalne opłaty, na ogół drogie.&lt;/p&gt;&lt;h2 id="to-ile-w-końcu-zapłacę?"&gt;To ile w końcu zapłacę?&lt;/h2&gt;&lt;p&gt;A czy to ważne? Zaufaj Systemowi.&lt;/p&gt;&lt;h2 id="ale-ja-chcę-wiedzieć-wcześniej!"&gt;Ale ja chcę wiedzieć wcześniej!&lt;/h2&gt;&lt;p&gt;&lt;a href="http://www.tfl.gov.uk/tfl/tickets/faresandtickets/farefinder/current/"&gt;Sprawdź w Systemie&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;
&lt;p&gt;Mam nadzieję, że to porównanie prostych zasad obowiązujących w dwóch stolicach okaże się dla kogoś przydatne. W trosce o własne zdrowie psychiczne nie przewiduję uaktualniania go ani uzupełniania o informacje dotyczące zniżek, biletów miesięcznych itd.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2013-02-01:post:tlumaczenia-technologiczne</id>
    <title>O książkach technologicznych i ich tłumaczeniach</title>
    <link href="http://plblog.danieljanus.pl/2013/02/01/tlumaczenia-technologiczne/"/>
    <updated>2013-02-01T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Trudna rzecz jest jeden język drugim językiem dostatecznie wyrznąć.&lt;/em&gt;&lt;/p&gt;&lt;p style="text-align: right;"&gt;-- Jan Leopolita (1523--1572)&lt;/p&gt;
&lt;/blockquote&gt;&lt;h2 id="rodzaje-książek-informatycznych"&gt;Rodzaje książek informatycznych&lt;/h2&gt;&lt;p&gt;Mam zasadę dotyczącą książek informatycznych: nie kupuję niczego, co ma w tytule nazwę konkretnej technologii. (Purystów z góry przepraszam za użycie tego słowa, mając świadomość jego &lt;a href="https://groups.google.com/d/msg/pl.comp.programming/UH4hrQWBCPo/ILTtM_GEfO8J"&gt;pretensjonalności&lt;/a&gt;; ale nie znam innego polskiego określenia, które by zbiorczo obejmowało konkretne języki programowania, systemy operacyjne, platformy wykonawcze, serwery aplikacji, architektury itd.) Po pierwsze, technologie pojawiają się i znikają, wciąż ukazują się ich nowe wersje, i w efekcie czas życia książki opisującej, dajmy na to, konkretną implementację jakiegoś języka programowania na ogół bywa nie dłuższy niż kilka lat. Można wręcz mówić o „okresie półtrwania”, po którym połowa informacji zawartej w książce jest już nieaktualna.  Po drugie, i może ważniejsze, tego typu publikacje często wnoszą niewielką wartość dodaną w porównaniu do dokumentacji: od książki oczekuję, że nie tylko przekaże mi suche instrukcje, ale także idee, które w jakiś sposób wzbogacą mój warsztat i zostaną ze mną na dłużej. Dlatego do poduszki wolę poczytać o &lt;a href="http://www.amazon.co.uk/Kernel-Methods-Pattern-Analysis-Shawe-Taylor/dp/0521813972"&gt;uczeniu maszynowym za pomocą metod jądrowych&lt;/a&gt; niż o &lt;a href="http://www.amazon.co.uk/Pro-IBM-WebSphere-Application-Professionals/dp/1430219580"&gt;bebechach serwera aplikacyjnego&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Rzecz jasna, jak od każdej zasady, tak i od tej istnieją wyjątki. Bywa, że technologia na tyle wryła się w informatyczny krajobraz, że nie grozi jej szybkie przeterminowanie, a dana książka uznawana jest za kanoniczny opis technologii, nie zaś tylko dodatek &lt;em&gt;post hoc&lt;/em&gt; (jak &lt;a href="http://pl.wikipedia.org/wiki/J%C4%99zyk_ANSI_C"&gt;K&amp;R&lt;/a&gt; do C czy &lt;a href="http://en.wikipedia.org/wiki/Programming_Perl"&gt;Księga Wielbłądzia&lt;/a&gt; do Perla). Zdarza się też i tak, że książka opisuje język czy środowisko, którego akurat intensywnie używam lub przymierzam się do intensywnego używania, i wtedy mam szansę odnieść z niej dużą korzyść. Tak było z &lt;em&gt;&lt;a href="http://pragprog.com/book/shcloj/programming-clojure"&gt;Programming Clojure&lt;/a&gt;&lt;/em&gt; Stuarta Hallowaya, której pierwsze wydanie kupiłem niedługo po jego ukazaniu się z górą trzy lata temu, a drugie, zmienione i uaktualnione (a jakże!) wkrótce &lt;a href="http://helion.pl/ksiazki/programowanie-w-jezyku-clojure-stuart-halloway-aaron-bedra,proclo.htm"&gt;ukaże się po polsku&lt;/a&gt; nakładem &lt;a href="http://helion.pl/"&gt;Helionu&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Po przeczytaniu darmowej próbki nasunęły mi się wątpliwości odnośnie samego tłumaczenia, a w ślad za nimi myśli dotyczące polskich tłumaczeń literatury informatycznej w ogóle. Spróbuję je poniżej zebrać.&lt;/p&gt;&lt;h2 id="o-clojure-po-polsku"&gt;O Clojure po polsku&lt;/h2&gt;&lt;p&gt;Od razu powiem, że ogólny poziom przekładu Tomasza Walczaka uważam za wysoki: tekst jest spójny, nośny i pozbawiony błędów merytorycznych. Czepianie się szczegółów, które za chwilę nastąpi, ma być nie tyle krytyką, co raczej próbą uświadomienia sobie, dlaczego mimo wszystko „Programowanie w języku Clojure” czyta mi się mniej płynnie niż oryginał angielski; a także zwrócenia uwagi na problemy, z jakimi musi się zmierzyć tłumacz. Zasygnalizuję więc pewne zagadnienia, ale wcale nie mam pewności, czy potrafiłbym rozwiązać je lepiej, albo nawet – czy w ogóle się da.&lt;/p&gt;&lt;p&gt;Bo wydawałoby się, że tłumaczenie tekstu informatycznego jest czynnością bez porównania prostszą niż na przykład poezji, gdzie trzeba ważyć każde słowo, prawda? Tymczasem rodzi ono zupełnie nowe problemy. Tłumacz zmuszony jest często wymyślić od nowa terminologię z zakresu wąskiej dziedziny, o której po polsku się nie pisze lub pisze się rzadko lub nieformalnie; podjąć pewne nierzadko arbitralne decyzje i konsekwentnie się ich trzymać; albo – jak zobaczymy – zadecydować, co zrobić z subtelnymi różnicami odcieni znaczeniowych pewnych pojęć.&lt;/p&gt;&lt;h3 id="lispa-czy-lispu?"&gt;Lispa czy Lispu?&lt;/h3&gt;&lt;p&gt;Tłumacz często używa formy &lt;em&gt;Lispa&lt;/em&gt; jako dopełniacza od rzeczownika &lt;em&gt;Lisp&lt;/em&gt;. A jak naprawdę powinna brzmieć ta forma? Zdania na ten temat są podzielone. Wydaje się, że tradycyjnie w środowisku utrwalona była postać &lt;em&gt;Lispu&lt;/em&gt; (zapewne nawiązująca do odmiany nazw innych wczesnych języków programowania: &lt;em&gt;Fortranu&lt;/em&gt; i &lt;em&gt;COBOL-u&lt;/em&gt;), co potwierdza zarówno &lt;a href="http://poradnia.pwn.pl/lista.php?id=9081"&gt;poradnia językowa PWN&lt;/a&gt;, jak i słowniki: &lt;a href="http://sgjp.pl/"&gt;Słownik gramatyczny języka polskiego&lt;/a&gt; i słownik &lt;a href="http://www.sjp.pl/Lispu"&gt;sjp.pl&lt;/a&gt;. Forma &lt;em&gt;Lispa&lt;/em&gt; (być może urobiona przez analogię do odmiany nazw nowych języków: &lt;em&gt;Perla&lt;/em&gt;, &lt;em&gt;Pythona&lt;/em&gt;) też jest żywa w uzusie, choć, jak się zdaje, nieznacznie ustępuje odmianie &lt;em&gt;Lispu&lt;/em&gt; (w &lt;a href="http://nkjp.pl/"&gt;Narodowym Korpusie Języka Polskiego&lt;/a&gt; znajdujemy jej 19 wystąpień wobec 33 wystąpień &lt;em&gt;Lispu&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;Dla kontrastu, słowo &lt;em&gt;Clojure&lt;/em&gt;, którego paradygmat odmiany (nieodmienne) nie budzi wątpliwości, pojawia się na ogół jako część sformułowania &lt;em&gt;język Clojure&lt;/em&gt;. Można by analogiczny zabieg przeprowadzić z &lt;em&gt;językiem Lisp&lt;/em&gt;. Albo pozostać przy formie &lt;em&gt;Lispu&lt;/em&gt;, która, choć zrazu brzmiała mi dziwnie, teraz wydaje się znacznie naturalniejsza niż &lt;em&gt;Lispa&lt;/em&gt;.&lt;/p&gt;&lt;h3 id="co-to-jest-instrukcja?"&gt;Co to jest instrukcja?&lt;/h3&gt;&lt;p&gt;Porównajmy taki fragment oryginału:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;defrecord&lt;/code&gt; and related functions are covered in Section 6.3, &lt;em&gt;Protocols&lt;/em&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;z odpowiadającym mu tłumaczeniem:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Instrukcję &lt;code&gt;defrecord&lt;/code&gt; i powiązane funkcje omawiamy w podrozdziale 6.3, „Protokoły”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Tłumacz – słusznie – uznaje, że po polsku to zdanie brzmiałoby koślawo bez wprowadzenia na początku rzeczownika określającego, cóż to jest owo &lt;code&gt;defrecord&lt;/code&gt;. Wybiera słowo „instrukcja”, które nie budziłoby żadnych sprzeciwów, gdyby to była książka o Pythonie albo C.  Tu jednak mam wątpliwości. Wiele języków programowania odróżnia wyrażenia od instrukcji, i w nich te drugie rozumiane są jako autonomiczne fragmenty wykonywalnego kodu potencjalnie zmieniającego stan programu. A przecież Clojure kładzie duży nacisk na niezmienność danych! Co więcej, w Clojure, jak we wszystkich Lispach i wielu innych językach funkcyjnych, tak rozumianych instrukcji nie ma (albo patrząc z innego punktu widzenia: termin ten jest tożsamy z terminem „wyrażenie”) -- kod programu po prostu składa się z wyrażeń.&lt;/p&gt;&lt;p&gt;Jeśli więc nie instrukcja, to co? W angielskim &lt;a href="http://clhs.lisp.se/Body/26_a.htm"&gt;żargonie lispowym&lt;/a&gt; używane jest słowo &lt;em&gt;form&lt;/em&gt; na określenie obiektu, który ma być poddany ewaluacji. Jeśli ten obiekt jest listą, której pierwszy element jest symbolem (i w takim razie składnia i semantyka listy są wyznaczone przez ten symbol, który może nazywać funkcję, makro albo wbudowaną konstrukcję języka), to nazwy tego symbolu używa się często jako przydawki. Mówi się więc o „&lt;code&gt;defmacro&lt;/code&gt; forms”, „&lt;code&gt;defvar&lt;/code&gt; forms”, itd. To właśnie to określenie mógł tu pominąć Halloway. A jak jest po polsku &lt;em&gt;form&lt;/em&gt;? Znowu: nie ma powszechnej zgody. Można by to mechanicznie przełożyć na &lt;em&gt;forma&lt;/em&gt;, ale moim zdaniem nazwą mniej przeładowaną znaczeniowo i bardziej oddającą sens jest &lt;em&gt;formuła&lt;/em&gt;. W użyciu są obie wersje i w Google można znaleźć ich przykładowe wystąpienia.&lt;/p&gt;&lt;p&gt;(Na marginesie można oddać tłumaczowi sprawiedliwość, że Halloway tą elizją zdaje się sugerować, że &lt;code&gt;defrecord&lt;/code&gt; jest funkcją, gdy w rzeczywistości jest to makro. Z drugiej strony, makra w Clojure są w istocie funkcjami, których &lt;em&gt;vars&lt;/em&gt; (patrz niżej) mają ustawioną odpowiednią flagę, co powoduje specjalne traktowanie ich przez kompilator.)&lt;/p&gt;&lt;h3 id="zmienna-zmiennej-nierówna"&gt;Zmienna zmiennej nierówna&lt;/h3&gt;&lt;p&gt;Znowu zacznę od cytatu, tym razem najpierw dwóch kawałków tłumaczenia:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Nie występują tu zmienne, modyfikowalny stan ani rozgałęzienia.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Przedrostek &lt;code&gt;#'&lt;/code&gt; oznacza, że funkcję zapisano w &lt;em&gt;zmiennej&lt;/em&gt; języka Clojure (...)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A tak wyglądają te fragmenty w oryginale:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It has no variables, no mutable state, and no branches.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;The prefix &lt;code&gt;#'&lt;/code&gt; indicates that the function was stored in a Clojure &lt;em&gt;var&lt;/em&gt; (...)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;W polskim tłumaczeniu w obydwu tych fragmentach pojawia się słowo &lt;em&gt;zmienna&lt;/em&gt;.  Tymczasem spojrzenie na oryginał ujawnia, że słowo to ma dwa różne odcienie znaczeniowe, z których każdy oddawany jest po angielsku inaczej: raz jest to &lt;em&gt;variable&lt;/em&gt;, a raz &lt;em&gt;var&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Co to jest &lt;em&gt;variable&lt;/em&gt;, wiadomo z innych języków: nazwane „pudełko” przechowujące wartość. (Zauważmy, że Halloway używa tego słowa mówiąc o zmiennych w Javie.)  Jest to termin ogólny i używany w kontekście wszystkich języków imperatywnych, jak C czy Perl. Natomiast &lt;em&gt;var&lt;/em&gt; jest terminem węższym i oznacza &lt;em&gt;jedną z clojurowych implementacji&lt;/em&gt; takiego pudełka. Dokładniej mówiąc, jest to obiekt klasy &lt;code&gt;clojure.lang.Var&lt;/code&gt;. Obiekty takie mają dwie charakterystyczne cechy, nietypowe dla zmiennych z innych języków, a odróżniające je od atomów, również podobnych do zmiennych:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;wartości &lt;em&gt;var&lt;/em&gt; mogą być różne w różnych wątkach (przy czym istnieje koncepcja głównego wiązania, czyli wartości domyślnej, którą nowo utworzone wątki widzą dopóki jej sobie nie przesłonią własnym; takie wiązanie nie musi istnieć);&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;obiekty takie są pamiętane w przestrzeniach nazw: w istocie o przestrzeni nazw można myśleć jako o mapie wiążącej symbol z obiektem &lt;em&gt;var&lt;/em&gt;.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;(Taka dychotomia nazewnicza jest endemiczna dla Clojure i ma również miejsce przy funkcjach: w angielskim slangu clojurowym mówi się o &lt;em&gt;fns&lt;/em&gt;, czytane „ef-ens”, co oznacza obiekty implementujące interfejs &lt;code&gt;clojure.lang.IFn&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;To dopiero dylemat dla tłumacza! Nie mam pojęcia, co mógłbym tu zaproponować. Można by pozostać przy długim, doprecyzowującym określeniu „zmienna &lt;em&gt;języka Clojure&lt;/em&gt;” dla odróżnienia od „zmiennej” po prostu, ale ceną za to jest rozwlekłość. Nie wiem, czy można jej jakoś uniknąć.&lt;/p&gt;&lt;h3 id="insert-into-użytkownik-(imię)-values-(&amp;apos;judyto&amp;apos;)"&gt;INSERT INTO użytkownik (imię) VALUES ('Judyto')&lt;/h3&gt;&lt;p&gt;Ach, kod wypluwający komunikaty w języku naturalnym! Błogosławieni użytkownicy angielszczyzny, albowiem oni odmianą przez przypadki martwić się nie muszą. Halloway, na nieszczęście tłumacza, wpada na pomysł uogólnienia kanonicznego „Hello, world” vel „Witaj, świecie” poprzez sparametryzowanie witanego obiektu.&lt;/p&gt;&lt;p&gt;Chciałoby się, żeby funkcja witająca zwracała się do użytkownika w wołaczu. Tłumacz osiąga to bez problemu:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;hello&lt;/span&gt; [name]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;Witaj, &amp;quot;&lt;/span&gt; name))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I świetnie. Tylko, że trzeba wołać tę funkcję tak: &lt;code&gt;(hello "Janku")&lt;/code&gt;, co jest trochę nienaturalne. A co, jeśli oprócz wyświetlenia powitania chcemy imię zapisać też w bazie danych? Z drugiej strony, &lt;em&gt;licentia poetica&lt;/em&gt; tłumacza nie może sięgać tak daleko, żeby walnąć tu&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;witaj&lt;/span&gt; [imię]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;Witaj, &amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;odmie&lt;/span&gt;ń imię &lt;span class="hljs-symbol"&gt;:wo&lt;/span&gt;łacz)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;!&lt;/p&gt;&lt;h2 id="warto,-nie-warto?"&gt;Warto, nie warto?&lt;/h2&gt;&lt;p&gt;Czy warto będzie kupić „Programowanie w języku Clojure” i przeczytać po polsku? Myślę, że tak, o ile ktoś jeszcze nie czytał po angielsku lub woli ojczysty język nawet kosztem drobnych nieścisłości. Jak już mówiłem, jestem pełen uznania dla pracy tłumacza i chylę czoła. Podziwiam!&lt;/p&gt;&lt;p&gt;Mam za to wątpliwości, czy warto &lt;em&gt;w ogóle tłumaczyć&lt;/em&gt; książki o technologiach. W pierwotnym zamyśle reszta niniejszej notki miała stanowić rozważania na ten temat, ale rozważania techniczne tak się rozrosły, że wpis zrobiłby się za długi. Więc o tym następnym razem.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-12-24:post:jlm</id>
    <title>JLM: o to chodzi</title>
    <link href="http://plblog.danieljanus.pl/2012/12/24/jlm/"/>
    <updated>2012-12-24T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Jeśli próbowaliście, Drodzy Czytelnicy tego bloga, kiedyś zgłębić jakiś temat czytając poświęcone mu artykuły naukowe, to wiecie, że to kosztuje. I to sporo. Typowa cena za pojedynczy artykuł w wersji elektronicznej to kilkanaście-kilkadziesiąt dolarów; a jeśli chce się pozostawać z tematem na bieżąco, pozostaje subskrypcja czasopisma, której koszty potrafią iść w tysiące dolarów rocznie za periodyk. Jeśli nie jest się afiliowanym przy jakiejś instytucji, która ma wykupione odpowiednie subskrypcje, albo nie jest się w stanie znaleźć biblioteki z takowymi, to dostęp do interesującej nas wiedzy jest praktycznie zablokowany.&lt;/p&gt;&lt;p&gt;Sprawy nie ułatwia fakt, że niektóre wydawnictwa uznanych periodyków stosują praktyki niekoniecznie godne moralnej aprobaty, jak grupowanie swoich publikacji w paczki i oferowanie ich subskrypcji tylko w tej formie (oczywiście po odpowiednio wysokich cenach), bez możliwości otrzymywania poszczególnych tytułów &lt;em&gt;à la carte&lt;/em&gt; – przez co biblioteki zmuszone są płacić za materiały, z których nikt potem nie korzysta. Na początku roku głośny był &lt;a href="http://lj.libraryjournal.com/2012/01/publishing/petition-targeting-elseviers-business-practices-begins-to-snowball/"&gt;bojkot wydawnictwa Elsevier&lt;/a&gt; zainicjowany przez Tima Gowersa, laureata Medalu Fieldsa; pod &lt;a href="http://thecostofknowledge.com/"&gt;petycją&lt;/a&gt; podpisało się ponad dziesięć tysięcy naukowców reprezentujących rozmaite dziedziny wiedzy.&lt;/p&gt;&lt;p&gt;Kto na tym zyskuje? Właściwie wyłącznie wydawcy (Wikipedia podaje, że dochód netto wspomnianego wydawnictwa Elsevier za rok 2010 wyniósł ponad miliard dolarów). Kto traci? My wszyscy, i to na wiele sposobów. Raz dlatego, że wydatki na czasopisma naukowe stanowią lwią część i tak skandalicznie niskiego budżetu bibliotek placówek naukowych (nawet jeśli udało nam się już znaleźć artykuł w bibliotece, i tak za niego zapłaciliśmy wydawcy wszyscy z naszych podatków); po wtóre – chciałoby się, żeby wiedza była wolna: żebyśmy uzbrojeni tylko w naszą własną cierpliwość i pasję byli w stanie dzięki dostępowi do niej &lt;em&gt;zrobić różnicę&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Dlatego tak cenna jest Wikipedia. (Wyobrażacie sobie Internet bez niej?) Dlatego tak ważne są projekty takie jak &lt;a href="http://arxiv.org"&gt;arXiv.org&lt;/a&gt;, grupujące setki tysięcy artykułów naukowych, głównie z zakresu fizyki. I dlatego tak się ucieszyłem, znalazłszy w skrzynce informację o ukazaniu się numeru zerowego nowego periodyku z zakresu językoznawstwa.&lt;/p&gt;&lt;p&gt;&lt;a href="http://jlm.ipipan.waw.pl/"&gt;Journal of Language Modelling&lt;/a&gt;, bo o nim mowa, będzie starał się łączyć tematykę lingwistyki teoretycznej i przetwarzania języka naturalnego (NLP). Nadsyłanie artykułów udostępnionych na wolnych licencjach Creative Commons jest nie tylko możliwe, ale wręcz wymagane (przy czym komitet redakcyjny silnie zaleca używanie licencji CC-BY 3.0, a więc najbardziej permisywnej). Równocześnie nowy periodyk nie czyni żadnych ustępstw w kwestii zachowania najwyższych standardów naukowych: artykuły muszą stać na wysokim poziomie merytorycznym i ortograficznym, a każdy z nich przed publikacją będzie musiał w ramach &lt;em&gt;peer review&lt;/em&gt; zostać pozytywnie zaopiniowany przez co najmniej trzech recenzentów.&lt;/p&gt;&lt;p&gt;Takich inicjatyw jest wciąż bardzo mało. Tym bardziej warto je doceniać i reklamować, zwłaszcza że mamy do czynienia z przedsięwzięciem rodzimym – pomysł pojawił się w Instytucie Podstaw Informatyki PAN. Wierzę, że JLM w krótkim czasie zyska sobie renomę i trafi na listy czasopism punktowanych. Brawo!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-12-19:post:o-jaskiniach-programowaniu-i-poezji</id>
    <title>O jaskiniach, programowaniu i poezji</title>
    <link href="http://plblog.danieljanus.pl/2012/12/19/o-jaskiniach-programowaniu-i-poezji/"/>
    <updated>2012-12-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;&lt;a href="http://bronikowski.com/2159/skutki-bycia-ekstremalnie-zajetym"&gt;Emil&lt;/a&gt; w swojej ostatniej notce wspomina, że jego marzeniem jest napisanie przygodówki tekstowej. Moim też! Z tej okazji przypomniał mi się eseik, który wysłałem pięć lat temu na jedną z grup usenetowych; publikuję tu bez zmian, dodając tylko odnośniki i poprawiając literówki. Odnotuję też, że większość materiału historycznego jest zaczerpnięta z „Inform Designer's Manual” Nelsona (1997), a programik, który cytuję na końcu, nie kompiluje się już ostatnimi wersjami I7, choć pewnie wymaga niewielkich przeróbek z zachowaniem formy.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;From: Daniel Janus &lt;przesunmalpe@nathell.korpus.pl&gt;
Newsgroups: pl.hum.poezja
Subject: # o jaskiniach, programowaniu i poezji
Date: Sat, 29 Sep 2007 09:32:45 +0000 (UTC)
Message-ID: &lt;slrnffs6tt.98o.przesunmalpe@students.mimuw.edu.pl&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zaczęło się w 1838 roku.&lt;/p&gt;&lt;p&gt;Wtedy to właściciel Stephena Bishopa, niewolnika „szybkiego, śmiałego, entuzjastycznego”, wykupił ziemię w stanie Kentucky, na której terenie leżała Jaskinia Mamucia. Jej rzeczywisty rozmiar nie był wtedy jeszcze znany: nazwę nadali jej górnicy, wydobywający stąd ongi guano mamucie, z którego wyrabiano saletrę używaną do produkcji prochu. Stephenowi, samoukowi, człowiekowi bystremu, znającemu łacinę i grekę, powierzono opiekę nad jaskinią i w krótkim czasie stał się słynny jako władca podziemnego królestwa.&lt;/p&gt;&lt;p&gt;W kilka lat podwoił on znaną połać jaskini, nadał też nazwy -- półklasyczne, półamerykańskie – podziemnym korytarzom, rzekom, studniom: Rzeka Styks, Sala Lawinowa, Bezdnia, Wielka Kopuła. Wiele z tych nazw przetrwało do tej pory. Stephen oprowadzał po jaskini turystów, których zaczęło ściągać tam coraz więcej; mapa, którą naszkicował posiłkując się tylko własną pamięcią w roku 1842, była jeszcze w użyciu czterdzieści lat później. Stephen Bishop zmarł w roku 1857 jako wolny człowiek, uwolniony na mocy testamentu swojego właściciela.&lt;/p&gt;&lt;p&gt;Minął wiek. W czasie drugiej wojny światowej na terenach Jaskini ustanowiono park narodowy, co skutecznie odcięło napływ turystów. Coraz więcej osób jednak podejrzewało, że Jaskinia połączona jest systemem korytarzy z innymi okolicznymi jaskiniami, i poszukiwało przejścia; jednak bezskutecznie. Dopiero trzydzieści lat później, w roku 1972, Patricia Crowther, członkini ekspedycji badającej jaskinię Flint Ridge, przebrnęła przez ciasne, błotniste przejście i przedostała się do podziemnych głębi Jaskini Mamuciej; korytarz ten odkryto później ponownie na mapie Bishopa.&lt;/p&gt;&lt;p&gt;Trzy lata później mąż Patricii, William, programista w BBN (jednym z pionierów ARPAnetu, z którego później rozwinął się Internet), współuczestnik tamtej wyprawy, postanowił napisać program komputerowy, który jemu samemu przypominałby jaskiniowe lata, a zarazem byłby grą dla jego dzieci. Jak pisał później, miał to być program „nieodstraszający ludzi nieobeznanych z komputerem”, więc umożliwił wydawanie poleceń postaci kierowanej przez gracza w naturalnej angielszczyźnie, zamiast używania jakiegoś sztucznego zestawu komend. W grze Crowthera przemierzało się korytarze jaskini używając kierunków geograficznych; opisy pomieszczeń (z nazwami nadanymi jeszcze przez Bishopa) były zwięzłe, choć wielopoziomowe i bogate w szczegóły geologiczne. Crowther uczynił też niektóre przejścia niedostępnymi; aby się do nich dostać, należało pomyśleć nad rozwiązaniem ich zagadki i wykonać sekwencję określonych czynności.&lt;/p&gt;&lt;p&gt;Tak narodziła się interaktywna fikcja.&lt;/p&gt;&lt;p&gt;Mówię „interaktywna fikcja”, choć na początku używano terminu „gry przygodowe”, a później „przygodowe gry tekstowe” na określenie takich programów. I rzeczywiście były to gry przygodowe: pełne smoków, potworów, magii, ratowania księżniczek, dziejące się w najróżniejszych światach. (Zresztą program Crowthera został później podlany magiczno-baśniowym sosem i zorientowany raczej na zagadki niż na eksplorację, i to ta wersja jest najbardziej znana.) W latach osiemdziesiątych, wraz z upowszechnieniem się komputerów domowych, ten rodzaj rozrywki przeżył swoją świetność, głównie za sprawą firmy Infocom.&lt;/p&gt;&lt;p&gt;Okazało się jednak, że medium wymyślone przez Crowthera potrafi być czymś więcej, niż nośnikiem tylko dla gier przygodowych. Przykładem „&lt;a href="http://en.wikipedia.org/wiki/Photopia"&gt;Photopia&lt;/a&gt;” (Adam Cadre, 1998), opowiadająca historię o wolnej woli i determinizmie, w której gracz, choć znajduje się w centrum wydarzeń, nie ma na nie wielkiego wpływu (choć iluzja, że jest odwrotnie, jest silna); czy „&lt;a href="http://en.wikipedia.org/wiki/Galatea_(video_game)"&gt;Galatea&lt;/a&gt;” (Emily Short, 2000), dla której najwłaściwszym określeniem wydaje się być „studium psychologiczne”: jedno pomieszczenie, jedno światło padające z sufitu, protagonista i... ożywiona rzeźba na piedestale, z którą można rozmawiać na wiele sposobów. Interaktywna fikcja (już teraz określana właśnie tym terminem) odkryta została na nowo, jako rodzaj literatury. Jako -- Sztuka.&lt;/p&gt;&lt;p&gt;Po minionym boomie lat osiemdziesiątych zaczęły powstawać i rozpowszechniać się bezpłatnie systemy programistyczne ułatwiające tworzenie interaktywnej fikcji. Jednym z nich był Inform (Graham Nelson, 1993–): jego pionierska zasługa to skodyfikowanie ścisłych, choć poddających się elastycznym modyfikacjom w razie potrzeby, domyślnych reguł rządzących konstruowanym światem – modelu świata. System ten rozwijał się powoli, acz stabilnie, bez rewolucyjnych zmian, aż do wersji siódmej, kiedy Nelson postanowił radykalnie zmienić sposób tworzenia IF przy użyciu Informu. Co się zmieniło?&lt;/p&gt;&lt;p&gt;Aby używać wersji 1-6, trzeba było zostać programistą: nauczyć się specjalnej składni, obiektowo-imperatywnego paradygmatu programowania i sporej związanej z tym teorii. Inform 7 przyjmuje bardziej deklaratywne podejście i ukrywa sporą część pracy z tym związanej za fasadą języka, czyniąc następny krok w używaniu języka naturalnego: nie tylko w produkcie końcowym – ale też w kodzie źródłowym programu. Oto programy w Informie 7 pisze się w języku angielskim (albo do złudzenia go przypominającym), tyle tylko, że nieco bardziej skodyfikowanym.&lt;/p&gt;&lt;p&gt;Żeby nie być gołosłownym – oto pokój zawierający jabłko w Informie 6:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Constant Story "Hello World";
Constant Headline "^An Interactive Example^";

Include "Parser";
Include "VerbLib";

[ Initialise;
  location = Living_Room;
  "Hello World";
];

Object Room "Living Room"
with description "A comfortably furnished living room.",
has  light;

Object -&gt; Apple "juicy apple"
with name 'juicy' 'apple' 'fruit',
     description "Looks yummy.";
has  edible;

Include "Grammar";
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A oto ten sam pokój w Informie 7:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;"Hello World" by Daniel Janus

The story headline is "An Interactive Example".

The Living Room is a room. "A comfortably furnished living room."
The apple is an edible thing in the Living Room. Description of
the apple is "Looks yummy." Understand "juicy", "apple" and
"fruit" as the apple.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Stąd już niedaleko do poezji. Na pewnym blogu ogłoszone zostało wyzwanie: czy da się napisać rymujący się wiersz (choćby i z rymami w stylu Nasha), który jest &lt;em&gt;jednocześnie&lt;/em&gt; kompilującym się programem w Informie 7? Okazało się, że tak. I to nie byle jaki wiersz, bo sonet (a nawet metasonet – jego tematem są bowiem sonety!)&lt;/p&gt;&lt;p&gt;Oto on:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Will's Study is a room. The desk is here.
A hastily handwritten note is on it.
Description is "It's from your friend Shakespeare:
'I've gone to lunch. You'll have to write the sonnet.'"
Composing is an action applying to nothing.
The quill is a thing that is in the study.
Understand "write sonnet" as composing.
Description of the quill is "Old and cruddy".
Instead of composing when the player
has no quill, say "You have not got the quill."
Instead of composing, say "And... done. 'Heya',
says Will, returning. You say, 'Hello, Will!'
Says Shakespeare, 'Thank you for the time you've taken!
You really are a pal, Sir Francis Bacon.'"

-- Robin Johnson
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Przypis: akcje w Informie są motorem zmiany stanu świata; przebieg akcji jest podzielony na kilka etapów – dokładnie specyfikuje to model świata, o którym pisałem wcześniej. Specyfikacja, że coś ma się wykonać „zamiast” jakiejś akcji, oznacza interferencję w model na pierwszym etapie, zanim zaczną działać normalne reguły.)&lt;/p&gt;&lt;p&gt;I moje rudymentarne tłumaczenie:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Dębowe biurko stoi w gabinecie Willa.
Na nim kartka z notatką skreśloną starannie.
Opis: "Od przyjaciela twojego, Szekspira:
„Poszedłem na kolację, napisz sonet za mnie.”"
Pisanie jest to akcja bezargumentowa.
Gęsie pióro to przedmiot, co jest w gabinecie.
Opis pióra: "Możesz nim ubrać myśli w słowa."
Powiedz: "Nie możesz pisać, pióra nie masz przecie!"
zamiast pisania, gdy gracz nie posiada pióra.
Zamiast pisania, powiedz: "I oto - skończone.
Will powraca z kolacji z uśmiechem ponurym,
lecz wnet się rozpromienia, widząc gotów sonet.
Uśmiechasz się, Will także, chyląc głowę w skłonie:
„Dobry z Waści przyjaciel, Franciszku Baconie.”"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oczywiście, to tylko połowa roboty, ta łatwiejsza – drugą będzie przeportowanie Informu 7 na język polski, tak aby powyższy wiersz dało się skompilować i sobie pograć :-)&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-12-04:post:moja-ulica-murem-podzielona</id>
    <title>Moja ulica murem podzielona</title>
    <link href="http://plblog.danieljanus.pl/2012/12/04/moja-ulica-murem-podzielona/"/>
    <updated>2012-12-04T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Autobus zjeżdżający z gór wysadza nas na przystanku u bramy starego miasta. Dokładniej w południowo-zachodniej części, konstatujemy spojrzawszy na mapę. Określenie "brama" w tym miejscu jest przenośne: funkcję bramy pełni rondo wyłożone pół asfaltem, pół brukiem; mur, który kiedyś otaczał miasto, jest teraz rozkopanym rowem, z którego zaczynają wyrastać fundamenty nie wiadomo czego.&lt;/p&gt;&lt;p&gt;Według mapy, hostel mieści się na starówce, we wschodniej części, tuż przy granicy. To jakieś półtora kilometra stąd. Wciąż nie wiemy, czy mają wolne miejsca (taki urok trampowego wyjazdu: nigdy nie wiesz, gdzie będziesz spać jutro) – iść tam od razu czy najpierw zadzwonić? Nie jest daleko, ciepły wieczór zachęca do spaceru. Zabieramy plecaki i ruszamy.&lt;/p&gt;&lt;p&gt;Miasto tętni życiem, jest gwarno, ludzie siedzą w knajpach albo niespiesznie przechadzają się od jednej do drugiej. Po kilku minutach docieramy do ulicy wyglądającej jak główny deptak. Na środku placu, między konarami drzewa rozsiadł się kot, drzemiąc i niewiele sobie robiąc z rzeczywistości. Dwa inne krążą między turystami w nadziei na darmowe żarcie. Jeszcze jeden rzut oka na mapę.&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;You need help?&lt;/em&gt; – z zamyślenia wyrywa mnie jakiś głos.&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;No, thanks, I'm fine.&lt;/em&gt; – odpowiadam, odsuwając się na wszelki wypadek. Ludzie tu bardzo chcą być pomocni, co czasem bywa irytujące: wystarczy przez chwilę wyglądać na zagubionego.&lt;/p&gt;&lt;p&gt;Powinniśmy pójść na północ, wzdłuż tego deptaku, a potem po jakichś dwustu metrach skręcić w prawo i iść cały czas prosto. Ulica, skręcając łukiem o dziewięćdziesiąt stopni z powrotem na północ, wyprowadzi nas w końcu na przecznicę, przy której jest hostel, a stamtąd już tylko dwa kroki. Odrywamy wzrok i obiektyw aparatu od śpiącego kota i ruszamy dalej, zgodnie z planem schodząc z deptaku za McDonaldem.&lt;/p&gt;&lt;p&gt;I wchodzimy w portal prowadzący do innego świata.&lt;/p&gt;&lt;p&gt;Pierwsze, co rzuca się w oczy, to znacznie mniej przechodniów; ale to jasne – przy tej ulicy prawie nie ma kawiarń, więc wszyscy siedzą przy deptaku, gdzie koncentruje się nocne życie. Przecinamy małe rondo na przełaj. Dalsza część ulicy nie jest oświetlona, tylko gdzieniegdzie w oknach palą się światła. Uliczka jest wąska, na szerokość jednego samochodu, z wąziutkim kawałkiem chodniczka z jednej strony: prawie trzeba się przytulać do budynków, gdy coś przejeżdża. Stare szyldy na pozamykanych zakładach wyglądają na nieodnawiane od lat. Uśmiechamy się na widok jednego z nich głoszącego NO BORDERS. Jakże adekwatne.&lt;/p&gt;&lt;p&gt;Dalej po prawej stronie ciągnie się mur pokryty graffiti, a po lewej nie ma już świateł w oknach. A jeszcze chwilę potem nie ma już nawet okien. To znaczy są, ale zabite deskami albo z powybijanymi szybami, za którymi piętrzą się sterty gruzu i drewnianych płyt. Noc jest bezchmurna, księżyc w pełni plus łuna znad żywej części miasta sprawia, że wszystko wyraźnie widać.&lt;/p&gt;&lt;p&gt;Wchodzimy w zakręt; więc już niedaleko. Mijane przecznice z jednej strony są ślepe, zakończone zasiekami z wielkich metalowych beczek, darni i drutu kolczastego. Granica. Tędy tylko koty przemykają się niezauważenie między przerwami w barykadzie.&lt;/p&gt;&lt;p&gt;Wychodząc na przecznicę hostelową mijamy ostatni fragment zasieków, obok którego, na ogrodzonym drucianą siatką posterunku na przestrzeni nie większej niż dwa na dwa metry, stoi żołnierz z karabinem. ONZ-owski? To nie może być łatwe, tak stać godzinami, bezczynnie. Zastanawiamy się, jak często zdarza się, że im odbija i zaczynają strzelać do przechodniów.&lt;/p&gt;&lt;p&gt;I absurdalna wydaje się myśl, że wciąż jesteśmy w mieście, które jest stolicą państwa członkowskiego UE. Ale to prawda. Jest tylko jedno takie miasto.&lt;/p&gt;&lt;p&gt;Λευκωσία.&lt;/p&gt;&lt;p&gt;Lefkoşa.&lt;/p&gt;&lt;p&gt;Nikozja, ostatnia podzielona stolica świata.&lt;/p&gt;&lt;hr&gt;
&lt;p&gt;Drewniane drzwi są zamknięte na klucz, ale w oknie świeci się światło. Zaglądam: duże, nieumeblowane pomieszczenie to zapewne to, co strona internetowa szumnie nazywa salą konferencyjną. Ktoś się tam snuje i gotuje herbatę. Po chwili drzwi otwierają się.&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;Hello, would you have two beds available for tonight?&lt;/em&gt;&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;No, no place.&lt;/em&gt; – Młody człowiek słabo mówi po angielsku.&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;Not even a free scrap of floor? We have sleeping bags.&lt;/em&gt; -- upewniam się.&lt;/p&gt;&lt;p&gt;-- &lt;em&gt;Sorry, no.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Trudno. Wracamy niedaleko punktu startowego, gdzie w hotelu Delphi znajdujemy pokój dwa razy droższy, ale za to na najwyższym piętrze i z niezłym widokiem na stare miasto z tarasu. Na horyzoncie za gruzami powiewają flagi Turcji i TRNC, Cypru Północnego.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-09-28:post:perfekcyjna-niedoskonalosc-usosa</id>
    <title>Perfekcyjna niedoskonałość USOS-a</title>
    <link href="http://plblog.danieljanus.pl/2012/09/28/perfekcyjna-niedoskonalosc-usosa/"/>
    <updated>2012-09-28T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;&lt;a href="http://www.rp.pl/artykul/10,937291-Studenci-bez-cwiczen.html"&gt;Rzeczpospolita&lt;/a&gt; pisze o problemach z &lt;a href="http://usos.edu.pl/"&gt;USOS-em&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;– Poprawki oprogramowania nie są potrzebne, bo oprogramowania lepiej nie da się napisać – tłumaczy dr Janina Mincer-Daszkiewicz, kierownik Komisji ds. USOS z Uniwersytetu Warszawskiego. Jej zdaniem problem tkwi w liczbie logowań. – Jeśli w ciągu pierwszych pięciu minut trwania rejestracji na serwery uczelniane rzuca się 5 czy 10 tysięcy studentów, to i najlepszy komputer nie pomoże -- dodaje.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href="http://dict.pl/dict?word=mincer"&gt;Nazwisko&lt;/a&gt; pani Mincer-Daszkiewicz jest doskonale znane każdemu studentowi informatyki na Wydziale MIM UW, który w ciągu ostatnich kilkunastu lat zdawał egzamin z systemów operacyjnych: jej zasługi nie tylko dydaktyczne, ale również na polu informatyzacji obsługi Wydziału, a także całego UW, są nie do przecenienia. Tym bardziej dziwi powyższa wypowiedź.&lt;/p&gt;&lt;p&gt;I nawet nie chodzi mi o to, że stwierdzenie, że nie da się obsłużyć dziesięciu tysięcy użytkowników naraz, jest zwyczajnie nieprawdziwe (wystarczy sobie uświadomić wielkość liczby żądań, jakimi w każdej sekundzie bombardowane są serwery wyszukiwarki Google, albo ilu użytkowników naraz jest średnio zalogowanych do Facebooka; bez kłopotu osiągalne jest &lt;a href="http://www.kegel.com/c10k.html"&gt;10 tys. żądań na sekundę nawet na pojedynczej maszynie&lt;/a&gt;). Problemem jest raczej mentalność wyczuwalna w pierwszym zdaniu.&lt;/p&gt;&lt;p&gt;Kto pierwszy powiedział: „dzieła sztuki nie są nigdy ukończone – są porzucane”? Nie wiem; guglanie za „never finished, only abandoned” daje najrozmaitsze wyniki i pokazuje tę frazę odnoszoną do najrozmaitszych dziedzin, w tym do oprogramowania. Każdy, kto kiedykolwiek pracował nad dostatecznie dużym systemem, wie, jak bardzo jest to prawdziwe. Zawsze można coś poprawić: zwiększyć wydajność, odchudzić, dopracować; nie ma siódmego dnia, w którym można by odpocząć i popatrzeć z dumą na ukończone dzieło. Pani Mincer-Daszkiewicz zdaje się sądzić, że USOS osiągnął stan perfekcji; nie, nie osiągnął, podobnie jak nie osiągnął go żaden inny system.&lt;/p&gt;&lt;p&gt;A poprawiać jest co. Świadczy o tym pomruk niezadowolenia setek studentów, niemogących „wbić się” do USOSweb. Gdyby USOS realizowany był jako przedsięwzięcie komercyjne, to ze względu na to, że realia rynkowe wymuszają takie myślenie – ktoś by w końcu przyszedł i powiedział: „tak nie może być”. Nie może, bo bywa nieużywalne w fundamentalny sposób; nie może, bo udostępnienie produktu w takiej postaci klientom narazi przedsiębiorstwo na duże straty. Tłumaczenie się ograniczeniami architekturalnymi jest nie do przyjęcia: jeśli architektura w obecnej postaci nie pozwala na sensowne działanie systemu, to znaczy, że trzeba go przeprojektować.&lt;/p&gt;&lt;p&gt;Bardzo starałem się nie oddzielić grubą linią „podejścia biznesowego” od „akademickiego”, bo po obu stronach są i chlubne, i mniej chlubne przykłady. W ramach promocji tych pierwszych skorzystam z okazji i polecę stronę &lt;a href="http://tao.inf.ug.edu.pl/"&gt;dr Włodzimierza Bzyla&lt;/a&gt; z Uniwersytetu Gdańskiego; widać z niej pragmatyzm, duże zainteresowanie nowoczesnymi narzędziami i dążenie do przygotowywania studentów do późniejszej praktyki zawodowej -- słowem, autor &lt;em&gt;wie o co chodzi&lt;/em&gt;. Oby takich więcej!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-01-10:post:praktyczne-uzycie-monady-state</id>
    <title>Praktyczne użycie monady state</title>
    <link href="http://plblog.danieljanus.pl/2012/01/10/praktyczne-uzycie-monady-state/"/>
    <updated>2012-01-10T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Wreszcie rozumiem monady!&lt;/p&gt;&lt;p&gt;Pierwszy raz zetknąłem się z nimi dobrych osiem lat temu, przy okazji nauki Haskella; wtedy jednak nie starczyło mi cierpliwości, aby zaznajomić się z podstawami teoretycznymi. Sprawy nie ułatwiał fakt, że trudno o &lt;em&gt;naprawdę&lt;/em&gt; przystępne i zrozumiałe wprowadzenie do tego tematu: wygląda na to, że każdy adept Haskella, zrozumiawszy monady, pisze na ten temat własny &lt;em&gt;tutorial&lt;/em&gt;. Ja się powstrzymam od tego naturalnego odruchu i po prostu odeślę do niesamowicie szczegółowego, ośmioczęściowego &lt;a href="http://mvanier.livejournal.com/3917.html"&gt;cyklu artykułów&lt;/a&gt; autorstwa Mike'a Vaniera, który -- wreszcie! – sprawił, że coś mi „zaskoczyło” w umyśle. Zamiast tego w tym wpisie wynotuję najważniejsze spostrzeżenia, jakie zapamiętałem, a potem pokażę dwie wersje pewnego kodu operującego na sekwencjach bitów: niemonadyczną i napisaną z użyciem monady &lt;code&gt;state&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Mike zakłada znajomość Haskella, jak pisze we wstępie, na poziomie typów polimorficznych i klas typów; w praktyce jednak myślę, że do zrozumienia tekstu wystarczy pewne obycie z haskellową notacją (bardzo zresztą przypominającą zwykłą notację matematyczną), bo bardziej zaawansowane rzeczy są wyjaśniane w tekście na bieżąco. Bardzo polecam.&lt;/p&gt;&lt;p&gt;Oto więc moje notatki:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Kluczowe dla zrozumienia monad jest pojęcie &lt;em&gt;funkcji monadycznych&lt;/em&gt;; znacznie łatwiej jest je sobie intuicyjnie wyobrazić niż monadyczne wartości, zwracane przez &lt;code&gt;m-return&lt;/code&gt;. Mike podaje pewne intuicje także dla tych drugich, ale dla mnie ważne jest, żeby myśleć o monadach w kategoriach (wzbogaconych) funkcji, a nie wartości przez nie zwracanych.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Łatwo wydefiniować przy użyciu prostego złożenia operacji &lt;code&gt;m-bind&lt;/code&gt; i &lt;code&gt;m-return&lt;/code&gt; funkcję składającą ze sobą dwie funkcje monadyczne. Przy użyciu tego operatora, który Mike nazywa &lt;code&gt;&lt;=&lt;&lt;/code&gt;, prawa rządzące monadami wyrażają się w elegancki i prosty do zapamiętania sposób: jest to operator łączny oraz &lt;code&gt;m-return&lt;/code&gt; jest jego lewą i prawą jedynką.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;I bodaj najistotniejsza rzecz – nie trzeba rozumieć monad, żeby ich używać. Niby oczywiste i niby tak właśnie pisałem do tej pory kod haskellowy, radośnie używając &lt;code&gt;do&lt;/code&gt;-notacji tak jakbym pisał kod imperatywny; ale okazuje się, że w ten sam sposób można z powodzeniem używać monady stanu i wyjaśnić, co ona robi, zupełnie w oderwaniu od całej reszty teorii monad. Wiedza na temat tego, co się dzieje „pod spodem”, jest konieczna tylko (i aż!) do zrozumienia, co poszczególne monady mają ze sobą wspólnego.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Reszta tego wpisu będzie poświęcona właśnie przykładowemu użyciu monady stanu w Clojure.  &lt;a href="http://plblog.danieljanus.pl/leniwa-wersja-makra"&gt;Pisałem wcześniej&lt;/a&gt;, że nie widziałem dotąd kodu, który dałby się wyrazić przejrzyściej zapisany z użyciem monad niż bez nich – i właśnie na taki kod się natknąłem.&lt;/p&gt;&lt;p&gt;Wyobraźmy więc sobie, że mamy strumień bitów. Dla prostoty zdefiniujmy sobie przykładowy strumień, na którym będziemy testować nasze funkcje, jako clojurowy wektor zer i jedynek.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;v&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Chcemy naddać naszemu strumieniowi pewne uporządkowanie: zdekodować zera i jedynki, które z niego przychodzą, według jakiegoś kodu o zmiennej długości. Najbardziej oczywistym, znanym każdemu kodem jest kod binarny: czytamy ze strumienia ileś bitów, traktujemy je jako binarną (bez znaku) reprezentację pewnej liczby i zwracamy tę liczbę. Łatwo napisać w Clojure funkcję, która to robi.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-binary&lt;/span&gt; [n bits]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [res &lt;span class="hljs-number"&gt;0&lt;/span&gt; n n [fst &amp;amp; rst &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; bs] bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; n)
      [res bs]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; res res fst) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; n) rst))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Spróbujmy wczytać z naszego strumienia czterobitową liczbę:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [14 (1 0 1 0 1 0)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czternaście. Ale zauważmy, że zdekodowana liczba nie jest jedyną zwracaną wartością! Żeby dało się z naszego strumienia odczytywać dalsze liczby, musimy zwrócić parę (wektor dwuelementowy): wartość odczytana plus &lt;em&gt;reszta strumienia&lt;/em&gt;, z której będziemy czytać dalej. Piszemy wszak funkcyjnie: nie zmieniamy naszych wartości w miejscu, tylko z jednych wartości produkujemy następne.&lt;/p&gt;&lt;p&gt;Innym rodzajem kodu jest &lt;em&gt;kod unarny&lt;/em&gt;: czytamy po prostu ze strumienia jedynki, aż napotkamy pierwsze zero – wtedy przestajemy czytać i naszą wartością jest liczba przeczytanych bitów. W ten sposób dowolną liczbę dodatnią &lt;em&gt;n&lt;/em&gt; da się zareprezentować na &lt;em&gt;n&lt;/em&gt; bitach. Implementacja jest równie prosta:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-unary&lt;/span&gt; [bits]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [n &lt;span class="hljs-number"&gt;1&lt;/span&gt; [fst &amp;amp; rst] bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; fst)
      [n rst]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;inc&lt;/span&gt;&lt;/span&gt; n) rst))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sprawdzamy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [4 (1 0 1 0 1 0)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Trzy jedynki i zero, razem cztery skonsumowane bity, więc odczytaną liczbą jest 4. Działa.&lt;/p&gt;&lt;p&gt;Spróbujmy teraz skleić nasze funkcje, to znaczy odczytać ze strumienia dwie liczby: najpierw zakodowaną unarnie, a potem binarnie na sześciu bitach.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [[x v1] (&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; v)
      [y v2] (&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; &lt;span class="hljs-number"&gt;6&lt;/span&gt; v1)]
  [x y])
&lt;span class="hljs-comment"&gt;;=&amp;gt; [4 42]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wynik jest poprawny, ale kod nieelegancki: tak naprawdę nie interesują nas te wszystkie &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;, które przepychamy przez nasze funkcje; potrzebujemy tylko przeczytanych liczb, a poszczególne części strumienia są nam tylko po to, żeby mieć z czego czytać. Im więcej składanych ze sobą operacji, tym łatwiej się pomylić.&lt;/p&gt;&lt;p&gt;Tu wkracza monada stanu, która umożliwia ukrycie tych pośrednich wartości i bardziej eleganckie złożenie &lt;code&gt;read-unary&lt;/code&gt; i &lt;code&gt;read-binary&lt;/code&gt;. Monadycznie zapisalibyśmy to jakoś tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;domonad&lt;/span&gt; state-m
         [x read-unary
          y (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;partial&lt;/span&gt;&lt;/span&gt; read-binary &lt;span class="hljs-number"&gt;6&lt;/span&gt;)]
         [x y])
&lt;span class="hljs-comment"&gt;;=&amp;gt; #&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wygląda to bardzo podobnie. &lt;code&gt;domonad state-m&lt;/code&gt; jest jak &lt;code&gt;let&lt;/code&gt;. Tak jak chcieliśmy, nie przekazujemy naszego stanu (strumienia) &lt;em&gt;explicite&lt;/em&gt;; zamiast tego przy &lt;code&gt;x&lt;/code&gt; i &lt;code&gt;y&lt;/code&gt; podajemy funkcje, które mają być zawołane na aktualnym stanie, żeby uzyskać żądaną wartość i nowy stan.&lt;/p&gt;&lt;p&gt;No dobrze, ale gdzie tu miejsce na nasz stan początkowy? Jak go przekazać? Proste: powyższa formuła, jak widać z mało czytelnego wyniku, zwróciła jakąś funkcję. Wystarczy ją teraz zawołać na naszym początkowym stanie, aby uzyskać wynik wraz ze stanem końcowym:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;*1&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [[4 42] nil]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jedyne, co wydaje się nadmiarowe w naszym złożeniu, to owo &lt;code&gt;partial&lt;/code&gt; pojawiające się wyżej. Jest to efekt uboczny faktu, że Clojure w odróżnieniu od Haskella rozróżnia funkcje jedno- i więcejargumentowe (tak dokładniej to w Haskellu występują wyłącznie funkcje jednoargumentowe). Skoro w &lt;code&gt;domonad state-m&lt;/code&gt; powinny na przemian pojawiać się symbole i funkcje jednoargumentowe biorące stan, to gdy nasza funkcja akceptuje coś jeszcze (tu: liczbę bitów), powinniśmy zrobić z niej funkcję jednoargumentową za pomocą &lt;code&gt;partial&lt;/code&gt;. Alternatywnie, jeżeli chcemy trzymać się stylu monadycznego, możemy przepisać &lt;code&gt;read-binary&lt;/code&gt; jako:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-binary&lt;/span&gt; [n]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [res &lt;span class="hljs-number"&gt;0&lt;/span&gt; n n [fst &amp;amp; rst &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; bs] bits]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; n)
        [res bs]
        (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; res res fst) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; n) rst)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Teraz zamiast &lt;code&gt;(partial read-binary 6)&lt;/code&gt; możemy napisać po prostu &lt;code&gt;(read-binary 6)&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Teraz, kiedy umiemy już składać operacje stanowe, zilustrujemy to przy pomocy jeszcze jednego kodu, a właściwie rodziny kodów nazywanych &lt;em&gt;kodami Golomba&lt;/em&gt;. Po szczegółowy opis odsyłam do &lt;a href="http://pl.wikipedia.org/wiki/Kod_Golomba"&gt;Wikipedii&lt;/a&gt;, natomiast dla potrzeb tego artykułu wystarczy nam wiedza, że te kody są parametryzowane jedną liczbą &lt;em&gt;m&lt;/em&gt;. Liczba w kodzie Golomba jest podzielona na dwie części, z których pierwsza jest zakodowana unarnie, a druga -- nie większa niż &lt;em&gt;m&lt;/em&gt; – binarnie. W implementacji możemy więc skorzystać z naszych gotowych już funkcji &lt;code&gt;read-unary&lt;/code&gt; i &lt;code&gt;read-binary&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Bez dłuższych już komentarzy przedstawię dwie implementacje: eksplicytną i używającą monady stanu. Obie będą używać pomocniczej funkcji &lt;code&gt;read-bit&lt;/code&gt;, czytającej po prostu jeden bit ze strumienia i używanej tak samo jak &lt;code&gt;read-unary&lt;/code&gt; czy &lt;code&gt;read-binary&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-bit&lt;/span&gt; [[bit &amp;amp; bits]]
  [bit bits])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Najpierw wersja niemonadyczna:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-golomb&lt;/span&gt; [m]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [k (&lt;span class="hljs-name"&gt;clog2&lt;/span&gt; m)
          r (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;bit-shift-left&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; k) m)
          [a bits1] (&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; bits)
          [b bits2] ((&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; k)) bits1)
          [ir bits] (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r)
                      [b bits]
                      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [[nb bits] (&lt;span class="hljs-name"&gt;read-bit&lt;/span&gt; bits)]
                        [(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; b b nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; r)) bits]))]
      [(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; a) m) ir) bits])))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A teraz używająca monad. Żeby nie przeplatać &lt;code&gt;let&lt;/code&gt; i &lt;code&gt;domonad&lt;/code&gt;, można po prostu zdefiniować funkcję zwracającą pewną stałą wartość i niezmieniony stan – nazwę ją &lt;code&gt;m-const&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;m-const&lt;/span&gt; [x]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [state]
    [x state]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Używając jej, możemy zaimplementować &lt;code&gt;read-golomb&lt;/code&gt; tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-golomb-monadic&lt;/span&gt; [m]
  (&lt;span class="hljs-name"&gt;domonad&lt;/span&gt; state-m
           [k (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; (&lt;span class="hljs-name"&gt;clog2&lt;/span&gt; m))
            r (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;bit-shift-left&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; k) m))
            a read-unary
            b (&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; k))
            nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r) (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; &lt;span class="hljs-literal"&gt;nil&lt;/span&gt;) read-bit)]
           (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; a) m)
              (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r) b (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; b b nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; r))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czytelniej?&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-09-05:post:blog-day</id>
    <title>Spóźniony wpis na Blog Day 2011</title>
    <link href="http://plblog.danieljanus.pl/2011/09/05/blog-day/"/>
    <updated>2011-09-05T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Przegapiłem tegoroczny Dzień Blogów. Ale kto powiedział, że notka zawierająca odnośniki do pięciu lubianych i czytanych przeze mnie blogów nie ma prawa pojawić się kilka dni po kanonicznej dacie? Oto więc one:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;&lt;a href="http://notatnik.mekk.waw.pl/"&gt;Notatnik zapisywany wieczorami&lt;/a&gt;: na tej liście teoretycznie nie powinno być blogów z „mojej branży” czyli okołoinformatycznych, ale nie mogę tu nie umieścić odnośnika do zapisków Marcina Kasperskiego. Bo też nie są one &lt;em&gt;stricte&lt;/em&gt; techniczne (z ostatnich miesięcy przychodzi mi na myśl świetna &lt;a href="http://notatnik.mekk.waw.pl/archives/259-Autorytet.html"&gt;notka o autorytetach&lt;/a&gt;), a są dla mnie wzorem: setki wpisów, a każdy obszerny, wartościowy i zawierający oryginalne przemyślenia lub cenną syntezę wiedzy. Marcin nie stara się zaskarbić sobie czytelników akcjami marketingowymi i nie dba specjalnie o rozgłos. Po prostu robi swoje: pisze ciekawie. Bardzo, bardzo bym chciał, żeby takich blogów było więcej.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;a href="http://andsol.blox.pl/html"&gt;Migotanie słów&lt;/a&gt;: Polski matematyk w Brazylii: o kulturze, o historii, o brazylijskich doświadczeniach i, jakże by inaczej, o matematyce. Jeśli miałbym określić ten blog jednym przymiotnikiem, byłoby to słowo „mądry”. Dziękuję, Andrzeju.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;a href="http://zoknanastrychu.blox.pl/html"&gt;Widziane z okna na strychu&lt;/a&gt;: Można by powiedzieć, że to blog feministyczny, ale to bardzo spłycające określenie. Jak dotąd, siedem wpisów. Ale jakich! Już pierwszy ujął mnie stylem i roztropnością – chciałbym umieć pisać takim językiem i z takim wyczuciem o trudnych sprawach.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;a href="http://haderech.blox.pl/html"&gt;Haderech, czyli droga&lt;/a&gt;: O ilu miejscach w sieci mogę powiedzieć „pamiętam, jak tu trafiłem”? O tym tak, choć od roku nie jest już aktualizowane. Szukałem, skąd się wziął tekst „Who By Fire” Cohena -- jak się okazuje, jest on zaczerpnięty z Unetane Tokef, dwunastowiecznej hebrajskiej modlitwy. Bardzo ciekawe studium odkrywania własnej tożsamości i odkrywania starodawnej ścieżki. Polecam i podziwiam.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;a href="http://czterycztery.pl/blog"&gt;Złote myśli Piotra S.&lt;/a&gt;: zanim Twitter stał się, ten blog już był. Trudno mi oddać jego specyfikę, polecam po prostu zajrzenie i doświadczenie specyficznego humoru autora :-)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I na zakończenie ciekawostka liczbowa: trzy z opisywanych blogów prowadzone są w serwisie Blox, a dwa używają autorskich silników. &lt;em&gt;Go figure.&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-07-24:post:mazunix-dabrowskiego</id>
    <title>MazUNIX Dąbrowskiego</title>
    <link href="http://plblog.danieljanus.pl/2011/07/24/mazunix-dabrowskiego/"/>
    <updated>2011-07-24T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;pre&gt;&lt;code&gt;Jeszcze Polska nie zginęła,    $ (ps ax | grep Polska)
póki my żyjemy.                  &amp;&amp; (ps ax | grep `whoami`)
Co nam obca przemoc wzięła,    $ sudo chown -R / `whoami`
szablą odbierzemy.
  Marsz, marsz, Dąbrowski,     $ ./Dabrowski
  z ziemi włoskiej do Polski.    &lt;/dev/ziemia-wloska &gt;/dev/polska
  Za twoim przewodem           $ ifconfig eth0 up
  złączym się z narodem.       $ telnet polska.pl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-07-21:post:clojurescript</id>
    <title>ClojureScript</title>
    <link href="http://plblog.danieljanus.pl/2011/07/21/clojurescript/"/>
    <updated>2011-07-21T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Rich Hickey, autor Clojure, wyrasta na Steve'a Jobsa nowoczesnych języków programowania – przynajmniej jeśli idzie o sposób prezentacji swoich dzieł. Oto kilka dni temu na grupie dyskusyjnej Clojure pojawiło się &lt;a href="https://groups.google.com/forum/#!msg/clojure/v2fP1qQQBhQ/Xz836_GafIgJ"&gt;ogłoszenie&lt;/a&gt;, że Rich wystąpi na najbliższym spotkaniu nowojorskiej grupy użytkowników Clojure, że strumień wideo z wystąpienia będzie nadawany na żywo i że zaprezentowane zostanie „coś nowego”.&lt;/p&gt;&lt;p&gt;Tym czymś okazał się &lt;a href="https://github.com/clojure/clojurescript"&gt;ClojureScript&lt;/a&gt;: kompilator Clojure do JavaScriptu, łączący moc pierwszego języka z powszechnością drugiego.&lt;/p&gt;&lt;p&gt;JavaScript bywa nazywany „czarnym koniem” języków programowania, najbardziej niedocenianym językiem minionej dekady i najważniejszym obecnej. Istotnie, silniki JavaScriptu w przeglądarkach – a także wolnostojące, jak Node.js – poczyniły w ostatnich latach kolosalne postępy, jeśli chodzi o wydajność, narzędzie to jest coraz chętniej używane do programowania coraz poważniejszych aplikacji i nie mijając się z prawdą można powiedzieć, że JavaScript jest wszędzie.&lt;/p&gt;&lt;p&gt;A teraz te same słowa można odnieść również do Clojure. W tegorocznym &lt;a href="http://cemerick.com/2011/07/11/results-of-the-2011-state-of-clojure-survey/"&gt;sondażu&lt;/a&gt;, podobnie jak rok temu, społeczność Clojure najczęściej wskazywała JavaScript jako hipotetyczną alternatywną wobec JVM platformę, ale zapewne mało kto się spodziewał, że urzeczywistni się to tak szybko. Łatwo sobie uzmysłowić możliwości, jakie daje nowa implementacja. Ten sam kod na serwerze i w przeglądarce? Programowanie aplikacji iOS w Clojure? Jak najbardziej!&lt;/p&gt;&lt;p&gt;W ramach dodawania łyżki dziegciu do tej beczki miodu trzeba zauważyć jednak, że dialekt ClojureScript jest okrojony i ma nieco inną semantykę niż standardowy Clojure: nie ma STM, niezmienność struktur danych nie jest wymuszana, wektory są zwykłymi JavaScriptowymi tablicami. Czy to się zmieni, pokaże przyszłość.&lt;/p&gt;&lt;p&gt;Jaka przyszłość czeka Clojure i ClojureScript? Spróbuję pokusić się o przepowiednię:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Pojawienie się alternatywnej wobec JVM platformy wymusi ukształtowanie się bogatszej niż do tej pory biblioteki standardowej Clojure, wspólnej dla obu wersji. Ten proces trwa już teraz: tam, gdzie w czasach Clojure 1.0 pisało się &lt;code&gt;(.toUpperCase "foo")&lt;/code&gt; (wychodząc z założenia &lt;em&gt;where Java isn't broken, Clojure doesn't fix it&lt;/em&gt;), teraz kanoniczną formą jest &lt;code&gt;(clojure.string/upper-case "foo")&lt;/code&gt;. W ten sposób Clojure stopniowo stanie się językiem niezabawkowym według &lt;a href="http://www.3ofcoins.net/2009/01/30/common-lisp-clojure-and-seriousness/"&gt;definicji Maćka Pasternackiego&lt;/a&gt;.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;ClojureScript otworzy dla Clojure zupełnie nową niszę: stanie się popularny wśród programistów-frontendowców, piszących do tej pory głównie w JavaScripcie lub CoffeeScripcie.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Równocześnie pojawienie się ClojureScript nie oznacza, że zatrzymają się prace nad „tradycyjnym” Clojure. JVM pozostanie główną platformą rozwoju języka i to tam będą pojawiać się kolejne innowacyjne cechy. Wersja 1.3 jest już w fazie beta i nadanie jej statusu &lt;em&gt;final&lt;/em&gt; jest kwestią kilku tygodni.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Patrząc wstecz, aż trudno uwierzyć, jak dużą popularność zdobyło sobie Clojure. Gdyby ktoś mi cztery lata temu powiedział, że za cztery lata pojawi się nowy język z rodziny Lisp, który będzie znacznie bardziej elegancki niż Common Lisp, znacznie praktyczniejszy niż Scheme i używać go będą wielkie instytucje finansowe, wyśmiałbym go. A jednak: zaczynam wierzyć, że prawo Kopernika jednak nie stosuje się w informatyce.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-07-17:post:jeszcze-o-matematyce-w-szkole</id>
    <title>Jeszcze o matematyce w szkole</title>
    <link href="http://plblog.danieljanus.pl/2011/07/17/jeszcze-o-matematyce-w-szkole/"/>
    <updated>2011-07-17T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Zaczęło się od &lt;a href="http://wyborcza.pl/1,95892,9882464,Nie_jestem_glupia_czy_leniwa___list.html"&gt;listu maturzystki&lt;/a&gt; opublikowanego przez Gazetę Wyborczą, zawierającego narzekanie na temat obowiązkowości matury z matematyki. Rozpętała się burza: &lt;a href="http://rewers.computerworld.pl/2011/07/03/list-do-maturzystki/"&gt;przez&lt;/a&gt; &lt;a href="http://jan.rychter.com/blog/2011/7/5/nie-musz-si-uczy-matematyki.html"&gt;Internet&lt;/a&gt; &lt;a href="http://www.playr.pl/2011/07/a-jednak-jestes-glupia-albo-leniwa-stawiam-na-to-pierwsze/"&gt;przetoczyła się&lt;/a&gt; &lt;a href="http://frizona.pl/threads/8153-Wyborcza-o-obowi%C4%85zkowej-maturze-z-matematyki"&gt;fala&lt;/a&gt; &lt;a href="http://skabanos.blogspot.com/2011/07/matematycznie-niekumaci-czyli-biednych.html"&gt;krytyki&lt;/a&gt;, w większości odsądzającej autorkę od czci i wiary. Moja własna reakcja była nieco bardziej stonowana; parę dni temu odkryłem, że została również &lt;a href="http://wyborcza.pl/1,76842,9912157,Nienawidza_humanistow____opinie_do_listu_maturzystki.html"&gt;opublikowana&lt;/a&gt; przez Wyborczą razem z innymi opiniami.&lt;/p&gt;&lt;p&gt;A dziś, szukając zupełnie czego innego, natknąłem się przypadkiem na &lt;a href="http://www.scribd.com/doc/59393251/Lament-Matematyka-Cz-1"&gt;ten artykuł&lt;/a&gt; Paula Lockharta. I zgadzam się z absolutnie każdym słowem; w swoim liście próbowałem powiedzieć mniej więcej to samo, ale nie potrafiłem ująć tego ani w jednej dziesiątej tak dobrze.&lt;/p&gt;&lt;p&gt;Przeczytajcie ten artykuł. Teraz. Uważam, że jest lekturą obowiązkową dla obu stron barykady: i antytalentów matematycznych, i ludzi twardo broniących matury z matematyki w jej obecnym kształcie.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-05-07:post:informatyka-jest-galezia-matematyki</id>
    <title>Informatyka jest gałęzią matematyki</title>
    <link href="http://plblog.danieljanus.pl/2011/05/07/informatyka-jest-galezia-matematyki/"/>
    <updated>2011-05-07T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;&lt;a href="http://geekgirls.pl/2011/05/kobieta-tworzaca-internet/"&gt;Claygirl pisze na blogu GeekGirls&lt;/a&gt;, że wbrew powszechnemu mniemaniu, informatyka to nie matematyka – i że informatykowi znajomość matematyki jest potrzebna w bardzo ograniczonym zakresie. Zdziwiłem się, bo jestem odmiennego zdania i zawsze wydawało mi się, że to właśnie pogląd, że informatyka &lt;em&gt;jest&lt;/em&gt; częścią matematyki, jest w mniejszości... Zapewne jednak większość klepaczy kodu rzadko zadaje sobie to pytanie. Szkoda, bo warto stawiać fundamentalne pytania o to, czym w istocie jest to, czym się zajmujemy.&lt;/p&gt;&lt;p&gt;Temat jest mi bliski, postaram się więc w tym wpisie uzasadnić swoją opinię. Żeby móc sensownie odpowiedzieć na nasze pytanie, powinniśmy wpierw zdefiniować, co rozumiemy przez informatykę, a co przez matematykę. Zaczniemy więc od definicji encyklopedycznych.&lt;/p&gt;&lt;h2 id="wikipedia-dixit"&gt;Wikipedia dixit&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href="http://pl.wikipedia.org/wiki/Informatyka"&gt;Informatyka&lt;/a&gt; – dyscyplina naukowa i techniczna zajmująca się przetwarzaniem informacji, a w tym technologiami przetwarzania informacji oraz technologiami wytwarzania systemów przetwarzających informacje.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Myślę, że większość z nas zgodziłaby się z takim opisem. Aplikacje webowe, bazy danych, systemy operacyjne... czymkolwiek się zajmujemy, zawsze ma to związek z przetwarzaniem informacji. A matematyka?&lt;/p&gt;&lt;p&gt;(Zanim zajrzymy do Wikipedii, zachęcam do pokuszenia się o własną definicję. Może to przyprawić o ból głowy, bo jak objąć kilkoma słowami całe to bogactwo abstrakcyjnych bytów, jakimi matematyka się zajmuje?  Liczby, zbiory, klasy, relacje, funkcje, punkty, przestrzenie, formuły, kategorie...)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href="http://pl.wikipedia.org/wiki/Matematyka"&gt;Matematyka&lt;/a&gt; – nauka dostarczająca narzędzi do otrzymywania ścisłych wniosków z przyjętych założeń, zatem dotycząca prawidłowości rozumowania.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id="ścisłość-rozumowania-i-abstrakcje"&gt;Ścisłość rozumowania i abstrakcje&lt;/h2&gt;&lt;p&gt;Ta definicja w ogóle prześlizguje się nad owym bogactwem; jest szersza, bardziej pierwotna i może budzić sprzeciw. Zdaje się sugerować, że ilekroć myślimy o czymś – &lt;em&gt;czymkolwiek&lt;/em&gt; – w sposób uporządkowany, zajmujemy się matematyką!  A jednak wielu matematyków uważa, że to właśnie w myśleniu tkwi sedno ich dziedziny. Włodek Holsztyński ujmuje to w swoim świetnym &lt;a href="http://knol.google.com/k/wlodzimierz-holsztynski/matematyka-esej/1jxfhq4x4sw0j/93#"&gt;eseju&lt;/a&gt; jeszcze zwięźlej:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Matematyka jest sztuką myślenia&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Gdzie jednak przy takim podejściu postawić granicę pomiędzy matematyką a innymi naukami?  Czy fizyka jest częścią matematyki, czy odrębną dziedziną wiedzy, która tylko korzysta z metod matematycznych? Wspominałem wcześniej o bytach abstrakcyjnych: wszyscy mamy mniej więcej zbliżone pojęcie na temat tego, czym jest liczba 7, mimo że taki obiekt nie istnieje fizycznie w otaczającym nas świecie. Wydaje się, że abstrakcyjność jest wpisana w naturę matematyki, i można ją dopisać do definicji. Wspomniany artykuł w Wikipedii zawiera inne próby zdefiniowania tej dziedziny przez sławnych matematyków, z których kilku o tym wspomina. Dirac: „Matematyka jest narzędziem stworzonym specjalnie do wszelkich abstrakcyjnych koncepcji, i nie ma ograniczeń dla jej potęgi w tym zakresie”.&lt;/p&gt;&lt;p&gt;A Poincaré mówi: „sztuka nadawania takich samych nazw różnym rzeczom”. Innymi słowy, zdolność dostrzegania podobieństw i abstrahowania od różnic. To jabłko jest czymś innym niż tamta gruszka, ale mają ze sobą coś wspólnego: liczność – tak zapewne brzmiało pierwsze, doniosłe odkrycie matematyczne.&lt;/p&gt;&lt;h2 id="matematyka-w-szkole"&gt;Matematyka w szkole&lt;/h2&gt;&lt;p&gt;...jest tylko drobnym wycinkiem wszystkich tych działów. To oczywiste, w końcu poświęca się jej ograniczoną ilość czasu. Ale za duży nacisk kładzie się na ich przedstawienie, a za mały na &lt;em&gt;rozumienie&lt;/em&gt;. Nie jest ważne, żeby wiedzieć, ile jest siedem razy dziewięć. Ważne, żeby umieć w razie potrzeby się zastanowić i dojść do prawidłowej odpowiedzi. Rzadko pokazuje się dowody twierdzeń (i potem uczniowie panicznie boją się słowa „udowodnij”), a szkoda!  Nie jest trudno zobaczyć, dlaczego twierdzenie Pitagorasa jest prawdziwe, albo dlaczego mnożenie liczb naturalnych jest przemienne. A tak właśnie ćwiczy się umiejętność &lt;em&gt;myślenia&lt;/em&gt;.&lt;/p&gt;&lt;h2 id="a-informatyka?"&gt;A informatyka?&lt;/h2&gt;&lt;p&gt;Clay pisze:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Ale całe dziesiątki poziomów abstrakcji wyżej siedzą sobie systemy operacyjne i maszyny wirtualne z którymi większość informatyków widuje się najczęściej. Informatyków – programistów języków wysokopoziomowych (takich jak Java, C#, Ruby, Python etc), którzy z matematyką muszą się znać tylko po to, żeby umieć myśleć logicznie i na przykład oszacować czas działania napisanego właśnie programu.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Już samo słowo „poziomy abstrakcji” kojarzy się z tym, o czym pisałem wcześniej. Oto mój program, mam w nim jakąś klasę.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs java"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Osoba&lt;/span&gt; {
    String imie, nazwisko;
    &lt;span class="hljs-type"&gt;int&lt;/span&gt; wiek;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ona modeluje jakiś obiekt rzeczywisty, ale sama w sobie jest równie &lt;em&gt;abstrakcyjnym&lt;/em&gt; bytem jak liczba 7. Jest napisana w języku Java -- mogę sięgnąć po formalny opis jego gramatyki i semantyki i &lt;em&gt;ściśle opisać&lt;/em&gt;, czym jest.  I wreszcie, pisząc taki kod, &lt;em&gt;pomyślałem&lt;/em&gt; o tym, co poszczególne osoby mają wspólnego i &lt;em&gt;abstrahowałem&lt;/em&gt; od nieistotnych dla mnie różnic między nimi.&lt;/p&gt;&lt;p&gt;Czy nie tym właśnie zajmuje się matematyka? W jaki sposób mój program albo język programowania jest mniej obiektem matematycznym, niż trójkąt albo zbiór {4, 42}?&lt;/p&gt;&lt;h2 id="nie-we-wszystkim-jesteśmy-dobrzy"&gt;Nie we wszystkim jesteśmy dobrzy&lt;/h2&gt;&lt;p&gt;Pomijając terminologię, trzeba się zgodzić z tezą artykułu Clay: fakt, że nie czujemy się mocni w jednej gałęzi matematyki (nazwijmy ją „licealną”), wcale nie musi oznaczać, że nie rozwiniemy skrzydeł w innej. Nikt nie jest omnibusem, nierozumienie czegoś nie jest powodem do wstydu. Srinivasa Ramanujan, bodaj największy geniusz matematyczny ubiegłego stulecia, wniósł gigantyczny wkład do teorii liczb i wielu innych dziedzin... ale nie miał zielonego pojęcia, co to jest funkcja zmiennej zespolonej!&lt;/p&gt;&lt;p&gt;Tak i my nie musimy tego wiedzieć, pisząc kod czy grzebiąc w bebechach systemu. Miejmy jednak świadomość, że w istocie uprawiamy matematykę, która jest sztuką myślenia – i cieszmy się tym.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-04-19:post:wrazenia-z-j4p-2011</id>
    <title>Wrażenia z java4people 2011</title>
    <link href="http://plblog.danieljanus.pl/2011/04/19/wrazenia-z-j4p-2011/"/>
    <updated>2011-04-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Jest niedzielny poranek, siedzę w pociągu relacji Szczecin–Warszawa i właśnie wyciągnąłem laptopa, aby spisać wrażenia z trzeciej edycji konferencji &lt;a href="http://java4people.com"&gt;java4people&lt;/a&gt;, która odbyła się wczoraj w Szczecinie, póki jeszcze nie okrzepły i nie rozmyły się.&lt;/p&gt;&lt;p&gt;Zanim opowiem o poszczególnych wystąpieniach, winienem oddać zasłużone gratulacje organizatorom, czyli szczecińskiemu JUG-owi. Przestronne, nowoczesne aule Zachodniopomorskiego Uniwersytetu Technologicznego, dostępne przez cały czas ciastka i herbata, obiad (załapałem się na dokładkę!), after-party, a nade wszystko ciekawe prezentacje i rozmowy kuluarowe – wszystko to stwarzało znakomitą atmosferę sprzyjającą temu, aby posłuchać o nowych językach i technologiach wokół Javy i JVM oraz „dzielić się wiedzą i pasją”, jak głosi motto konferencji. Wielkie brawa!&lt;/p&gt;&lt;p&gt;Jedyny niedosyt polegał na tym, że prezentacje poprowadzone były w dwóch równoległych blokach, a do tego w trzecim bloku trwały warsztaty z SWT/JFace. Momentami bardzo doskwierał mi brak możliwości bilokacji, ale trudno winić o to organizatorów.&lt;/p&gt;&lt;p&gt;Oficjalnego otwarcia dokonał prof. Antoni Wiliński, dziekan Wydziału Informatyki ZUT, życząc uczestnikom owocnej wymiany doświadczeń. Następnie część ludzi udała się posłuchać wystąpienia Tomka Kopacza o .NET i Javie, ja zaś zadebiutowałem w roli prelegenta na imprezie tego formatu, opowiadając o Clojure.&lt;/p&gt;&lt;p&gt;Trudno mi wypowiadać się o własnej prezentacji – nie jestem dobrym mówcą i to &lt;a href="http://pacykarz.blogspot.com/2011/04/jawnosc-dla-ludu-s03e02-reminesencje.html"&gt;było&lt;/a&gt; &lt;a href="http://blog.kedziorski.pl/2011/04/18/java4people/"&gt;widać&lt;/a&gt;.  Jak się jednak okazało z rozmów ze słuchaczami, udało się zainteresować część osób tematem. Sukces jest więc połowiczny, a postaram się, aby następnym razem było lepiej. Slajdy można znaleźć &lt;a href="http://danieljanus.pl/slides/j4p/"&gt;tu&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Potem wybrałem się na wystąpienie Przemka Pokrywki o Scali. Scala bywa wymieniana jednym tchem z Clojure, gdy mowa o nowych językach funkcyjnych działających na JVM, więc byłem bardzo ciekaw tego wykładu. I nie zawiodłem się: z Przemka wprost emanowała pasja, znać było też dużą wiedzę i doświadczenie w temacie. Przemek ciekawie pokazał, w jaki sposób Scala łączy paradygmat funkcyjny z obiektowym -- w tym drugim zakresie przypomina mi Smalltalka: tu też wszystko jest obiektem i &lt;code&gt;2 + 3&lt;/code&gt; oznacza zawołanie na obiekcie &lt;code&gt;2&lt;/code&gt; metody &lt;code&gt;+&lt;/code&gt; z parametrem &lt;code&gt;3&lt;/code&gt;.  Fajnym mechanizmem wydaje się też możliwość definiowania własnych niejawnych konwersji – to sposób na wzbogacanie już istniejących klas o nowe funkcjonalności, ale bez zmiany tych klas; przypomina to trochę Clojurowe protokoły i &lt;code&gt;extend-type&lt;/code&gt;. Dopisuję do listy rzeczy do wypróbowania.&lt;/p&gt;&lt;p&gt;Po obiedzie, zachęcony tytułem, wybrałem się na prezentację „Skalowalność technologii Javowych w zastosowaniach komercyjnych” Dawida Gruszczyńskiego. Organizatorzy konferencji na samym początku rozdali ankiety, w których prosili o ocenienie każdego wystąpienia w skali od 1 (szkoda czasu) do 5 (naprawdę warto) – po tej prezentacji dały się słyszeć rozmowy, że należałoby dać jej 0 albo i -1. Ja jestem łagodniejszy w swych ocenach. Bezsprzecznie prezentacja była źle zatytułowana: tytuł obiecywał opowieść o technikach czy narzędziach projektowania skalowalnych aplikacji; tymczasem okazało się to klasycznym &lt;em&gt;success story&lt;/em&gt; dotyczącym systemu obsługi spisu powszechnego, w tym oprogramowania działającego na terminalach używanych przez rachmistrzów.&lt;/p&gt;&lt;p&gt;Czy ta prezentacja była nie na miejscu na takiej konferencji? Może i tak. Mam jednak słabość do &lt;em&gt;success stories&lt;/em&gt; i duży szacunek do ludzi, którym udaje się stworzyć działające, niezawodne oprogramowanie, nawet jeśli nie zgadzam się z wyborem technologii. To naprawdę spora rzecz. A samej prelekcji trzeba oddać, że była sprawnie przeprowadzona, slajdy zrozumiałe, a diagramy prezentujące architekturę systemu – czytelne. Wątpliwości słuchaczy budziła kwestia bezpieczeństwa transmisji danych (i samych rachmistrzów!) Ciekawostka: terminale rachmistrzowskie mają zmienne IP, ale nie powiadamiają serwera co chwila o swoim aktualnym adresie, bo wywoływałoby to zanadto duże obciążenie. Co więc robi terminal, gdy zmieni mu się adres? Otóż... wysyła SMS (!) ze swoim aktualnym adresem!&lt;/p&gt;&lt;p&gt;Na wystąpieniu Sebastiana Pietrowskiego o Jythonie (a także samym Pythonie i Django) nie dowiedziałem się wiele nowego, z racji wcześniejszych swoich doświadczeń z Pythonem. Ale też celem tej prezentacji było chyba nie tyle przekazanie wiedzy, co podzielenie się własnymi doświadczeniami, z subiektywnego punktu widzenia – i to udało się Sebastianowi znakomicie. Stąd slajdy zatytułowane „What I Like”, „What I Don’t Like”, „What I’d Use Python For” – bardzo pragmatyczne. Sebastian opowiadał między innymi o tym, że Jythona można fajnie wykorzystać jako narzędzie do eksploracji nowych API z REPL-a: używając pythonowej introspekcji można łatwo obejrzeć listę pól i metod udostępnianych przez dowolną klasę. W swoim wystąpieniu o tym nie opowiadałem, ale w pakiecie repl-utils w Clojure Contrib jest funkcja &lt;code&gt;show&lt;/code&gt;, która daje bardzo podobny efekt.&lt;/p&gt;&lt;p&gt;Słuchając wystąpienia Bartka Kuczyńskiego o Vaadinie utwierdziłem się w przekonaniu, że nie chcę pisać aplikacji webowych tak jak okienkowe, na modłę swingową.  Vaadin ma w zamierzeniu służyć właśnie do tego: to nadbudowany nad GWT modularny framework WWW.  Bartek pokazywał dziejącą się pod spodem magię, wraz z protokołem, którym część kliencka kompilowana do JS komunikuje się z serwerową (UIDL – dobrze pamiętam? – i JSON z danymi do wyświetlenia, poprzedzonymi ni mniej ni więcej tylko pętlą nieskończoną &lt;code&gt;for(;;);&lt;/code&gt;, co podobno ma jakiś sens). Psikus w tym, że ostatnio w &lt;a href="http://smyrna.danieljanus.pl"&gt;Smyrnie&lt;/a&gt; zrobiłem dokładnie odwrotną rzecz, to znaczy napisałem aplikację &lt;em&gt;desktopową&lt;/em&gt; tak jakbym pisał webową, a więc używając ajaksowego XML-RPC (odsyłam do niedawnego &lt;a href="http://plblog.danieljanus.pl/smyrna"&gt;postu na ten temat&lt;/a&gt;), i wyszło na oko prościej niż w Vaadinie. Może opowiem o tym na Warszawa JUG?&lt;/p&gt;&lt;p&gt;Nie zdążyłem się obejrzeć, a to już koniec konferencji! Do dotychczasowych bonusów dołączył kolejny: wśród wypełniających ankiety organizatorzy rozlosowali pięć książek. Los wytypował mnie jeszcze raz i schowałem do plecaka „Domain-Specific Languages” Martina Fowlera.&lt;/p&gt;&lt;p&gt;Pokonferencyjne after-parties bywają bodaj ważniejszymi nawet elementami takich spotkań niż same prezentacje. To tam toczy się najwięcej rozmów, to tam zawiązują się nowe znajomości i trwają nieskrępowane, długie dyskusje. Tak bywa z pizzą na &lt;a href="http://aulapolska.pl"&gt;Auli&lt;/a&gt;, tak było i teraz. Wielkie dzięki raz jeszcze organizatorom, wszystkim, których poznałem, z którymi udało mi się porozmawiać, i tym, którzy przebrnęli przez moje wystąpienie.  Do zobaczenia przy innej okazji!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-03-14:post:nadchodzi-wayland</id>
    <title>Nadchodzi Wayland</title>
    <link href="http://plblog.danieljanus.pl/2011/03/14/nadchodzi-wayland/"/>
    <updated>2011-03-14T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Podczas jednego z ostatnich surfathlonów rzuciła mi się w oczy informacja o nowym kawałku uniksowego oprogramowania. Rozwijany bez wielkiego rozgłosu i dziś niemal nieznany, za kilka lat prawdopodobnie będzie jednym z kluczowych elementów absolutnie każdego biurkowego Linuksa. Mowa o serwerze wyświetlania &lt;a href="http://wayland.freedesktop.org/"&gt;Wayland&lt;/a&gt;, który ma wielką szansę stać się sensowną alternatywą dla &lt;a href="http://x.org"&gt;X Window System&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;X-y mają jedną kluczową zaletę. To wielki kawał oprogramowania bardzo intensywnie testowanego od dziesiątków lat, który działa. A jednak ilekroć uruchamiam &lt;code&gt;ps ax&lt;/code&gt; lub &lt;code&gt;top&lt;/code&gt; i widzę działający na moim komputerze, zżerający kilka procent czasu procesora i kilkanaście procent pamięci serwer X, mierzi mnie, gdy pomyślę, co tam się dzieje pod spodem.&lt;/p&gt;&lt;p&gt;X Window System narodził się jako protokół wyświetlania, pomyślany z wielkim rozmachem na początku lat 80. tak, aby w pełni wykorzystać moc ówczesnych stacji roboczych z wielkimi, monochromatycznymi wyświetlaczami. Bezstanowy model wyświetlania X jest już na starcie bardzo skomplikowany. Żeby zrobić coś tak prostego jak narysowanie kreski na ekranie, trzeba kolejno: otworzyć połączenie do serwera X; utworzyć okienko; zamapować je; utworzyć maskę notyfikacji; zaczekać na zdarzenie powiadamiające, że można rysować; wywołać właściwą funkcję rysującą kreskę; wyczyścić bufor połączenia (jak to opisuje &lt;a href="http://tronche.com/gui/x/xlib-tutorial/"&gt;ten tutorial&lt;/a&gt;). System czcionek bitmapowych, wiele sposobów określania współrzędnych ekranowych, wielki zestaw prymitywów graficznych, biblioteki Athena czy Xt – wszystko to jest bagażem historycznym, z którego współczesne oprogramowanie już nie korzysta, a mimo to każdy Linux przychodzi z całym tym dobrodziejstwem inwentarza. Dwa wzajemnie niekompatybilne podsystemy renderowania tekstu? Taka jest rzeczywistość. A próbowaliście kiedyś zapanować nad tym w czystym C? &lt;a href="http://www.pps.jussieu.fr/~jch/software/XinC.html"&gt;Powodzenia.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Do tego X przez lata obrastał w sadełko w postaci różnych rozszerzeń protokołu, jak XRandR czy XRENDER, potrzebnych do jako takiego sensownego działania. Współczesne pulpity z bajeranckimi efektami jak falujące okna czy choćby półprzezroczyste terminale nie byłyby bez nich możliwe.&lt;/p&gt;&lt;p&gt;Przezroczystość sieciowa, powiadacie? Tak, ale spróbujcie zdalnie uruchomić glxgears albo pograć w Quake'a po sesji X. Nawet Firefox, gdy go uruchamiam przez &lt;code&gt;ssh -X&lt;/code&gt; po szybkim łączu na serwerze oddalonym o kilka kilometrów od domu, ślimaczy się i jest nieużywalny. Kwestię zdalnego uruchamiania aplikacji graficznych znacznie lepiej rozwiązał Microsoft ze swoim Remote Desktop.&lt;/p&gt;&lt;p&gt;Wayland ma szansę uprościć cały okołoiksowy bałagan. Jest napisany tak, by mógł wykorzystać sterowniki dla nowych kart graficznych, już istniejące dla &lt;a href="http://X.org"&gt;X.org&lt;/a&gt;. Ma bardzo prosty model wyświetlania: to nie sam serwer wyświetla grafikę, ale po prostu udostępnia klientom swoje bufory. Współczesne biblioteki, jak Cairo, GTK+ czy Qt, już potrafią z niego korzystać. Chciałoby się powiedzieć: nareszcie!&lt;/p&gt;&lt;p&gt;Z niecierpliwością czekam dnia, w którym na żadnej z moich linuksowych maszyn nie będzie działał żaden serwer X.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-02-28:post:sen-elektryka</id>
    <title>Przeczytane: Sen elektryka (Wiesław Myśliwski, „Traktat o łuskaniu fasoli”)</title>
    <link href="http://plblog.danieljanus.pl/2011/02/28/sen-elektryka/"/>
    <updated>2011-02-28T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Mówi się czasem, że zapamiętujemy wszystko, co rejestrują nasze zmysły. Nawet jeśli wydaje się, że jakieś mało z pozoru znaczące zdarzenie z przeszłości odeszło w zapomnienie – bywa, że ni stąd ni zowąd przypominamy je sobie pod wpływem nowych zmysłowych doznań albo skojarzeń. I właśnie czegoś takiego ostatnio doświadczyłem, odłożywszy na półkę właśnie przeczytany „Traktat o łuskaniu fasoli” Wiesława Myśliwskiego.&lt;/p&gt;&lt;p&gt;To było jakieś cztery, może pięć lat temu; siedziałem wtedy w którymś ze stołecznych Sphinxów, jedząc obiad. Między jednym a drugim kęsem moje uszy wychwyciły znaną melodię, której zdecydowanie nie spodziewałem się usłyszeć w radiu „pod kotleta”. Pink Floyd. Który utwór Floydów może puszczać popularna rozgłośnia? „Money”? „Another Brick In The Wall, Part II”? Może od wielkiego dzwonu „Wish You Were Here”? Nie, to był utwór z jednego z najposępniejszych, najmniej podkotletowo wtapiających się w tło albumów (a przy okazji mojego ulubionego).  „The Gunner’s Dream” z płyty „The Final Cut”.&lt;/p&gt;&lt;p&gt;Jest taki moment w tym utworze, kiedy przeciągłe „...and hold on to the dream” Watersa zanika przechodząc w długie solo saksofonowe. I właśnie wtedy usłyszałem ten saksofon inaczej. Do tej pory wydawało mi się, że harmonijnie współbrzmi z resztą, teraz jednak wybijał się, skrzeczał, zaznaczał się niespokojnym dysonansem. Mniej miło, ale może bardziej pasująco do nastroju utworu. Niepokój, niejasne wrażenie czegoś utraconego, co już nie istnieje. Ot, szybko wietrzejąca emocja, ale w uśpieniu przetrwała lata.&lt;/p&gt;&lt;p&gt;I obudziła się z ostatnią stroną „Traktatu...”.&lt;/p&gt;&lt;p&gt;Blurb na okładce mówi o „drążeniu Tajemnicy”, o „nieszukaniu łatwych rozwiązań”, o „metafizyce”. Może tak, ale nie to zapamiętam. Zwłaszcza że sama fabuła nie jest zawiła: oto u starszego pana zjawia się przybysz poszukujący fasoli do kupienia; fasolę ma ów pan tylko niełuskaną, więc zaprasza nieznajomego do domu i podczas całodziennego wspólnego łuskania snuje monolog – opowieść o sobie. O wspomnieniach z dzieciństwa, o rzece i lampie naftowej. O strasznych doświadczeniach wojny. O trudnych powojennych czasach odbudowy. O ciężkiej pracy elektryka i, tak, tak, o saksofonach. A także o kapeluszach i psach. Jest ten monolog bardzo naturalny i pozbawiony śladu pretensjonalności (i w tym tkwi jego siła: przywodzi mi na myśl równie mocne i proste „Requiem” Achmatowej), a mimo to zmusza do myślenia i zadawania pytań.&lt;/p&gt;&lt;p&gt;Jeśli przyjąć definicję prozy Włodka Holsztyńskiego (parafrazuję: proza to taki tekst literacki, w którym autor podaje 90% obrazu, zostawiając czytelnikowi zbudowanie sobie pozostałych 10%; ta definicja stawia prozę w opozycji do poezji, w której ta proporcja jest pół na pół – te procenty są oczywiście umowne, ale dają pewne wyobrażenie o roli czytelnika), to na tej skali „Traktat...” sytuuje się gdzieś pomiędzy. Bo jest niedopowiedziany, bo budzi wątpliwości, bo pozwala zajrzeć do niedookreślonego świata, który nie jest moim światem.&lt;/p&gt;&lt;p&gt;Ale podoba mi się. Bardzo polecam.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-02-13:post:janusowie-z-deblina</id>
    <title>Janusowie z Dęblina?</title>
    <link href="http://plblog.danieljanus.pl/2011/02/13/janusowie-z-deblina/"/>
    <updated>2011-02-13T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Pisze do mnie Nan Cain z Florissant, Missouri (cytuję w tłumaczeniu):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Mój dziadek, Walenty Janus, urodził się w Polsce w roku 1890. Przybył do Stanów Zjednoczonych w młodym wieku, osiadł w stanie Michigan i ożenił się z Sophie Hojnacki. Próbuję dowiedzieć się czegoś więcej o jego rodzinie (być może jego ojciec miał na imię Franciszka [sic!]). W dokumentach imigracyjnych jako miejscowość pochodzenia figuruje Giblin, ale nie mogę znaleźć miejscowości w Polsce o tej nazwie.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Niewiele mi to mówi. Janus to dość popularne nazwisko; na stronie &lt;a href="http://www.futrega.org/etc/nazwiska.html"&gt;Marka Futregi&lt;/a&gt; widzę, że nosi je około 10 tys. osób w Polsce i akurat wśród znanych mi Janusów nie było żadnego Walentego, który by wyemigrował do USA. Wydaje mi się, że "Giblin" może być amerykańsko-fonetycznym zapisem Dęblina – tak mógł usłyszeć oficer imigracyjny i tak zapisać.&lt;/p&gt;&lt;p&gt;A publikuję ten post w nadziei, że może ktoś z czytelników tego bloga wie coś o Walentym i mógłby podzielić się informacją. Będę wdzięczny za wiadomość (najlepiej w komentarzach do niniejszego wpisu).&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-02-12:post:smyrna</id>
    <title>Smyrna</title>
    <link href="http://plblog.danieljanus.pl/2011/02/12/smyrna/"/>
    <updated>2011-02-12T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Dziś chciałbym zaprezentować kolejny stworzony przeze mnie przykład wykorzystania Clojure w praktyce – program &lt;a href="http://smyrna.danieljanus.pl"&gt;Smyrna&lt;/a&gt;. To proste narzędzie do przeszukiwania korpusów: umożliwia łatwe zindeksowanie zbioru dokumentów w formacie HTML, wyszukanie wystąpień interesującego nas leksemu i stworzenie listy frekwencyjnej słów.&lt;/p&gt;&lt;img src="/img/blog/smyrna.png"&gt;
&lt;p&gt;Program zrodził się z potrzeby chwili (potrzebowałem porównać wystąpienia pewnej grupy słów w różnych zestawach danych), ale uznałem, że jest na tyle użyteczny, że może przydać się nie tylko mnie. Zwłaszcza że nie było dotąd programu na wolnej licencji, który by umożliwiał &lt;em&gt;łatwe&lt;/em&gt; przeszukiwanie własnych zbiorów polskich tekstów. Można wykorzystać &lt;a href="http://poliqarp.sf.net"&gt;Poliqarpa&lt;/a&gt;, w którym też maczałem palce (a raczej nurzałem ręce), jednak używanie go z własnymi zbiorami danych wymaga ekwilibrystyki w stylu &lt;a href="http://github.com/nathell/dxces"&gt;dxces&lt;/a&gt; i jest trudne do przeskoczenia dla nietechnicznych użytkowników. Jest więc Smyrna swoistym uzupełnieniem dla Poliqarpa – &lt;a href="http://pl.wikipedia.org/wiki/Polikarp_ze_Smyrny"&gt;stąd zresztą nazwa&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Z technicznego punktu widzenia interesujący może być sposób, w jaki skonstruowałem interfejs użytkownika. Mimo że Smyrna jest w zasadzie aplikacją desktopową, obsługuje się ją przez przeglądarkę WWW: uruchamiany jest lokalny serwerek HTTP na porcie 8080, a następnie przeglądarka z tym adresem; cała dalsza komunikacja między JavaScriptowym kodem klienckim a Clojurowym silnikiem przeszukującym odbywa się przez JSON-RPC.&lt;/p&gt;&lt;p&gt;W ramach ćwiczenia, kliencki kod napisałem w nowej, alternatywnej składni dla JavaScriptu, czyli zyskującym coraz większą popularność &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;CoffeeScripcie&lt;/a&gt;. Eksperyment uważam za udany: kod wychodzi czytelniejszy i zwięźlejszy niż w "zwykłym" JS.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-31:post:czyszczenie-dowiazan-lokalnych</id>
    <title>Clojure: czyszczenie dowiązań lokalnych</title>
    <link href="http://plblog.danieljanus.pl/2011/01/31/czyszczenie-dowiazan-lokalnych/"/>
    <updated>2011-01-31T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;O jednej z nowych cech Clojure 1.2 dowiedziałem się dopiero niedawno, bo jej wprowadzenie przeszło właściwie bez echa. Może dlatego, że to optymalizacja niewidoczna na zewnątrz; jednak rozwiązuje ważny problem i dlatego warto mieć świadomość jej istnienia. Chodzi o &lt;em&gt;locals clearing&lt;/em&gt;, które tłumaczę na polski jako „czyszczenie lokalnych dowiązań”.&lt;/p&gt;&lt;p&gt;Wyobraźmy sobie taką sytuację: mamy do wykonania skomplikowane obliczenie. Możemy je podzielić na mniejsze kroki; na każdym etapie wyliczamy nowy pośredni wynik na podstawie tylko niewielu ostatnich kroków – jednego, może dwóch. Załóżmy jeszcze, że te pośrednie wyniki są dużych rozmiarów: powiedzmy setek megabajtów. To częsty przypadek, kiedy wykonuje się np. obliczenia na dużych, gęstych macierzach: najpierw normalizujemy każdy z wektorów naszej macierzy, potem ją transponujemy, następnie liczymy jej rozkład własny i odrzucamy część wektorów własnych.&lt;/p&gt;&lt;p&gt;W Javie wyglądałoby to tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs java"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title function_"&gt;calculate&lt;/span&gt;&lt;span class="hljs-params"&gt;(Matrix m)&lt;/span&gt; {
    &lt;span class="hljs-type"&gt;Matrix&lt;/span&gt; &lt;span class="hljs-variable"&gt;m1&lt;/span&gt; &lt;span class="hljs-operator"&gt;=&lt;/span&gt; step1(m);
    &lt;span class="hljs-type"&gt;Matrix&lt;/span&gt; &lt;span class="hljs-variable"&gt;m2&lt;/span&gt; &lt;span class="hljs-operator"&gt;=&lt;/span&gt; step2(m1);
    &lt;span class="hljs-comment"&gt;// i tak dalej, każdy krok odwołuje się do poprzedniego&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to jednak prosta droga do pojawienia się &lt;code&gt;OutOfMemoryError&lt;/code&gt;, o ile nie dysponujemy wystarczającą ilością pamięci, aby pomieścić &lt;em&gt;wszystkie&lt;/em&gt; kroki. Gdy &lt;code&gt;m2&lt;/code&gt; jest obliczone, wartość &lt;code&gt;m1&lt;/code&gt; jest już niepotrzebna i w zasadzie można by zwolnić zajmowaną przez nią pamięć. Odśmiecacz jednak tego nie zrobi, dopóki wywołanie funkcji &lt;code&gt;calculate&lt;/code&gt; nie zakończy się, ponieważ do tej pamięci odwołuje się ramka na stosie wywołań.&lt;/p&gt;&lt;p&gt;Zilustrujmy to uruchamialnym przykładem:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs java"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;foo&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] calculate(&lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] lastStep) {
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;byte&lt;/span&gt;[&lt;span class="hljs-number"&gt;10485760&lt;/span&gt;];
    }

    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title function_"&gt;main&lt;/span&gt;&lt;span class="hljs-params"&gt;(String... args)&lt;/span&gt; {
        &lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] a, b, c, d;
        a = calculate(&lt;span class="hljs-literal"&gt;null&lt;/span&gt;);
        b = calculate(a);
        c = calculate(b);
        d = calculate(c);
        System.out.println(&lt;span class="hljs-string"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Funkcja &lt;code&gt;calculate&lt;/code&gt; emuluje skomplikowane obliczenia, alokując za każdym razem 10-megabajtowy blok pamięci. Jeśli skompilujemy ten program i uruchomimy go w środowisku z maksymalnym rozmiarem sterty ograniczonym do 30 MB (&lt;code&gt;java -Xmx30M foo&lt;/code&gt;), zobaczymy błąd braku pamięci.&lt;/p&gt;&lt;p&gt;Inaczej jest w Clojure. Odpowiednik powyższego programu wygląda tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;calculate&lt;/span&gt; [last-step]
  (&lt;span class="hljs-name"&gt;make-array&lt;/span&gt; Byte/TYPE &lt;span class="hljs-number"&gt;10485760&lt;/span&gt;))

(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;-main&lt;/span&gt; [&amp;amp; args]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [a (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; &lt;span class="hljs-literal"&gt;nil&lt;/span&gt;)
        b (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; a)
        c (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; b)
        d (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; c)]
    (&lt;span class="hljs-name"&gt;println&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W Clojure 1.2 i nowszych ten program wypisze &lt;code&gt;OK&lt;/code&gt;, nawet gdy będzie miał do dyspozycji tylko 30 MB pamięci.&lt;/p&gt;&lt;p&gt;Dlaczego to działa? Okazuje się, że kompilator Clojure przeprowadza statyczną analizę każdego kawałka kodu, w którym wartości są dowiązane do nazw (czyli każdego użycia formuły &lt;code&gt;let&lt;/code&gt;), i dla każdego dowiązania sprawdza, w którym miejscu jest ono ostatni raz używane, biorąc pod uwagę wszystkie możliwe ścieżki wykonania. Natychmiast po ostatnim użyciu emitowany jest bajtkod zerujący odpowiednią zmienną (ustawiający wskaźnik na &lt;code&gt;null&lt;/code&gt;). Innymi słowy, efekt jest taki, jakbyśmy w przykładowym kodzie w Javie wstawili instrukcję &lt;code&gt;a = null;&lt;/code&gt; zaraz po obliczeniu &lt;code&gt;b&lt;/code&gt; i analogicznie dalej.&lt;/p&gt;&lt;p&gt;O jednej z sytuacji, w których takie czyszczenie się przydaje, już powiedziałem. Innym częstym przypadkiem jest branie za punkt wyjścia długiej (być może nawet nieskończonej) leniwej sekwencji, która jest realizowana w kolejnych krokach obliczeń. Dzięki automatycznemu czyszczeniu odśmiecacz może pozbyć się pierwszych, niepotrzebnych już elementów zrealizowanej sekwencji, co umożliwia pisanie klarownego kodu bez troszczenia się o jego poprawność pamięciową.&lt;/p&gt;&lt;p&gt;Jak jednak &lt;a href="https://groups.google.com/d/msg/clojure/Xmu3pUMgiJk/Lx1Sq462h4wJ"&gt;napisał mi Rich Hickey&lt;/a&gt;, sama alokacja pamięci w momencie wiązania nazwy &lt;em&gt;nie jest&lt;/em&gt; uważana za użycie tej nazwy. Stąd jeśli nazwa nigdy nie zostanie użyta, to odpowiadająca jej wartość nie zostanie też odśmiecona aż do opuszczenia ciała bloku &lt;code&gt;let&lt;/code&gt;. Dlatego też nie mogłem pominąć w powyższym przykładzie argumentu &lt;code&gt;last-step&lt;/code&gt; funkcji &lt;code&gt;calculate&lt;/code&gt;.  Jednak taki przypadek nie pojawia się w praktyce: wszak jeśli wyliczamy jakiś wynik, to nie po to, by go do niczego nie użyć!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-19:post:pokoloruj-sobie-europe</id>
    <title>Pokoloruj sobie Europę!</title>
    <link href="http://plblog.danieljanus.pl/2011/01/19/pokoloruj-sobie-europe/"/>
    <updated>2011-01-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;To jest druga część minicyklu zapoczątkowanego &lt;a href="http://plblog.danieljanus.pl/zippery-w-clojure"&gt;artykułem o zipperach&lt;/a&gt;. Temat ten zaprezentowałem również podczas wystąpienia na spotkaniu &lt;a href="http://warszawa.jug.pl"&gt;Warszawa JUG&lt;/a&gt;. Dostępne są &lt;a href="http://danieljanus.pl/slides/2010/wjug-clojure/"&gt;slajdy z tej prezentacji&lt;/a&gt; (&lt;em&gt;uwaga:&lt;/em&gt; slajdy mają postać oskryptowanego HTML; należy je oglądać w Firefoksie lub którejś przeglądarce WebKitowej). Można również obejrzeć &lt;a href="http://jaceklaskowski.blip.tv/file/4573404/"&gt;nagranie wideo&lt;/a&gt; wystąpienia, niestety ze słabą jakością dźwięku.&lt;/p&gt;&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&lt;p&gt;Jakiś czas temu zostałem poproszony o sporządzenie kilku mapek Europy pokolorowanych w różny sposób. Dostałem kilka zestawów danych, w których każdemu krajowi UE przypisana była jakaś liczba: im większa ta wartość, tym ciemniejszym odcieniem państwo miało być zaznaczone na mapce. Przykładowa pokolorowana mapka miała wyglądać tak:&lt;/p&gt;&lt;img src="/img/blog/europa.png"&gt;
&lt;p&gt;Rozpocząłem od ściągnięcia łatwo edytowalnej &lt;a href="http://commons.wikimedia.org/wiki/File:Blank_map_of_Europe.svg"&gt;mapy&lt;/a&gt; ze stron Wikimedia Commons, obliczyłem potrzebne intensywności kolorów dla pierwszego zestawu, uruchomiłem &lt;a href="http://www.inkscape.org"&gt;Inkscape&lt;/a&gt; i zabrałem się do kolorowania. Po półgodzinie żmudnego klikania zdałem sobie sprawę, że prościej i szybciej byłoby mi napisać programik w Clojure, który wygeneruje mapkę za mnie. Okazało się to zadaniem dość prostym: reszta tego postu będzie rekonstrukcją przedsięwziętych przeze mnie kroków.&lt;/p&gt;&lt;h2 id="svg"&gt;SVG&lt;/h2&gt;&lt;p&gt;Formatem źródłowego obrazka jest SVG. Wiedziałem, że to XML-owy otwarty format grafiki wektorowej, często widywałem na Wikipedii obrazy w tym formacie – jednak jego ręczna edycja to coś, z czym nie miałem do tej pory do czynienia. Szczęśliwie okazało się, że obrazek z mapkami ma prostą strukturę. Krzywa-obwiednia każdego kraju jest opisana elementem &lt;code&gt;path&lt;/code&gt;, wyglądającym mniej więcej tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;path&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;id&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;eu europe&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;d&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;współrzędne kolejnych węzłów krzywej (bardzo dużo)&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Warto zwrócić uwagę na atrybut &lt;code&gt;id&lt;/code&gt; – jest to dwuliterowy skrót ISO-3166-1-ALPHA2 nazwy kraju. Na początku kodu obrazka znajduje się zresztą komentarz, który wyjaśnia użyte konwencje nazewnicze. Dysponowanie tak porządnie przygotowanymi danymi wejściowymi znakomicie ułatwiło mi pracę.&lt;/p&gt;&lt;p&gt;Jak się okazuje, SVG, podobnie jak HTML, &lt;a href="http://www.w3.org/TR/SVG/styling.html"&gt;używa stylów CSS&lt;/a&gt; do definiowania wyglądu elementów. Wszystko, co trzeba zrobić do nadania Polsce koloru czerwonego, to nadanie odpowiedniej wartości atrybutowi stylu &lt;code&gt;fill&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;path&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;id&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;style&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;fill: #ff0000;&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;eu europe&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;d&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;współrzędne kolejnych węzłów krzywej (bardzo dużo)&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Uzbrojeni w taką wiedzę, możemy rozpocząć pracę.&lt;/p&gt;&lt;h2 id="xml-w-clojure"&gt;XML w Clojure&lt;/h2&gt;&lt;p&gt;Podstawowym sposobem obsługi XML w Clojure jest wykorzystanie biblioteki &lt;code&gt;clojure.xml&lt;/code&gt;, pozwalającej na parsowanie XML-a (na zasadzie DOM, tzn. do struktury w pamięci) oraz serializację, tzn. działanie w odwrotną stronę. Uruchommy więc REPL i zacznijmy od wczytania naszej mapki i sparsowania jej:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;use&lt;/span&gt;&lt;/span&gt; &amp;#x27;clojure.xml)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m&lt;/span&gt; (&lt;span class="hljs-name"&gt;parse&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/home/nathell/eur/Blank_map_of_Europe.svg&amp;quot;&lt;/span&gt;))
[...dłuuugie oczekiwanie...]
Unexpected end of file from server
  [Thrown class java.net.SocketException]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zaraz! Jaki znowu &lt;code&gt;SocketException&lt;/code&gt;? Firefox wyświetla tę mapkę poprawnie, Chromium też, WTF? W tak świetnym języku, jakim jest Clojure, wszystko powinno działać od ręki?!&lt;/p&gt;&lt;p&gt;Cóż, język jest tak dobry, jak dobre są jego biblioteki – a jeśli chodzi o Clojure, można to rozwinąć: biblioteki Clojure są tak dobre, jak dobre są biblioteki Javy, których używają pod spodem. W tym wypadku natrafiliśmy na cechę standardowego (z pakietu &lt;code&gt;javax.xml&lt;/code&gt;) parsera XML w Javie. Jest on restrykcyjny i stara się odrzucać dokumenty, które nie są poprawne (w sensie &lt;em&gt;valid&lt;/em&gt;, a nie tylko &lt;em&gt;well-formed&lt;/em&gt;). Jeśli tylko dany plik zawiera deklarację &lt;code&gt;DOCTYPE&lt;/code&gt;, to parser javowy, a za nim &lt;code&gt;clojure.xml/parse&lt;/code&gt;, próbuje ściągnąć z podanego adresu schemat DTD i zwalidować dokument według tego schematu. Jest to działanie z wielu względów niepożądane, zwłaszcza z punktu widzenia &lt;a href="http://www.w3.org"&gt;World Wide Web Consortium&lt;/a&gt;, bo to na serwerach tej organizacji przechowywane są schematy opisujące sieciowe standardy. Łatwo sobie wyobrazić, jak duży ruch sieciowy generują nadgorliwe parsery: traktuje o tym &lt;a href="http://www.w3.org/blog/systeam?cat=68"&gt;post na blogu W3C&lt;/a&gt;. Zapewne wielu programistów Javy zetknęło się kiedyś z tym problemem. Jest kilka rozwiązań; użyjemy najprostszego z nich, czyli po prostu... ręcznie usuniemy przeszkadzającą deklarację &lt;code&gt;DOCTYPE&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m&lt;/span&gt; (&lt;span class="hljs-name"&gt;parse&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/home/nathell/eur/bm.svg&amp;quot;&lt;/span&gt;))
#&amp;#x27;user/m
&amp;gt; m
[...wiele ekranów liczb...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No, tym razem udało się wczytać. Obejrzenie sparsowanej struktury jest utrudnione z uwagi na jej wielkość (można było się tego spodziewać: plik SVG waży ponad 0,5 MB!), ale z samego początku tego, co nam wypluwa REPL, widzimy, że jest to mapa. Zobaczmy, z jakich kluczy się składa:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;keys&lt;/span&gt;&lt;/span&gt; m)
(&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to więc mapa o trzech polach: z nazw (albo z dokumentacji) można się domyślić, co zawierają. Pole &lt;code&gt;:tag&lt;/code&gt; to nazwa elementu XML, &lt;code&gt;:attrs&lt;/code&gt; jest mapą atrybutów tego elementu, a &lt;code&gt;:content&lt;/code&gt; – sekwencją jego podelementów, z których każdy jest znów reprezentowany przez mapę o takiej samej strukturze (względnie przez napis, jeśli jest to element napisowy):&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; m)
&lt;span class="hljs-symbol"&gt;:svg&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; m)
{&lt;span class="hljs-symbol"&gt;:xmlns&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;http://www.w3.org/2000/svg&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:width&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;680&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:height&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;520&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:viewBox&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;1754 161 9938 7945&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:version&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;svg2&amp;quot;&lt;/span&gt;}
&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;count&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))
&lt;span class="hljs-number"&gt;68&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tak dla wprawki, spróbujmy zapisać sparsowaną reprezentację z powrotem jako XML. Funkcja &lt;code&gt;emit&lt;/code&gt; powinna być w stanie to zrobić, ale wypisujekl ona XML na standardowe wyjście. Możemy użyć makra &lt;code&gt;with-out-writer&lt;/code&gt; z pakietu &lt;code&gt;clojure.contrib.io&lt;/code&gt;, aby zrzucić XML-a do pliku:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;use&lt;/span&gt;&lt;/span&gt; &amp;#x27;clojure.contrib.io)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.xml&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; m))
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Próbujemy otworzyć wynikowy obrazek w Firefoksie i...&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Błąd parsowania XML: nieprawidłowo sformowany
Obszar: file:///tmp/a.xml
Numer linii: 15, kolumna 44: Updated to reflect dissolution of Serbia &amp;amp; Montenegro: http://commons.wikimedia.org/wiki/User:Zirland
----------------------------------------------------------------------^
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Argh! Wygląda na to, że znaleźliśmy błąd w &lt;code&gt;clojure.xml&lt;/code&gt;: elementy zawierające ampersandy nie są poprawnie serializowane, przez co późniejsze parsowanie się nie powodzi. Zgłosimy ten błąd później, a teraz usuńmy znowu doraźnie wskazaną przez Firefoksa linijkę (możemy to bezpiecznie zrobić, bo to tylko komentarz). Jeśli teraz powtórzymy nasze dotychczasowe kroki, zobaczymy, że wygenerowany XML jest wreszcie poprawny.&lt;/p&gt;&lt;h2 id="kolorujemy-polskę"&gt;Kolorujemy Polskę&lt;/h2&gt;&lt;p&gt;Widzieliśmy wcześniej, że główny element XML w naszym pliku zawiera 68 podelementów. Zobaczmy, jakie one są – wystarczą nam tagi:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))
(&lt;span class="hljs-symbol"&gt;:title&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:desc&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:defs&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:rect&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jak dotąd jest świetnie. Wygląda na to, że wszystkie opisy krajów są bezpośrednio zawarte w głównym elemencie jako podelementy. Spróbujmy znaleźć Polskę:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;count&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;filter&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;and&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
                       (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; ((&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt;) &lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;))
                 (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m)))
&lt;span class="hljs-number"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Ten kawałek kodu wybiera z listy podelementów &lt;code&gt;m&lt;/code&gt; takie, których tag to &lt;code&gt;path&lt;/code&gt;, a wartość atrybutu &lt;code&gt;id&lt;/code&gt; to &lt;code&gt;"pl"&lt;/code&gt; i zwraca długość takiej podlisty). Spróbujmy teraz dodać do tego elementu atrybut &lt;code&gt;style&lt;/code&gt;, zgodnie z tym, co powiedzieliśmy wcześniej. Ze względu na niezmienność struktur danych musimy zdefiniować nowy nadrzędny element, który będzie taki sam, jak &lt;code&gt;m&lt;/code&gt;, z tym, że odpowiedniemu podelementowi ustawimy styl:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m2&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; m
                &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt;
                (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;and&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
                               (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; ((&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt;) &lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;))
                        (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; % &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill: #ff0000;&amp;quot;&lt;/span&gt;))
                        %)
                     (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))))
#&amp;#x27;user/m&lt;span class="hljs-number"&gt;2&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; m2))
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Otwieramy utworzony plik i widzimy mapkę z Polską zaznaczoną na czerwono. Hura!&lt;/p&gt;&lt;h2 id="generalizacja"&gt;Generalizacja&lt;/h2&gt;&lt;p&gt;Uogólnimy trochę nasz kod. Napiszemy funkcję, która pokoloruje jedno państwo, dostając na wejściu element &lt;code&gt;path&lt;/code&gt; (podelement &lt;code&gt;svg&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;color-state&lt;/span&gt;
  [{&lt;span class="hljs-symbol"&gt;:keys&lt;/span&gt; [tag attrs] &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; element} colorize-fn]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [state (&lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; attrs)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if-let&lt;/span&gt;&lt;/span&gt; [color (&lt;span class="hljs-name"&gt;colorize-fn&lt;/span&gt; state)]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; element &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; attrs &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill:&amp;quot;&lt;/span&gt; color)))
      element)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to funkcja bardzo podobna do tej anonimowej, użytej powyżej w wywołaniu &lt;code&gt;map&lt;/code&gt;, ale różni się w kilku szczegółach. Po pierwsze, bierze dwa argumenty. Jednym jest, jak powiedzieliśmy, element XML-owy (od razu rozbity na &lt;code&gt;tag&lt;/code&gt; i &lt;code&gt;attrs&lt;/code&gt;: więcej o takiej notacji, zwanej &lt;em&gt;destructuring&lt;/em&gt; („dzielenie struktury”) można przeczytać w &lt;a href="http://clojure.org/special_forms"&gt;odpowiedniej części dokumentacji Clojure&lt;/a&gt;), a drugim... funkcja, która dostanie jako argument dwuliterowy kod państwa i zwróci HTML-owy opis jego koloru (lub &lt;code&gt;nil&lt;/code&gt;, jeśli kolor dla tego państwa nie jest określony – &lt;code&gt;color-state&lt;/code&gt; poradzi sobie z tym i zwróci element w stanie nienaruszonym).&lt;/p&gt;&lt;p&gt;Mając &lt;code&gt;color-state&lt;/code&gt;, możemy łatwo napisać funkcję wyższego poziomu, która w jednym kroku przetworzy i zapisze XML:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;save-color-map&lt;/span&gt;
  [svg colorize-fn outfile]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [colored-map (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; svg &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;color-state&lt;/span&gt; % colorize-fn) (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; svg)))]
    (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; out
      (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; colored-map))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Testujemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;save-color-map&lt;/span&gt; m {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#00ff00&amp;quot;&lt;/span&gt;} &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt;)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tym razem Polska jest zielona (jako argumentu &lt;code&gt;color-state&lt;/code&gt; użyliśmy mapy kraj→kolor, bo mapy są w Clojure wołalne jak funkcje). Spróbujmy dorzucić niebieskie Niemcy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;save-color-map&lt;/span&gt; m {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#00ff00&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#0000ff&amp;quot;&lt;/span&gt;} &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt;)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Działa!&lt;/p&gt;&lt;h2 id="problem-z-wielką-brytanią"&gt;Problem z Wielką Brytanią&lt;/h2&gt;&lt;p&gt;Zachęceni sukcesem, próbujemy kolorować różne kraje. Dla większości działa, ale Wielka Brytania jak była szara, tak jest, niezależnie od tego, czy podamy jej kod jako „uk”, czy jako „gb”. Zaglądamy jeszcze raz do źródła naszego obrazka. Komentarze na początku jak zwykle okazują się pomocne:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Certain countries are further subdivided the United Kingdom has gb-gbn for Great Britain and gb-nir for Northern Ireland. Russia is divided into ru-kgd for the Kaliningrad Oblast and ru-main for the Main body of Russia. There is the additional grouping #xb for the „British Islands” (the UK with its Crown Dependencies - Jersey, Guernsey and the Isle of Man)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Może więc trzeba podać „gb-gbn” i „gb-nir”, zamiast samego „gb”? Próbujemy i... też nie działa. Po chwili namysłu: no jasne! Nasze początkowe założenie, że &lt;em&gt;wszystkie&lt;/em&gt; definicje krajów są bezpośrednimi podelementami &lt;code&gt;path&lt;/code&gt;, okazuje się fałszywe. Trzeba to naprawić.&lt;/p&gt;&lt;p&gt;Dotychczas robiliśmy „płaską” transformację drzewa SVG: zamienialiśmy wszystkie podelementy elementu głównego i nic głębiej. Trzeba by zmienić wszystkie elementy &lt;code&gt;path&lt;/code&gt; (i &lt;code&gt;g&lt;/code&gt;, jeśli chcemy kolorować grupy takie jak Wielka Brytania), niezależnie od tego, jak głęboko siedzą w drzewie. Z pomocą przychodzi funkcja &lt;code&gt;map-zipper&lt;/code&gt; z &lt;a href="http://plblog.danieljanus.pl/zippery-w-clojure"&gt;poprzedniego odcinka&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Zważywszy, że funkcja &lt;code&gt;clojure.xml/zip-xml&lt;/code&gt; zwraca zipper działający na drzewach XML-owych, przepisujemy &lt;code&gt;save-color-map&lt;/code&gt; jako:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;save-color-map&lt;/span&gt;
  [svg colorize-fn outfile]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [colored-map (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; #(&lt;span class="hljs-name"&gt;color-state&lt;/span&gt; % colorize-fn) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [x] (#{&lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;} (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; x))) (&lt;span class="hljs-name"&gt;zip/xml-zip&lt;/span&gt; svg))]
    (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; out
      (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; colored-map))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tym razem Wielka Brytania już daje się pomalować.&lt;/p&gt;&lt;h2 id="koloryzatory"&gt;Koloryzatory&lt;/h2&gt;&lt;p&gt;Mamy już zautomatyzowany sam proces kolorowania, ale tłumaczenie konkretnych wartości liczbowych na RGB jest żmudne. W ostatniej części tego artykułu zobaczymy, jak je ułatwić: stworzymy funkcję zwracającą &lt;em&gt;koloryzator&lt;/em&gt;, czyli funkcję nadającą się do przekazania jako argument do &lt;code&gt;color-state&lt;/code&gt; i &lt;code&gt;save-color-map&lt;/code&gt; (dotąd używaliśmy w tym celu map).&lt;/p&gt;&lt;p&gt;Zaczniemy od napisania funkcji tłumaczącej trójkę liczb na HTML-owy zapis RGB, będzie nam bowiem łatwiej pracować z liczbami całkowitymi niż napisami:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;htmlize-color&lt;/span&gt;
  [[r g b]]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;format&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#%02x%02x%02x&amp;quot;&lt;/span&gt; r g b))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wstawmy jeszcze wywołanie &lt;code&gt;htmlize-color&lt;/code&gt; w odpowiednim miejscu w &lt;code&gt;color-state&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;color-state&lt;/span&gt;
  [{&lt;span class="hljs-symbol"&gt;:keys&lt;/span&gt; [tag attrs] &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; element} colorize-fn]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [state (&lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; attrs)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if-let&lt;/span&gt;&lt;/span&gt; [color (&lt;span class="hljs-name"&gt;colorize-fn&lt;/span&gt; state)]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; element &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; attrs &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill:&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;htmlize-color&lt;/span&gt; color))))
      element)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wyobraźmy sobie teraz, że mamy tabelkę z wartościami liczbowymi dla państw, np. taką:&lt;/p&gt;&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Państwo&lt;/th&gt;&lt;th&gt;Wartość&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Polska&lt;/td&gt;&lt;td class="center"&gt;20&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Niemcy&lt;/td&gt;&lt;td class="center"&gt;15&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Holandia&lt;/td&gt;&lt;td class="center"&gt;30&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Chcemy mieć funkcję, która przypisze każdemu krajowi kolor tym intensywniejszy, im większa wartość, przy czym intensywność ma być proporcjonalna do wartości. Dla większej ogólności załóżmy, że mamy daną parę kolorów, c1 i c2, i dla każdej ze składowych R, G i B danemu państwu przypisujemy taką wartość tej składowej, jaka jest odległość rozważanej wartości od najmniejszej wartości w naszym zestawie danych (oczywiście znormalizowana biorąc pod uwagę długości odpowiednich przedziałów).&lt;/p&gt;&lt;p&gt;Brzmi to zagmatwanie, ale mam nadzieję, że na przykładzie będzie widać lepiej. Tak wygląda implementacja clojurowa powyższej funkcji:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;make-colorizer&lt;/span&gt;
  [dataset ranges]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [minv (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; min (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;vals&lt;/span&gt;&lt;/span&gt; dataset))
        maxv (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; max (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;vals&lt;/span&gt;&lt;/span&gt; dataset))
        progress (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [[min-col max-col]] (/ (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; max-col min-col) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; maxv minv))) ranges)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;into&lt;/span&gt;&lt;/span&gt; {}
          (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [[k v]] [(&lt;span class="hljs-name"&gt;.toLowerCase&lt;/span&gt; k) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [progress [min-color _]] (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;int&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; min-color (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; v minv) progress)))) progress ranges)])
               dataset))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zobaczmy, jak działa na naszych przykładowych danych:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;make-colorizer&lt;/span&gt; {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;20&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;15&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;nl&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;30&lt;/span&gt;} [[&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;255&lt;/span&gt;] [&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;] [&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;]])
{&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;85&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;nl&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;255&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Drugi argument oznacza, że składowa czerwona koloru ma się zmieniać od 0 do 255, a zielona i niebieska mają pozostać stałe (na poziomie 0).&lt;/p&gt;&lt;p&gt;Tak jak żądaliśmy, Niemcy wychodzą najciemniejsze (bo mają najmniejszą wartość), Holandia najjaśniejsza (bo ma największą wartość), a Polska jest w 1/3 jasności między Niemcami a Holandią (bo 20 jest w 1/3 drogi między 15 a 30).&lt;/p&gt;&lt;h2 id="podsumowanie"&gt;Podsumowanie&lt;/h2&gt;&lt;p&gt;Aplikację stworzoną w tym artykule można dalej rozwijać w wielu kierunkach. Można stworzyć jej wersję sieciową (jak się do tego zabrać -- opowiedziałem w skrócie na spotkaniu Warszawa JUG). Można pisać różne koloryzatory, np. dyskretny (stałe wartości dla różnych podprzedziałów wejściowego przedziału danych) albo „temperaturowy” (płynnie przechodzący od błękitu do czerwieni przez biel: trzeba w tym celu przejść przez przestrzeń kolorów HSV).&lt;/p&gt;&lt;p&gt;A jaki Wy macie pomysł na rozwinięcie przykładu? Zapraszam do komentowania i eksperymentowania! Dla tych, którym się nie chce przeklejać kodu do REPL, kompletne źródło działające z Leiningenem umieszczam na &lt;a href="https://github.com/nathell/color-europe"&gt;GitHub&lt;/a&gt;. Forki będą bardzo mile widziane.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-13:post:protokoly-i-git-bisect</id>
    <title>Pierwsze użycie: protokoły i git-bisect</title>
    <link href="http://plblog.danieljanus.pl/2011/01/13/protokoly-i-git-bisect/"/>
    <updated>2011-01-13T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Lubię rozwiązywać problemy przy użyciu narzędzi, których dotychczas nie znałem albo znałem tylko teoretycznie, na zasadzie „wiem, że istnieje coś takiego i do czego z grubsza służy”. Jeszcze przyjemniej jest, kiedy uzyskane rozwiązanie okazuje się czytelniejsze, zrozumialsze, szybsze albo pod innymi względami lepsze niż wersja używająca tylko dotychczasowego „arsenału”. W tych dniach zdarzyło mi się tego doświadczyć dwukrotnie, gdy pracowałem nad &lt;a href="http://fablo.pl"&gt;Fablo&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Fablo to napisany w &lt;a href="http://clojure.org"&gt;Clojure&lt;/a&gt; silnik wyszukiwarki dla polskich sklepów internetowych: zna odmianę polskich wyrazów, obsługuje literówki i błędy ortograficzne. Radzi sobie też z wyrazami wpisanymi bez polskich znaków, a więc wie, że „papryka zolta” to to samo, co „papryka żółta”. Działa to na ogół zupełnie dobrze, ale wciąż ulepszamy wyszukiwarkę i eliminujemy błędy polegające na tym, że na jakieś zapytanie nie wyszukują się niektóre produkty, choć powinny. Tym razem błąd brzmiał: zapytanie „sledzie” zwraca inne wyniki niż „śledzie”.&lt;/p&gt;&lt;p&gt;Analiza problemu ujawniła, że algorytm odpowiedzialny za „polszczenie” zapytania powinien być stosowany szerzej. Mamy dwie implementacje pewnej struktury danych używanej w tym algorytmie: jedna z nich jest czysto funkcyjna i zbudowana z rdzennie clojurowego tworzywa – map i wektorów; druga zaś jest ukryta za fasadą używanej przez nas biblioteki Javowej o prostym interfejsie, a więc explicite do tej pory z niej nie korzystaliśmy. Algorytm polszczenia był uruchamiany tylko na pierwszej implementacji (co wystarczy w wielu przypadkach, ale nie zawsze), a powinien być na obu.&lt;/p&gt;&lt;p&gt;Koncepcyjnie te dwie implementacje są podobne, ale mają zupełnie różne API – zrazu wydawało mi się więc, że będę musiał pisać drugą wersję funkcji realizującej „polszczenie”. To jednak nie byłoby optymalne: duplikacja kodu oznacza dwa razy więcej okazji do popełnienia błędu i konieczność pamiętania o wprowadzeniu zmian każdorazowo w obu miejscach. Z pomocą przyszły &lt;a href="http://clojure.org/Protocols"&gt;protokoły&lt;/a&gt; – nowy element języka wprowadzony w Clojure 1.2.&lt;/p&gt;&lt;p&gt;Protokół jest czymś bardzo zbliżonym do znanego z Javy interfejsu: to zestaw deklaracji nazwanych funkcji i ich argumentów, jednak bez implementacji. Implementacje definiuje się dla konkretnych typów. Mogą to być zarówno typy definiowane w Clojure (przy użyciu konstrukcji &lt;code&gt;deftype&lt;/code&gt;), jak i istniejące klasy Javowe. Można więc zadeklarować w protokole jakąś operację (nazwijmy ją blabalizacją), po czym zdefiniować, co oznacza blabalizowanie liczb, a co napisów. Efekt jest taki, jak gdybyśmy w Javie jakoś zmusili klasy &lt;code&gt;java.lang.Integer&lt;/code&gt; i &lt;code&gt;java.lang.String&lt;/code&gt; do implementowania interfejsu &lt;code&gt;Blabalize&lt;/code&gt;; możemy wołać odpowiednią funkcję bezpośrednio na obiektach odpowiednich klas, bez żadnych wrapperów!&lt;/p&gt;&lt;p&gt;Zacząłem więc od zdefiniowania protokołu, opisującego dwie operacje, jakich można dokonywać na mojej strukturze danych. Potem przerobiłem implementację algorytmu „polszczącego” zapytania tak, aby korzystała tylko z tych dwóch operacji, co uniezależniło ją od implementacji „pod spodem”. Napisanie Clojurowej implementacji protokołu było proste; trudniejsze okazało się odpowiednie skorzystanie z biblioteki Javowej, ale i to udało się zrobić w miarę szybko.&lt;/p&gt;&lt;p&gt;Efekt: brak duplikacji kodu i tylko nieznacznie zmodyfikowana dotychczasowa wersja. A przy tym udało się nagiąć bibliotekę Javową do robienia rzeczy, do których nie była zaprojektowana, bez ingerencji w jej kod. Clojure nie przestaje mnie zadziwiać.&lt;/p&gt;&lt;p&gt;Innym razem zauważyłem, że od kilku commitów przy próbie uruchomienia testów jednostkowych i regresji w logach testowania pojawia się komunikat, którego zdecydowanie nie powinno tam być. Mógłbym zabrać się do tego testując okolice pojawienia się komunikatu w REPL, ale tym razem postanowiłem zrobić to inaczej. Ponieważ używamy &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; do kontroli wersji, wykorzystałem narzędzie &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html"&gt;git-bisect&lt;/a&gt;, aby znaleźć commit wprowadzający komunikat. Potrzebuje ono do działania trzech rzeczy: identyfikatorów commitu „dobrego” i „złego” oraz skryptu, który odpowiada na pytanie, czy dany commit jest „dobry” czy „zły” (zwracającego kod wyjścia odpowiednio 0 albo 1). Skrypt wyglądał mniej więcej następująco:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;cake clean
cake deps
cake proto
&lt;span class="hljs-built_in"&gt;sleep&lt;/span&gt; 10
cake release
&lt;span class="hljs-built_in"&gt;rm&lt;/span&gt; *.&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;
make &lt;span class="hljs-built_in"&gt;test&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; grep -q &lt;span class="hljs-string"&gt;&amp;quot;PODEJRZANY_KOMUNIKAT&amp;quot;&lt;/span&gt; fablo*&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;then&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 1
&lt;span class="hljs-keyword"&gt;else&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 0
&lt;span class="hljs-keyword"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(&lt;a href="http://github.com/ninjudd/cake"&gt;Cake&lt;/a&gt; jest narzędziem, którego używamy do budowania oprogramowania; 10-sekundowa przerwa między &lt;code&gt;cake proto&lt;/code&gt; a &lt;code&gt;cake release&lt;/code&gt; jest remedium na błąd w cake.)&lt;/p&gt;&lt;p&gt;Potem wystarczyło tylko:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;$ git bisect start HEAD dobry_commit --
$ git bisect run check.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i już wiedziałem, w którym miejscu się zepsuło, a stąd było już niedaleko do znalezienia przyczyny problemu.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-25:post:zippery-w-clojure</id>
    <title>Zippery w Clojure</title>
    <link href="http://plblog.danieljanus.pl/2010/11/25/zippery-w-clojure/"/>
    <updated>2010-11-25T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Niniejszy artykuł jest pierwszym z dwuczęściowego minicyklu, stanowiącego demonstrację wykorzystania Clojure w praktyce do prostego, acz nietrywialnego zadania, na które natknąłem się w codziennej pracy. Dzisiejsza część może wydać się mało interesująca i mocno teoretyczna, ale mam nadzieję, że następny odcinek pokaże, jak można ją ciekawie wykorzystać (na razie nie zdradzę, jakie to wykorzystanie).&lt;/p&gt;&lt;p&gt;Planuję, że takie artykuły lub cykle zdominują Clojurową część tego bloga – będę się tu dzielił rozwiązaniami praktycznych problemów, na jakie natrafiam. Nie będzie tu artykułów w stylu „hej, jaki fajny nowy framework XYZ, napiszmy w nim Hello World przy wykorzystaniu technologii ABC!”, chyba że XYZ lub ABC będą przydatnymi narzędziami do rozwiązania problemu z życia wziętego. Bo takie rozwiązywanie w Clojure daje dużo radości: nie przypadkiem taki właśnie jest tytuł &lt;a href="http://joyofclojure.com/"&gt;książki M. Fogusa i C. Housera&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;A dziś opowiem o ciekawej strukturze danych, jaką są &lt;em&gt;zippery&lt;/em&gt;. (Mierzi mnie trochę używanie angielskiej nazwy, ale nie potrafię wymyślić dobrego polskiego odpowiednika tego słowa; będę szczęśliwy widząc propozycje w komentarzach!) Cóż to takiego?&lt;/p&gt;&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Zipper_(data_structure)"&gt;Zipper&lt;/a&gt; jest strukturą danych, dającą &lt;em&gt;iluzję imperatywności&lt;/em&gt; przy manipulowaniu drzewami. Pamiętamy, że Clojurowe natywne struktury danych są niezmienne (&lt;em&gt;immutable&lt;/em&gt;): jeśli mamy listę składającą się z liczb 2 i 5, to nie możemy jej w żaden sposób zmienić (np. dodać elementu) -- możemy co najwyżej stworzyć na jej podstawie &lt;em&gt;nową&lt;/em&gt; listę, która będzie zawierać wszystkie elementy listy wyjściowej i jeszcze jakiś.&lt;/p&gt;&lt;p&gt;Niezmienność jest bardzo przydatna (o filozoficznych podstawach takiego podejścia, które zadecydowały o jego wykorzystaniu w Clojure, można poczytać w artykule &lt;a href="http://clojure.org/state"&gt;„On State and Identity”&lt;/a&gt;), ale wymusza myślenie w innych kategoriach. To jest to, co czasami nazywa się „myśleniem funkcyjnym”: zamiast zastanawiać się, w jaki sposób &lt;em&gt;zmienić&lt;/em&gt; wartość naszej zmiennej, aby doprowadzić ją do pożądanego stanu, pytamy o to, &lt;em&gt;jak z jednych wartości robić inne&lt;/em&gt;. Przy tym nigdzie nie jest powiedziane, że taki sposób myślenia jest koniecznie lepszy od imperatywnego, do którego przyzwyczajeni są programiści języków takich jak Java. Jest po prostu inny. Warto się go nauczyć, bo okazuje się, że wiele problemów się upraszcza, gdy już umysł się przestawi na taki &lt;em&gt;modus operandi&lt;/em&gt;. Bywa jednak i tak, że o danym problemie imperatywnie myśli się wygodniej niż funkcyjnie. Tu właśnie wkraczają zippery.&lt;/p&gt;&lt;img src="/img/blog/zipper.png"&gt;
&lt;p&gt;Wyobraźmy sobie, że chcemy dodać liczbę 4 do konkretnego drzewa BST, tak jak na rysunku powyżej. Załóżmy chwilowo, że nie interesuje nas ogólna funkcja dodająca do BST, a tylko chcemy wstawić czwórkę w konkretne miejsce. Jak to zrobić imperatywnie?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Zejdź dwa razy w dół w prawo („dobierz się” do węzła 6).&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wstaw czwórkę na lewo od bieżącego węzła.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I już. A teraz funkcyjnie – pamiętamy, że z drzewa robimy nowe drzewo:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Nowym drzewem jest drzewo, którego korzeniem jest korzeń drzewa wyjściowego, lewym poddrzewem – lewe poddrzewo drzewa wyjściowego, a prawym poddrzewem drzewo, którego korzeniem jest korzeń prawego poddrzewa drzewa wyjściowego, lewym poddrzewem – drzewo składające się z tylko jednego węzła 4, a prawym poddrzewem – prawe poddrzewo prawego poddrzewa drzewa wyjściowego.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Pierwsze podejście jest łatwiejsze, prawda? Zipper pozwala nam zachować je prawie niezmienione, nie rezygnując przy tym z zalet niezmienności. Oto jak można by opisać dodawanie do drzewa z zipperem:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Stwórz zipper na podstawie drzewa wyjściowego.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „zejdź w dół”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „przejdź w prawo”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „zejdź w dół”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „wstaw 4 przed bieżącym elementem”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „daj drzewo wynikowe”, otrzymując pożądane drzewo.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Clojure zawiera implementację zipperów w bibliotece standardowej (w przestrzeni nazw &lt;code&gt;clojure.zip&lt;/code&gt;). Oto jak można by zapisać powyższy przykład w Clojure:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;nowe-drzewo&lt;/span&gt;
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [z1 (&lt;span class="hljs-name"&gt;zip&lt;/span&gt; drzewo)
        z2 (&lt;span class="hljs-name"&gt;down&lt;/span&gt; z1)
        z3 (&lt;span class="hljs-name"&gt;right&lt;/span&gt; z2)
        z4 (&lt;span class="hljs-name"&gt;down&lt;/span&gt; z3)
        z5 (&lt;span class="hljs-name"&gt;insert-left&lt;/span&gt; z4 &lt;span class="hljs-number"&gt;4&lt;/span&gt;)]
    (&lt;span class="hljs-name"&gt;root&lt;/span&gt; z5)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(tu &lt;em&gt;explicite&lt;/em&gt; nazywam kolejne kroki obliczeń). Albo tak, używając makra &lt;code&gt;-&gt;&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;nowe-drzewo&lt;/span&gt;
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; drzewo
      zip
      down
      right
      down
      (&lt;span class="hljs-name"&gt;insert-left&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;)
      root)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Proste i wygodne. Pod spodem zipper to po prostu oryginalne drzewo plus informacja o tym, w którym miejscu drzewa w tej chwili jesteśmy, plus lista „zmian”, jakie do tej pory zostały na nim wykonane.&lt;/p&gt;&lt;p&gt;Rozwiązując mój problem (na razie nie zdradzam, jaki), natknąłem się na potrzebę posiadania funkcji, która działałaby jak &lt;code&gt;map&lt;/code&gt;, ale na drzewach, a nie listach. To znaczy, przekształcałaby każdy element, niezależnie od tego, jak głęboko w drzewie siedzi, aplikując do niego jakąś funkcję, i zwracała w wyniku nowe drzewo. Co więcej, chciałem móc dodatkowo kontrolować, które węzły są zmieniane: funkcja ma dostawać dodatkowy predykat i tylko kiedy zwróci on &lt;code&gt;true&lt;/code&gt; na wartości danego węzła, zmieniać tę wartość. Nie ma chyba takiej funkcji w standardowej bibliotece, ale dzięki zipperom można ją łatwo napisać.&lt;/p&gt;&lt;p&gt;Tak się szczęśliwie składa, że Clojurowe zippery mają funkcję &lt;code&gt;next&lt;/code&gt;, która spaceruje po drzewie „w głąb”. Mając je, mogłem pomyśleć tak o swojej implementacji:&lt;/p&gt;&lt;p&gt;Jeśli zipper jest na końcu drzewa, to wynikiem transformacji jest wynikowe drzewo dla tego zippera, w przeciwnym razie wynikiem jest ta sama transformacja wywołana rekurencyjnie dla zippera uzyskanego przez wykonanie operacji „edit” (edycji bieżącego węzła w razie potrzeby) i „next”.&lt;/p&gt;&lt;p&gt;Tłumacząc to na Clojure, dostajemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;map-zipper&lt;/span&gt; [f pred z]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;zip/end?&lt;/span&gt; z)
    (&lt;span class="hljs-name"&gt;zip/root&lt;/span&gt; z)
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; f pred (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; z (&lt;span class="hljs-name"&gt;zip/edit&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;pred&lt;/span&gt; %) (&lt;span class="hljs-name"&gt;f&lt;/span&gt; %) %)) zip/next)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I testujemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; inc integer?
              (&lt;span class="hljs-name"&gt;zip/vector-zip&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]))
[&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; [&lt;span class="hljs-number"&gt;4&lt;/span&gt; &lt;span class="hljs-number"&gt;5&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;6&lt;/span&gt;] [&lt;span class="hljs-number"&gt;7&lt;/span&gt; [[&lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt; &lt;span class="hljs-number"&gt;10&lt;/span&gt;]]]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Trzeba podać predykat &lt;code&gt;integer?&lt;/code&gt;, bo zipper działający na zagnieżdżonych wektorach działa na poszczególnych poddrzewach, a nie tylko na liściach (które tu są liczbami). Możemy łatwo zobaczyć, jakie poddrzewa odwiedza &lt;code&gt;map-zipper&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;do&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;println&lt;/span&gt; %) %)
              (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;constantly&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-literal"&gt;true&lt;/span&gt;)
              (&lt;span class="hljs-name"&gt;zip/vector-zip&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]))
[&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]
&lt;span class="hljs-number"&gt;1&lt;/span&gt;
[&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]]
&lt;span class="hljs-number"&gt;2&lt;/span&gt;
[&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]
&lt;span class="hljs-number"&gt;3&lt;/span&gt;
&lt;span class="hljs-number"&gt;4&lt;/span&gt;
[&lt;span class="hljs-number"&gt;5&lt;/span&gt;]
&lt;span class="hljs-number"&gt;5&lt;/span&gt;
[&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]
&lt;span class="hljs-number"&gt;6&lt;/span&gt;
[[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]
[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]
&lt;span class="hljs-number"&gt;7&lt;/span&gt;
&lt;span class="hljs-number"&gt;8&lt;/span&gt;
&lt;span class="hljs-number"&gt;9&lt;/span&gt;
[&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-19:post:filmujmy-bledy</id>
    <title>Filmujmy błędy!</title>
    <link href="http://plblog.danieljanus.pl/2010/11/19/filmujmy-bledy/"/>
    <updated>2010-11-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Każdy programista (o ile jego oprogramowanie jest używane przez kogoś jeszcze poza nim samym) dostał kiedyś zgłoszenie błędu w programie. Wielu pracuje na co dzień z systemami śledzenia błędów, wielu zgłasza błędy w programach, których sami używają.&lt;/p&gt;&lt;p&gt;Dobre zgłoszenie powinno pomóc programiście odtworzyć błąd. Instrukcja odtworzenia jest bodaj najważniejszym z trzech nieodzownych elementów zgłoszenia. (Pozostałe dwa, jak pisze &lt;a href="http://www.joelonsoftware.com/articles/fog0000000029.html"&gt;Joel Spolsky&lt;/a&gt;, to odpowiedzi na pytania: &lt;em&gt;co powinno było się stać&lt;/em&gt; oraz &lt;em&gt;co naprawdę się stało&lt;/em&gt;.) Na ogół w systemach śledzenia błędów opisujemy po prostu prozą kroki, które wykonaliśmy, a które doprowadziły do błędnej sytuacji.&lt;/p&gt;&lt;p&gt;Dokładne opisanie wykonanych czynności jest jednak trudne. Znaczenie może mieć każdy, nawet najmniejszy szczegół, łatwo więc o nieścisłość. Zamiast „otworzyłem plik” lepiej napisać „nacisnąłem Alt-O” albo „kliknąłem w menu Plik i kliknąłem polecenie Otwórz”, w zależności od tego, co naprawdę zrobiliśmy. W świetnym eseju &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/bugs-pl.html"&gt;&lt;em&gt;Jak efektywnie zgłaszać błędy&lt;/em&gt;&lt;/a&gt;, z którego zaczerpnąłem ten przykład, Simon Tatham podaje inną, efektywniejszą metodę zgłaszania: należy posadzić programistę przed swoim komputerem i pokazać mu, w jaki sposób doprowadzamy do błędu.&lt;/p&gt;&lt;p&gt;Ta metoda jest dobra, kiedy da się zastosować. Problem pojawia się w przypadku, kiedy programista jest fizycznie na drugim końcu świata. Nie może wtedy zobaczyć monitora użytkownika i patrzeć mu na ręce... albo przynajmniej do niedawna nie mógł.&lt;/p&gt;&lt;p&gt;Od jakiegoś czasu w &lt;a href="http://fablo.pl"&gt;Fablo&lt;/a&gt; dołączamy do zgłoszeń błędów krótkie filmiki – &lt;em&gt;screencasty&lt;/em&gt; z komentarzem głosowym. Z każdym kolejnym błędem rozwiązanym dzięki wskazaniu w taki sposób coraz bardziej przekonuję się do rozlicznych zalet tej metody. Przede wszystkim film jest dokładnym zapisem sesji. Widać wszystko, co się działo krok po kroku, i nie trzeba prosić o dodatkowe szczegóły, co zabiera czas i naprawiającego, i zgłaszającego. Film można odtwarzać wielokrotnie i dowolnie po nim nawigować, a nawet spowalniać; to nawet lepsze niż patrzenie na żywo na pracę użytkownika z programem. Jeśli mamy do czynienia z &lt;em&gt;heisenbugiem&lt;/em&gt; („wywala się mniej więcej co pięć minut i w różnych sytuacjach, za cholerę nie mam pomysłu!”), możemy po prostu włączyć nagrywanie i zapomnieć o nim, pracując normalnie do momentu wystąpienia błędu. Film można wtedy obciąć do ostatnich dwóch-trzech minut i wysłać.&lt;/p&gt;&lt;p&gt;Co więcej, sporządzenie filmiku bardzo mało kosztuje. Na obie używane przez nas platformy dostępne są narzędzia pozwalające łatwo nagrać film. Dzięki &lt;a href="http://www.ambrosiasw.com/utilities/snapzprox/"&gt;Snapz Pro X&lt;/a&gt; na Mac OS X nagranie jest tak proste jak naciśnięcie Shift-Cmd-3 na początku i na końcu filmu; &lt;a href="http://recordmydesktop.sourceforge.net/about.php"&gt;Record My Desktop&lt;/a&gt; na Linuksa i FreeBSD jest nieco trudniejszy w obsłudze, ale wciąż łatwy.&lt;/p&gt;&lt;p&gt;Wypadałoby wspomnieć o minusach filmów. Przychodzą mi do głowy dwa. Jednym jest fakt, że nie każdy błąd można zgłosić w ten sposób, a mówiąc ściślej: błąd w nie każdym oprogramowaniu. Gdy pracuje się z bibliotekami zamiast z okienkowym lub przeglądarkowym oprogramowaniem, wyłapywane błędy są innej natury i polegają na ogół na nieoczekiwanym zachowaniu konkretnej funkcji lub klasy. To, &lt;em&gt;nolens volens&lt;/em&gt;, trzeba opisać.&lt;/p&gt;&lt;p&gt;Druga sprawa to ilość miejsca zajmowanego przez filmiki i czasu potrzebnego na ich wrzucenie do systemu kontroli błędów. To zapewne jeszcze do niedawna był powód, dla którego rzadko widzi się takie zgłoszenia np. w oprogramowaniu &lt;em&gt;open source&lt;/em&gt;. Jednak problem ten staje się mało znaczący wraz z upowszechnieniem się szybkich łącz. Typowa wielkość naszego filmiku to ok. 1.5-2 MB; to naprawdę nie jest dużo, a &lt;a href="http://fogbugz.com"&gt;FogBugz&lt;/a&gt; pozwala na bardzo łatwe – jednym przeciągnięciem myszy – dodanie screencastu do raportu błędu. Większe rzeczy z łatwością można udostępnić w &lt;a href="http://dropbox.com"&gt;Dropbox&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Filmujmy więc błędy!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-15:post:toccata-galuppiego</id>
    <title>Wiersze nieprzetłumaczalne: Robert Browning, „Toccata Galuppiego”</title>
    <link href="http://plblog.danieljanus.pl/2010/11/15/toccata-galuppiego/"/>
    <updated>2010-11-15T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;„Nie ma wierszy nieprzekładalnych, są tylko leniwi tłumacze”. Taki aforyzm nieznanego autorstwa cytuje Stanisław Barańczak w „Ocalonym w tłumaczeniu”, zastrzegając się, że nie jest pewien jego słuszności. I choć Barańczak kontestuje istnienie takich wierszy, których on sam nie potrafiłby na polski przełożyć, nieprzekładalności dopatrując się raczej w tłumaczeniach w drugą stronę – ja od czasu do czasu, zastanawiając się nad tym, jak po polsku mógłby brzmieć ten lub inny wiersz, dochodzę do wniosku, że dobrze przetłumaczyć się go nie da. Dziś przykład: &lt;em&gt;Toccata Galuppiego&lt;/em&gt; (&lt;em&gt;A Toccata of Galuppi's&lt;/em&gt;) Roberta Browninga, wiersz z roku 1855 ze zbioru „Women and Men”.&lt;/p&gt;&lt;p&gt;Posłuchajmy pierwszej zwrotki:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Oh, Galuppi, Baldassaro, this is very sad to find!&lt;br&gt; I can hardly misconceive you; it would prove me deaf and blind;&lt;br&gt; But although I take your meaning, ’tis with such a heavy mind!&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Mówię „posłuchajmy”, a nie „popatrzmy” albo „przeczytajmy”, bo to jeden z tych wierszy, które &lt;em&gt;brzmią&lt;/em&gt;; w nim melodia tekstu jest co najmniej równie ważna, jak jego dosłowny sens. Napisałem tak, jak gdyby dało się oddzielić te dwie rzeczy, ale to nieprawda. Ten wiersz jest autotematyczny: jest o muzyce i zarazem jest muzyką. I to taką, która jest w stanie przenieść słuchacza w nigdy niewidziane krainy z całym ich bogactwem, tak, aby czuł tam swoją obecność niemal namacalnie. I właśnie przeniesienie tej muzyki w obszar naszego języka wydaje mi się niemożliwe do urzeczywistnienia.&lt;/p&gt;&lt;p&gt;Jak bowiem mógłby wyglądać polski przekład? Jak musiałby oddać różne aspekty oryginału?&lt;/p&gt;&lt;p&gt;Po pierwsze – rytm. W oryginale jest to peon trzeci z kataleksą, w którym każdy wers składa się z czterech stóp. Tak przynajmniej ja to słyszę; można też to traktować jako ośmiostopowiec trocheiczny (tak podobno pisał o nim sam Browning w liście do F. J. Furnivalla – podaję za wydaniem z komentarzami pod redakcją Woolforda i Karlina), ale moim zdaniem pierwsze akcenty w każdej parze trochejów są znacznie słabsze niż drugie. Jednak ta subtelna różnica jest mało istotna wobec pytania, jaki rytm wybrać dla polskiej wersji?&lt;/p&gt;&lt;p&gt;Już pierwszy półwers narzuca odpowiedź. Inwokacja do kompozytora wydaje się jednym z tych fragmentów, które trzeba pozostawić nienaruszone na swoim miejscu, a więc początek brzmiałby jakoś „O Galuppi, Baltazarze!” albo jakoś podobnie. Tak samo jak w oryginale, dwa peony, po których następuje średniówkowy oddech, pauza. A jeśli średniówka, to po niej drugi półwers powinien mieć mniej więcej tę samą długość, może skróconą o jedną sylabę. Do rozważenia są więc tylko dwa warianty:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;czterostopowiec peoniczny (bez kataleksy, z rymami żeńskimi),&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;czterostopowiec peoniczny z kataleksą, tak jak w oryginale.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Pierwszy wariant byłby łatwiejszy, bo z rymami żeńskimi jest w polszczyźnie znacznie mniejszy problem niż z męskimi (w każdej zwrotce, a jest ich piętnaście, trzeba znaleźć rym potrójny i to niebanalny!) Jednak wystarczy sobie to wystukać na stole, żeby usłyszeć, dlaczego tak się nie da:&lt;/p&gt;&lt;p&gt;&lt;em&gt;puk puk PUK puk / puk puk PUK puk / puk puk PUK puk / puk puk PUK puk&lt;/em&gt;&lt;br&gt; &lt;em&gt;puk puk PUK puk / puk puk PUK puk / puk puk PUK puk / puk puk PUK puk&lt;/em&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Słychać? Katarynka. Wciąż ten sam &lt;em&gt;puk-puk-PUK-puk&lt;/em&gt; przez sto osiemdziesiąt iteracji (3 * 15 * 4). Po dwudziestu paru razach można usnąć; przykuwający uwagę rytm oryginału, z dobitnymi ostatnimi sylabami, gdzieś uciekł.&lt;/p&gt;&lt;p&gt;A więc tak też nie. Rytm musi być dokładnie taki jak w oryginale, co skazuje nas na rymy męskie, z którymi, jako się rzekło, jest problem: słów jednosylabowych jest w języku polskim za mało. W „Oficjalnym słowniku polskiego scrabblisty” można znaleźć ich kilka tysięcy (na ponad dwa miliony wszystkich odnotowanych form słów):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ egrep '^[^aeioóuyąę]*(y|i?[aeouóąę])[^aeioóuyąę]*$' osps.txt | wc -l
5975
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Z tego przydatnych i możliwych do użycia w nieudziwnionym języku jest zapewne nie więcej niż tysiąc; reszta to dziwolągi w stylu &lt;em&gt;angst&lt;/em&gt;, &lt;em&gt;alb&lt;/em&gt; i &lt;em&gt;agf&lt;/em&gt; (wybrałem parę na chybił trafił z pierwszej dziesiątki). Wybranie z tego tysiąca piętnastu wzajemnie rymujących się trójek, stanowiących w dodatku trafne ekwiwalenty kawałków oryginału, wydaje się graniczyć z niemożliwością.&lt;/p&gt;&lt;p&gt;Nawet gdyby się to jednak udało, pozostaje kwestia naturalnej zwięzłości języka angielskiego, której nie ma polszczyzna. Jest w „Toccacie” wiele półwersowych fraz składających się tylko z jednosylabowych słów i brzmiących bardzo naturalnie, jak choćby „It’s as if I saw it all.” Jak powiedzieć po polsku w siedmiu sylabach „Tak, jak gdybym to wszystko widział”? Albo „Venice spent what Venice earned” – dwukrotne powtórzenie nazwy Wenecji jest tu bardzo znaczące, ale jedno z nich trzeba by w polskiej wersji wyrzucić, żeby mieć niezerową ilość miejsca, a i tak będzie bardzo trudno dobrze oddać tę frazę. Albo te wielokrotne echolalie, „those suspensions, those solutions”, „dear dead women”...&lt;/p&gt;&lt;p&gt;Nie, moim zdaniem się nie da. &lt;a href="http://www.victorianweb.org/authors/rb/toccata.html"&gt;Wsłuchajmy się&lt;/a&gt; w bicie dzwonów bazyliki św. Marka, rozbrzmiewające dobitnie w oryginale.&lt;/p&gt;&lt;p&gt;Na zakończenie wspomnę, że cytowany tu fragment pochodzi z późniejszej, częściej spotykanej redakcji wiersza. W pierwszej wersji, tej z &lt;em&gt;Women and Men&lt;/em&gt;, trzecia linijka brzmiała:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;But although I give you credit, ’tis with such a heavy mind!&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-11:post:leniwa-wersja-makra</id>
    <title>Leniwa wersja makra -&gt;</title>
    <link href="http://plblog.danieljanus.pl/2010/11/11/leniwa-wersja-makra/"/>
    <updated>2010-11-11T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;&lt;a href="http://jaceklaskowski.pl"&gt;Jacek Laskowski&lt;/a&gt; podaje ciekawy &lt;a href="http://blog.japila.pl/2010/11/monady-maybe-odsona-kolejna-rozwiazanie.html"&gt;przykład wykorzystania monad w Clojure&lt;/a&gt; -- aplikowanie kolejnych funkcji do wyrażenia, dopóki ma ono wartość nie będącą &lt;code&gt;nil&lt;/code&gt;.  Przykład mi się podoba, bo jest prosty, ale nie trywialny – wykorzystuje monadę &lt;code&gt;maybe&lt;/code&gt; do eleganckiego rozwiązania rzeczywistego problemu.  Jest to w dodatku problem, z którym borykają się czasem programiści piszący w Javie, co widać choćby w &lt;a href="http://groups.google.com/group/pl.comp.lang.java/browse_thread/thread/d290ced113686b34/f4cc877a5053969d"&gt;tym wątku&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Nie mogę powiedzieć, że rozumiem monady. Owszem, znam definicję i potrafię napisać program w Haskellu wykorzystujący monadę &lt;code&gt;IO&lt;/code&gt;, ale każdy monadyczny kod, jaki dotychczas widziałem, dawał się zapisać równie zwięźle bez użycia monad w językach, które mniej religijnie podchodzą do niewpływania funkcji na stan świata zewnętrznego. Tak jest również w tym przypadku.&lt;/p&gt;&lt;p&gt;Jacek tak postawił oryginalny problem:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Napisać metodę, która zwraca walutę, dla pracownika z danego departamentu międzynarodowej korporacji. Pracownik jest przypisany do departamentu (np. poprzez mapę – pracownik-departament), departament do kraju, a kraj do waluty. Funkcja na wejściu dostaje nazwę, identyfikator, lub cokolwiek jednoznacznie reprezentującego pracownika, a na wyjściu symbol waluty, np. dla „Jacek” powinno być „PLN”, a dla „John” „USD”, a „Tomek” i „Mateusz” dawaliby „CHF”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Moje pierwotne rozwiązanie jest po prostu złożeniem trzech funkcji:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;pracownik-&amp;gt;waluta&lt;/span&gt; [p]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; p pracownik-&amp;gt;departament departament-&amp;gt;kraj kraj-&amp;gt;waluta))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;O ile wszystkie z nich są mapami (pamiętamy, że w Clojure mapy implementują interfejs &lt;code&gt;IFn&lt;/code&gt;, można je więc uważać za funkcje i wołać jak funkcje), to dla żadnej wartości wejściowej nie zostanie rzucony &lt;code&gt;NullPointerException&lt;/code&gt;, ponieważ wywołanie mapy dla nieistniejącego klucza zwraca &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Jacek słusznie zauważa jednak, że wykonujemy w ten sposób więcej pracy niż trzeba. Jeżeli już w mapie &lt;code&gt;pracownik-&gt;departament&lt;/code&gt; nie ma departamentu dla danego pracownika, to otrzymany &lt;code&gt;nil&lt;/code&gt; zostanie „przepchnięty” przez pozostałe dwie mapy, zanim będzie zwrócony. Można kontrargumentować, że taka implementacja &lt;code&gt;pracownik-&gt;waluta&lt;/code&gt; jest bardzo czytelna i mała strata wydajności jest niewielką ceną do zapłacenia za tę czytelność.  Co jednak, gdy nie możemy lub nie chcemy zgodzić się na taką stratę, a jednocześnie nie chcemy stracić czytelności?&lt;/p&gt;&lt;p&gt;Odpowiedzią jest makro, które nazwałem &lt;code&gt;and-&gt;&lt;/code&gt;. Działa ono tak samo, jak &lt;code&gt;-&gt;&lt;/code&gt;, z tą różnicą, że kolejne wartości oblicza tylko jeśli po drodze nie pojawiło się &lt;code&gt;false&lt;/code&gt; lub &lt;code&gt;nil&lt;/code&gt;, podobnie jak &lt;code&gt;and&lt;/code&gt; (stąd nazwa). Oto ono:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defmacro&lt;/span&gt; &lt;span class="hljs-title"&gt;and-&amp;gt;&lt;/span&gt;
  ([x] x)
  ([x form] `(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;when-let&lt;/span&gt;&lt;/span&gt; [y# ~x] (~form y#)))
  ([x form &amp;amp; more] `(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;when-let&lt;/span&gt;&lt;/span&gt; [y# (&lt;span class="hljs-name"&gt;and-&amp;gt;&lt;/span&gt; ~x ~form)]
                      (&lt;span class="hljs-name"&gt;and-&amp;gt;&lt;/span&gt; y# ~@more))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Implementacja jest bardzo zbliżona do zwykłego &lt;code&gt;-&gt;&lt;/code&gt;, którego kod w Clojure 1.2 wygląda &lt;a href="https://github.com/clojure/clojure/blob/1.2.0/src/clj/clojure/core.clj#L1284"&gt;tak&lt;/a&gt;. W stosunku do &lt;code&gt;-&gt;&lt;/code&gt; moje makro jest dla większej poglądowości trochę uproszczone i nie obsługuje konstrukcji typu &lt;code&gt;(and-&gt; mapa (get :klucz))&lt;/code&gt;, które normalnie rozwijane są do &lt;code&gt;(get mapa :klucz)&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Warto zwrócić uwagę na to, że argument makra nigdy nie jest wyliczany w jego rozwinięciu więcej niż raz; zawsze jest wiązany do lokalnego symbolu o unikatowej nazwie za pomocą &lt;code&gt;when-let&lt;/code&gt;. Jest to jedna z reguł pozwalających unikać błędów w makrach. Szerszy opis tego problemu, występującego też w Common Lispie, można znaleźć w &lt;a href="http://gigamonkeys.com/book/macros-defining-your-own.html#plugging-the-leaks"&gt;rozdziale 8 książki „Practical Common Lisp”&lt;/a&gt;.&lt;/p&gt;&lt;h2 id="aktualizacja-(7.12.2010)"&gt;Aktualizacja (7.12.2010)&lt;/h2&gt;&lt;p&gt;Jak się okazuje, clojure-contrib &lt;a href="http://clojuredocs.org/clojure_contrib/clojure.contrib.core/-_q%3E"&gt;zawiera już&lt;/a&gt; takie makro! Nazywa się &lt;code&gt;-?&gt;&lt;/code&gt; i można je znaleźć w przestrzeni nazw &lt;code&gt;clojure.contrib.core&lt;/code&gt;. To już któryś raz, kiedy okazuje się, że jakieś użyteczne makro lub funkcja już jest w contrib i nie trzeba jej było pisać.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-07:post:o-lacznikach-i-myslnikach</id>
    <title>O łącznikach i myślnikach w metrze, czyli dlaczego typografii powinno się uczyć w szkołach</title>
    <link href="http://plblog.danieljanus.pl/2010/11/07/o-lacznikach-i-myslnikach/"/>
    <updated>2010-11-07T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Lubię ekrany informacyjne zainstalowane w metrze warszawskim, tak na stacjach, jak i w pociągach; pozwalają nie nudzić się, gdy akurat nie mam pod ręką niczego innego do czytania. Jednak treść prezentowana na tych wyświetlaczach pozostawia często wiele do życzenia, także pod względem jej prezentacji.&lt;/p&gt;&lt;img src="/img/blog/metro.jpg"&gt;
&lt;p&gt;Przykład można zobaczyć na powyższym zdjęciu. Rzuca się w oczy błąd typograficzny: w funkcji myślnika, a więc otoczonego odstępami znaku oddzielającego dwie odrębne części myśli zawartej w wypowiedzeniu, użyty jest tu dywiz (-) zamiast dłuższej pauzy (—) lub półpauzy (–).&lt;/p&gt;&lt;p&gt;Na &lt;a href="http://pl.wikipedia.org/wiki/Pauza_(znak_typograficzny)#Zastosowanie"&gt;Wikipedii&lt;/a&gt; można znaleźć dobre omówienie różnic między tymi znakami, nie będę się więc rozpisywał na ten temat.&lt;/p&gt;&lt;p&gt;To częsty błąd, i ktoś mógłby powiedzieć, że się czepiam. Zapewne tak; jestem wyczulony na tego typu pomyłki. Gdy widzę taki błąd na czyjejś prywatnej stronie internetowej, przechodzę nad nim do porządku dziennego. Gdybym wiedział, że to jednorazowa wpadka i w innych artykułach znaki interpunkcyjne stosowane są poprawnie — zmełłbym w ustach przekleństwo. Jest jednak inaczej. &lt;em&gt;Za każdym razem&lt;/em&gt;, kiedy na ekranach metra powinna pojawić się gdzieś pauza, widzę w tym miejscu dywiz. Uważam to za niechlujstwo godne publicznego piętnowania.&lt;/p&gt;&lt;p&gt;Te komunikaty są dziennie czytane przez dziesiątki tysięcy osób. Błąd widziany wielokrotnie przenika w końcu do podświadomości, stając się złym nawykiem. Ten sam zarzut można zresztą podnieść w stosunku do stron internetowych Gazety Wyborczej, z której — jak widać — pochodzi notka ze zdjęcia.&lt;/p&gt;&lt;p&gt;Zapytajmy jednak, skąd wziął się błąd? Zapewne osoby przygotowujące notki mają tyle pracy, że nie mają czasu na typograficzną redakcję. Tego typu praca może być jednak z powodzeniem wykonywana automatycznie, przez oprogramowanie wyświetlające artykuły. Każdy procesor tekstu zamienia otoczone spacjami łączniki na myślniki w czasie pisania; dla stron internetowych istnieje &lt;a href="http://daringfireball.net/projects/smartypants/"&gt;SmartyPants&lt;/a&gt; i jego odpowiedniki. Czasy, kiedy taka zamiana była utrudniona technicznie, minęły wraz z upowszechnieniem się Unikodu. Szkoda, że tu tego zabrakło.&lt;/p&gt;&lt;p&gt;A zabrakło dlatego, że świadomość typograficzna w polskim społeczeństwie (a więc i u autora oprogramowania) jest niska. Uczymy się w szkole, że nie należy zostawiać jednoliterowych słów na końcach linijek (por. wystające &lt;em&gt;w&lt;/em&gt; na zdjęciu), że na początku akapitów robi się wcięcia, że marginesy są istotne — i to wszystko. Ta wiedza wystarcza do zapewnienia estetyki tekstów pisanych ręcznie, ale w dobie komputerów i Internetu to nie wszystko. Kiedyś zdecydowana większość tekstu drukowanego, z jakim mieli styczność ludzie, składana była przez zawodowych zecerów. Dziś każdy może wydrukować cokolwiek na drukarce, ale nie każdy potrafi sprawić, aby wynik jego pracy był estetyczny.&lt;/p&gt;&lt;p&gt;Podstawy typografii powinny być nauczane w szkole podstawowej. Nie trzeba do tego osobnego przedmiotu; wystarczy kilka godzin na lekcjach polskiego lub plastyki. Gdyby więcej osób znało różnicę między cudzysłowem a znakiem cala, świat stałby się ładniejszy.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-06:post:zaczynamy</id>
    <title>Zaczynamy</title>
    <link href="http://plblog.danieljanus.pl/2010/11/06/zaczynamy/"/>
    <updated>2010-11-06T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Jeszcze jeden blog? Nie dość było jednego, i tak nieczęsto uaktualnianego? A jednak.&lt;/p&gt;&lt;p&gt;Od jakiegoś czasu prowadzę nieregularny &lt;a href="http://blog.danieljanus.pl"&gt;blog po angielsku&lt;/a&gt;. Zbierając pomysły na notki zauważyłem, że część z nich ma charakter trochę hermetyczny, raczej niezrozumiały dla szerokiej, angielskojęzycznej publiczności. Teksty, które dotyczą lokalnych, warszawskich spraw albo języka polskiego, nie nadawałyby się na angielskiego bloga. Pomyślałem też, że pisząc po polsku o Clojure mógłbym przyczynić się do popularyzacji tego języka wśród polskich programistów.&lt;/p&gt;&lt;p&gt;Jest to też dobra okazja do wypróbowania nowej platformy blogowej. O istnieniu &lt;a href="http://posterous.com"&gt;Posterous&lt;/a&gt; wiedziałem od dawna, ale nie byłem świadom rozlicznych ułatwień, jakie oferuje, a do których się przyzwyczaiłem pisząc starego bloga. Zobaczymy, jak się sprawdzi. Kusi mnie, żeby się rozpisać więcej, ale nie chcę, by ta pierwsza notka przerodziła się w długie rozważania na temat zalet i wad systemów blogowych; poużywam trochę i napiszę osobną notkę na ten temat.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
</feed>
