<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom"><title>Radarek bloguje - blog programisty</title><subtitle>Wpisy z dziennika internetowego Jogger, wspomaganego przez Jabbera</subtitle><id>http://radarek.jogger.pl/atom/content/html/15</id><link href="http://radarek.jogger.pl/" /><updated>2012-05-25T13:01:50Z</updated><author><name>Radosław Bułat</name></author><generator uri="http://jogger.pl/" version="1.0">JoggerPL</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/radarek-blog" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="radarek-blog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title>Forkuj z głową.</title><link href="http://radarek.jogger.pl/2010/04/19/forkuj-z-glowa/" /><id>http://radarek.jogger.pl/2010/04/19/forkuj-z-glowa/</id><updated>2010-04-19T10:02:57Z</updated><content type="html">&lt;p&gt;Stwierdzenie, że idea &lt;a href="http://pl.wikipedia.org/wiki/Free_software"&gt;wolnego oprogramowania&lt;/a&gt; to wspaniała rzecz nie będzie zbyt odkrywcze. Na tej idei może skorzystać praktycznie każdy, niezależnie czy jest wielką korporacją, średnią lub małą firmą czy też "niedzielnym" koderem. Czyż to nie wspaniałe, że można zbudować praktycznie dowolnie skomplikowane oprogramowanie i nie trzeba za to płacić ogromnych pieniędzy?&lt;/p&gt;
&lt;p&gt;Świat ten rządzi się swoimi prawami. Istnieje głównie dlatego, że są ludzie, którzy dzielą się swoim kodem (nie oszukujmy się, większość z nas tego nie robi), rzecz jasna tylko ten dobry jest zauważany i używany przez innych. Co lepsi urastają nawet do miana "celebrytów". Któż z programistów Rubiego nie zna takich osób jak Yehuda Katz czy też Ryan Bates (ten ostatni jest przykładem tego iż nie zawsze chodzi o oprogramowanie)?&lt;/p&gt;
&lt;p&gt;Jedną z idei towarzyszącej temu ekosystemowi jest &lt;a href="http://en.wikipedia.org/wiki/Fork_%28software_development%29"&gt;"forkowanie"&lt;/a&gt; (na język polski trzeba by było to przetłumaczyć jako "skopiuj kod, na przemian udoskonalaj go i dziel się z innymi"). Niektórzy mogą odnieść wrażenie, że to zjawisko jest dosyć młode i jest dzieckiem twórców &lt;a href="http://github.com/"&gt;GitHuba&lt;/a&gt;. Prawda jest jednak taka, że to zjawisko jest stare jak świat, a wspomniany serwis tylko go spopularyzował czy też wręcz uczynił prostackim (bo czymże jest to jedno kliknięcie w przycisk "fork"?).&lt;/p&gt;
&lt;p style="width: 100%; overflow-x: auto;"&gt;&lt;img class="single" src="http://img94.imageshack.us/img94/6530/forkbutton1.png"&gt;&lt;/p&gt;
&lt;p&gt;Powiem Wam, że to co zrobiono na GitHubie było bardzo nieprzemyślane i głupie. Zanim stwierdzisz, że bredzę przeczytaj proszę do końca to co mam Ci do powiedzenia.&lt;/p&gt;
&lt;p&gt;Cofnijmy się do czasów gdy jeszcze GitHub nie istniał. Każdy kto chciał rozwijać swoją wersję jakiegoś projektu (czyli sforkować) mógł to robić. Nie istniała żadna techniczna przyczyna by tego nie robić. Jednakże czynili to nieliczni. Po pierwsze wymagało to odrobinę wysiłku (skopiuj kod, załóż własne repozytorium), po drugie trzeba było mieć konkretny pomysł na własny fork. Że co, pomysł? Ano właśnie tak! Fork bez konkretnego celu jest niczym innym jak zbędną kopią oryginału. To, że dorzucisz do niego kilka swoich zmian (które najczęściej wynikają z jakiejś konkretnej potrzeby i nie przyda się innym) nie czyni go czymś użytecznym. Wręcz przeciwnie. Nikogo nie obchodzi to, że potrzebowałeś opcji "--bardzo-specjalna-zachcianka" i dlatego zrobiłeś forka. Bądź tak miły i zachowaj te zmiany dla siebie i nie pokazuj je innym.&lt;/p&gt;
&lt;p&gt;Tak jak wspomniałem - fork musi mieć konkretny cel. Cel, które nie da się zrealizować w projekcie "matce". To, że autor nie chce zmergować Twojego wspaniałego patcha z "bardzo specjalną zachcianką" ciągle nie jest wystarczającym powodem (gdyby tak robili wszyscy to ich kod zamieniłby się bardzo szybko w coś brzydkiego i cuchnącego). Nie ma nic złego w tym, że kopiujesz czyjś kod (oczywiście jeśli pozwala na to licencja), ale wystawianie go dla innych i nazywanie tego Twoim własnym forkiem jest kłamstwem w żywe oczy.&lt;/p&gt;
&lt;p&gt;Najgorsze jest to że robi się niepotrzebny bałagan. Są takie projekty, że ciężko jest rozróżnić który projekt jest tym oryginalnym. Tylko dlatego, że autorowi nie chciało się napisać w pliku README "Hej, to tylko mój prywatny fork gdzie eksperymentuję z bardzo specjalną zachcianką. Używaj proszę wersji oryginalnej." Wiem, że GitHub pokazuje umieszcza napis "forked from", ale to wciąż za mało. Nie pozwala mi to rozróżnić forków stworzonych bo taką zachciankę miał forkujący od forków, które faktycznie wnoszą coś nowego (innego) do projektu. Tym bardziej, że w przypadku gdy projekt zostanie po prostu sklonowany, a potem wypchany ze zmianami taki napis się nie pojawi (a powinien to być bardziej naturalny sposób powstawania forków). Nie mówiąc już o tym, że skopiowany projekt może być trzymany zupełnie gdzie indziej.&lt;/p&gt;
&lt;p&gt;Wspomniany bałagan najlepiej widać w grafie sieci połączeń między forkami. Za przykład weźmy projekt &lt;a href="http://github.com/carlhuda/bundler"&gt;bundler&lt;/a&gt;.&lt;/p&gt;
&lt;p style="width: 100%; overflow-x: auto; text-align: center;"&gt;&lt;img class="single" src="http://img210.imageshack.us/img210/7105/bundlernetworkgraph.png"&gt; &lt;a href="http://github.com/carlhuda/bundler/network"&gt;Obejrzyj pełną wersję grafu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To tylko część grafu, ale widzimy co najmniej 3 forki, które mają po 1 commicie. Podejrzewam, że intencją autorów było poprawienie jakiejś małej rzeczy i namówienie autora oryginalnego projektu do zmergowania zmian. Takie kopie w ogóle nie powinny być widoczne dla świata bo i po co? Ten graf miał na celu pokazanie jak wygląda rozwijanie projektu, a przez ten bałagan nic z niego nie wynika. Ba, spróbujcie obejrzeć sobie ten graf dla projektu &lt;a href="http://github.com/rails/rails"&gt;rails/rails&lt;/a&gt;. &lt;a href="http://github.com/rails/rails/network"&gt;Niestety nie jest to możliwe&lt;/a&gt; a winowajcami są wszyscy Ci, którzy sforkowali railsy tylko dlatego, że robi się to załatwo.&lt;/p&gt;
&lt;p&gt;Taki graf miałby sens gdyby było tak jak wcześniej napisałem. Wtedy zawierałby 2, 3, może 5 albo 10 (ale nie 1000!) takich pod projektów, a każdy z nich miał swój konkretny cel (np. "lepszy GC dla Rubiego"). Widać byłoby faktycznie, że ludzie pracują nad &lt;strong&gt;nowymi&lt;/strong&gt; wersjami, które mają konkretne zmiany i które nie są mergowane przez autora głównego projektu.&lt;/p&gt;
&lt;p&gt;Co można by było zrobić by poprawić tę sytuację? Jeśli chodzi o GitHuba to uważam, że sforkowanie projektu nie powinno być od razu widoczne. Niestety to raczej nie wchodzi w grę ponieważ w wersji darmowej nie można posiadać prywatnych projektów. Musimy zatem wziąć w swoje ręce. Po pierwsze nie powinniśmy tak lekkomyślnie tworzyć forków, a po prostu tworzyć lokalne klony na komputerze. Po drugie jeśli utworzyłeś fork tylko po to by prosić autora o tzw. "pull request" to napisz w README, że zainteresowani powinni używać wersji oryginalnej. Wydaje mi się, że dobrym pomysłem jest utworzenie osobnego brancha dla naszych zmian i pozostawienie w spokoju mastera (wtedy nie powinno być niepotrzebnych połączeń w grafie). Po trzecie jeśli faktycznie tworzysz fork (czyli wprowadzasz nową większą ideę do projektu) to opisz to w README. Napisz jaki cel przyświeca Twojemu forkowi i dlaczego zdecydowałeś się na swoją wersję zamiast gnębić autora o zmergowanie zmian.&lt;/p&gt;
&lt;p&gt;Chociaż nie ma pisanych zasad by robić tak czy inaczej to i tak takie zasady istnieją. Dzięki niepisanym zasadom instalacja większości projektów w C to "make &amp;amp;&amp;amp; make install". Takie niepisane zasady powinny także obowiązywać w kwestii forkowania. Inaczej sami sobie tworzymy dżunglę, w której tylko nieliczni sobie poradzą. Mam nadzieję, że chociaż część z moich argumentów ma dla Ciebie sens.&lt;/p&gt;
&lt;p&gt;ps. Dowodem mojej tezy może być fakt, że railsy mają 929 forków (sic!), ale &lt;a href="http://ryanbigg.com/2010/04/want-it-give/"&gt;wciąż mają otwartych 900 ticketów!&lt;/a&gt; Jak widać ilość nie przekłada się na jakość.&lt;/p&gt;
</content><category term="git" label="Git" /><category term="programowanie" label="Programowanie" /><category term="fork" label="fork" /><category term="github" label="github" /><category term="opensource" label="opensource" /><category term="dobre-praktyki" label="dobre praktyki" /></entry><entry><title>Git - system kontroli wersji, który powinien znać każdy</title><link href="http://radarek.jogger.pl/2009/08/10/git-system-kontroli-wersji-ktory-powinien-znac-kazdy/" /><id>http://radarek.jogger.pl/2009/08/10/git-system-kontroli-wersji-ktory-powinien-znac-kazdy/</id><updated>2009-08-10T09:15:56Z</updated><content type="html">&lt;p&gt;&lt;img src="http://img195.imageshack.us/img195/227/gitlogo.png" style="float: left;"&gt; Witam po kolejnej długiej przerwie. Ponieważ ostatnio nie za bardzo mam czas na pisanie porządnych wpisów (czyli takich gdzie muszę dobrze przemyśleć temat, poszperać w internecie, dodać swoje przemyślenia - a zajmuje dobre kilka godzin) to tym razem trochę w innym stylu. Bez kodu, bez przykładów. Ba, temat nawet nie będzie dotyczył Rubiego!&lt;/p&gt;
&lt;p&gt;A jak nie chodzi o Ruby to chodzi o &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;! Chciałbym podzielić się z Wami moimi spostrzeżeniami na temat tego narzędzia.&lt;/p&gt;
&lt;p&gt;Mniej więcej rok temu zetknąłem się z gitem. Pamiętam, że obejrzałem film video &lt;a href="http://www.youtube.com/watch?v=4XpnKHJAok8"&gt;Linus Torvalds on git&lt;/a&gt;. Chociaż nie do końca trafił do mnie (nie potrafiłem np. pojąć idei rozproszoności), to sposób w jaki Linus opowiadał o nim sprawił, że miałem ochotę zgłębić temat. Tak też zrobiłem. Opinie internautów, jak i moje pierwsze doświadczenie z gitem, potwierdzały, że jest to świetne narzędzie. Niestety okazało się także, że jest to stosunkowo trudne narzędzie.&lt;/p&gt;
&lt;p&gt;Początkowe trudności wynikały głównie z dwóch powodów. Po pierwsze git pokazał, że znane mi do tej pory narzędzia do kontroli kodu źródłowego (czyli głównie &lt;code class="inline"&gt;svn&lt;/code&gt;, a w przeszłości także &lt;code class="inline"&gt;cvs&lt;/code&gt;), używam w dosyć prymitywny sposób i ogranicza się to mniej więcej do komend "update, commit, status". Po drugie, toporność pewnych czynności (powiedzmy, że "bardziej zaawansowanych") jakie należy wykonać we wspomnianym &lt;code class="inline"&gt;svn&lt;/code&gt; sprawiała, że albo ich świadomie nie używałem, albo po prostu nie wiedziałem, że istnieją. Z moich obserwacji wynika, że większość programistów ma podobne doświadczenie z &lt;code class="inline"&gt;svn&lt;/code&gt; i podobnymi systemami kontroli wersji. Poniższej zamieszczam dosyć luźno opisane aspektu gita, które najbardziej mnie zaskoczyły, utkwiły w pamięci i zmieniły sposób w jaki teraz tego typu narzędzi używam.&lt;/p&gt;
&lt;h3&gt;Tworzenie nowego repozytorium&lt;/h3&gt;
&lt;p&gt;Git od samego początku zachwyca. Założenie nowego repozytorium to kwestia jednego polecenia &lt;code class="inline"&gt;git init&lt;/code&gt;, wydanego z katalogu z projektem. Choć mogłoby się wydawać, że nie jest to zbyt istotne (w końcu jak często zakładamy nowe repozytorium?) to dzięki temu otwierają się przed nami nowe możliwości. Nie raz zdarzało mi się, że musiałem "pogrzebać" w jakimś katalogu/kodzie, ale nie chciałem stracić starej zawartości. Bawienie się w kopie jest uciążliwe, teraz używam do tego gita. Co ciekawe git tworzy tylko jeden katalog o nazwie &lt;code class="inline"&gt;.git&lt;/code&gt;, zatem ewentualne usunięcie repozytorium to kwestia skasowania tego katalogu.&lt;/p&gt;
&lt;h3&gt;Rozproszoność&lt;/h3&gt;
&lt;p&gt;Jedną z najczęściej powtarzanych i podkreślanych cech gita jest rozproszoność. W uproszczeniu polega to na tym, że każdy kto skopiuje (sklonuje) repozytorium ma wszystkie zawarte w nim informacje. W szczególności jest to &lt;strong&gt;cała&lt;/strong&gt; historia. Wszystkie te informacje zawarte są we wspomnianym katalogu &lt;code class="inline"&gt;.git&lt;/code&gt;. Od momentu wykonania operacji &lt;code class="inline"&gt;clone&lt;/code&gt; stajemy się właścicielami własnego, prywatnego, lokalnego repozytorium. Jedynie operacje synchronizacji repozytoriów (w końcu musimy wymienić z innymi dane) wymagają dostępu do sieci. Każda inna jest lokalna i nie dość, że mogą nam odciąć kabel a i tak będzie wszystko działać, to jeszcze brak opóźnień operacji sieciowych odczujemy momentalnie (przypomnij sobie te kilkunastosekundowe lagi po wydaniu polecania &lt;code class="inline"&gt;svn log&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Rozmiar repozytorium&lt;/h3&gt;
&lt;p&gt;Pomimo tego, że klonując repozytorium gita pobieramy całą jego historię, rozmiar katalogu z projektem nie jest o wiele większy niż gdybyśmy użyli pojedynczego "checkouta" svn, a często jest nawet większy (pamiętajmy, że svn tworzy ogromną ilość podkatalogów o nazwie &lt;code class="inline"&gt;.svn&lt;/code&gt;. Dla przykładu repozytorium ze źródłami &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; pod kontrolą gita zajmował 43MB, zaś pod kontrolą svna 61MB (&lt;a href="http://whygitisbetterthanx.com/#git-is-small"&gt;źródło&lt;/a&gt;). Z kolei projekt, przy którym obecnie pracuję, przy ponad 3000 rewizjach zajmuje ~22MB z czego 7MB to &lt;strong&gt;cała&lt;/strong&gt; historia, a pozostałe 15MB to rozmiar pojedynczego checkouta.&lt;/p&gt;
&lt;h3&gt;Gałęzie&lt;/h3&gt;
&lt;p&gt;Jest to jedna z tych cech gita, o których mógłbym pi(s)ać peany na jej cześć. Przed gitem nigdy nie używałem gałęzi. Projekty były "jednotokowe" (czyli "trunk" i cała naprzód). Brzmi znajomo? Podejrzewam, że tak...&lt;/p&gt;
&lt;p&gt;Git maksymalnie upraszcza prace na gałęziach. Tworzenie gałęzi trwa ułamek sekundy (bo tyle trwa utworzenie i zapis 41 bajtów do pliku) a przełączanie się między nimi jest tylko ciut wolniejsze (w praktyce zależy od ilości różnic między przełączanymi gałęziami). Co ciekawe w gicie pracujemy na różnych gałęziach używając tego samego katalogu roboczego - zupełnie inaczej niż w svn, gdzie każda gałąź (tag) jest osobną kopią. Nie musimy się więc ograniczać co do ich ilości (osobiście tworzę osobny branch dla każdej zmiany). Chociaż jeśli komuś ten sposób nie podoba się to nic nie stoi na przeszkodzie by utworzyć dodatkowe klony w oddzielnych katalogach (tylko po co?).&lt;/p&gt;
&lt;h3&gt;Lokalność&lt;/h3&gt;
&lt;p&gt;Wspomniałem już o tym, że git jest rozproszony. Pochodną tej cechy jest fakt, że większość operacji na repozytorium jest lokalna. Nie doceniłem tego faktu, aż do wspomnianego momentu, gdy praca z repozytorium była czymś więcej niż "update, commit, status". I tak na prawdę nie chodzi mi o to, że możemy pracować odpięci od sieci (przecież niedużo nam trzeba, żeby mieć internet w lodówce), ale o prędkość i niezależność poleceń. Przykład? A choćby najprostsze &lt;code class="inline"&gt;git log&lt;/code&gt; trwa ułamek sekundy. Chcąc wyszukać commity, które zawierają w zmianach słowo "foo", wpisuję &lt;code class="inline"&gt;git log -S"foo"&lt;/code&gt; i wręcz natychmiast dostaję wyniki (dzięki temu szybko znajdę informację kto i kiedy dodał/usunął daną klasę/metodę itp.). Gdy czeka nas jakieś większe łączenie gałęzi, możemy pociągnąć wszystkie zmiany ze zdalnego repozytorium (&lt;code class="inline"&gt;git fetch&lt;/code&gt;) i od tego momentu działać lokalnie. Wspomniana lekkość i łatwość tworzenia branchy, daje nam możliwość bezstresowego próbowania różnych kombinacji. Jeśli coś nam nie wyjdzie, zawsze możemy szybko wrócić do stanu początkowego.&lt;/p&gt;
&lt;h3&gt;Perełki&lt;/h3&gt;
&lt;p&gt;Na koniec zostawiłem kilka ciekawych przykładów. Są to polecenia/narzędzia, która bardzo pozytywnie mnie zaskoczyły. Jednym z najczęściej używanych przeze mnie narzędzi jest &lt;code class="inline"&gt;gitk&lt;/code&gt;. Jest to graficzne narzędzie, które dosyć ładnie zwizualizuje nam powiązania commitów. Z uwagi na niezbyt nowoczesny wygląd GUI, może być z początku odbierany negatywnie, ale szybko się okazuje, że tak nie jest. W szczególności używam go do szybkiego przeglądu ostatnich zmian dokonanych przez innych (czyli robię fetch i przeglądam commity od master..origin/master).&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;a href="http://img33.imageshack.us/img33/565/gitk.png" target="_blank"&gt;&lt;img class="single" src="http://img269.imageshack.us/img269/425/gitk2.png"&gt;&lt;/a&gt; Lista commitów, łatwy dostęp do diffów i inne informacje, które prezentuje nam gitk.&lt;/p&gt;
&lt;p&gt;&lt;code class="inline"&gt;git cherry-pick&lt;/code&gt; pozwala na dołączania zmian z wybranych commitów z jednej gałęzi do drugiej. Przydatne jeśli posiadasz w projekcie gałąź rozwojową i produkcyjną i prowadzisz aktualne prace na tej pierwszej, ale czasem chciałbyś pociągnąć pewne zmiany do tej drugiej. Wtedy jednym poleceniem &lt;code class="inline"&gt;git cherry-pick SHA1&lt;/code&gt; pociągasz zmiany i jeśli nie było konfliktu od razu jest tworzony commit.&lt;/p&gt;
&lt;p&gt;Wyobraź sobie taką sytuację. Odpalasz aplikację, klikasz tu i tam, po czym okazuje się, że coś nie działa. Cóż, cudów nie ma, trzeba zlokalizować buga i go naprawić. I tu pojawiają się schody, bo pomimo iż dokładnie wiesz, która linijka powoduje błąd, to nie jesteś w stanie go poprawić bo błąd de facto siedzi w innej części systemu (nie trudno sobie wyobrazić takiej sytuacji, skoro aplikacja może być zależna od wielu zewnętrznych czynników takich jak użyte biblioteki, konfiguracja, system operacyjny, baza danych itp.). Jeśli dodatkowo towarzyszą Ci myśli "przecież to kiedyś działało!" to prawie na pewno potrzebujesz pomocy w postaci &lt;code class="inline"&gt;git bisect&lt;/code&gt; (mój ulubieniec). Idea jest prosta: znajdź pierwszy commit, który spowodował, że coś przestało działać. Jeśli wiesz, że kod sprzed tygodnia działał to informujesz o tym gita, następnie wskazujesz commit niedziałający (np. aktualny master) i rozpoczynasz binarne przeszukiwanie. Git pobiera jakąś wersję projektu, a Ty tylko odpowiadasz czy ten błąd występuje czy też nie. W ten sposób bardzo szybko (przypominam, że wyszukiwanie jest binarne) zostanie znaleziony commit, po którym to nasze "coś" przestało działać. Przyglądając się zmianom, które wniósł, możemy dosyć szybko wywnioskować, co jest przyczyną problemu (przykładowo, ktoś dokonał aktualizacji jakiejś biblioteki).&lt;/p&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;Uważam, że git to wspaniałe narzędzie. Nie jest to lek na całe zło zarządzania projektem od strony kodu źródłowego, ale ułatwia ogromną ilość spraw. Jest na pewno sporo trudniejszy do opanowania niż wspominany svn, ale każda chwila spędzona przy nauce tego narzędzia, zwraca nam się bardzo szybko. Moja osobista rada jest taka: jeśli w swoim zespole chcielibyście użyć tego narzędzia, to dobrze jest by przynajmniej jedna osoba miała dosyć dobrze opanowanego gita i w razie problemów (a na początku będą) będzie interweniować. W przeciwnym wypadku można się zniechęcić i nie dać szansy doznać olśnienia, którego niejeden doznał. Życzę miłego gitowania!&lt;/p&gt;
</content><category term="git" label="Git" /><category term="narzedzia" label="Narzędzia" /><category term="techblog" label="Techblog" /><category term="svn" label="svn" /><category term="scm" label="scm" /></entry><entry><title>Projekt Eulera - wyzwanie dla programistów</title><link href="http://radarek.jogger.pl/2009/04/06/projekt-eulera-wyzwanie-dla-programistow/" /><id>http://radarek.jogger.pl/2009/04/06/projekt-eulera-wyzwanie-dla-programistow/</id><updated>2009-04-06T00:35:14Z</updated><content type="html">&lt;p&gt;&lt;img src="http://triton.imageshack.us/Himg14/scaled.php?server=14&amp;amp;filename=180pxleonhardeuler.jpg&amp;amp;xsize=640&amp;amp;ysize=480" style="float: left;" alt="Leonhard Euler"&gt;&lt;/p&gt;
&lt;p&gt;Muszę przyznać, że od zawsze uwielbiałem zadania, które wymagają myślenia, główkowania i kombinowania. W okresie mojej podstawówki dotyczyło to głównie rozwiązywania zadań z matematyki, potem (gdy tylko zetknąłem się z informatyką w LO) przeszło to na zadania algorytmiczne. To były zresztą moje początki z programowaniem i do dzisiaj mam ogromny sentyment do tej dziedziny informatyki.&lt;/p&gt;
&lt;p&gt;Ostatnio na &lt;a href="http://forum.rubyonrails.pl/"&gt;forum rubyonrails.pl&lt;/a&gt; zaproponowano, by rozwiązywać zadania ze &lt;a href="http://projecteuler.net/"&gt;strony o nazwie "Project Euler"&lt;/a&gt;. Jak się okazało zadania są na pograniczu matematyki, logiki, kombinatoryki, algorytmiki i większość ma tę własność, że bez pomocy komputera nie da się ich rozwiązać.&lt;/p&gt;
&lt;p&gt;To co mnie urzekło od samego początku to fakt, że nie zawsze chodzi o znalezienie najbardziej wydajnego rozwiązania. Nie ma bowiem narzuconego z góry żadnego limitu (na stronie musimy podać otrzymane rozwiązanie i sprawdzana jest jego poprawność). Często naiwne rozwiązanie wystarcza, co nie znaczy, że napisanie programu jest banalne! Barierą może być sam język, w którym piszemy. Sporo zadań wymaga obliczeń na dużych liczbach lub operowania danymi tekstowymi, co może okazać się bardzo niewygodne (nie mylić z "niemożliwe") w takich językach jak C/C++ czy Java. Z kolei te same zadania okazują się przysłowiową "bułką z masłem" jeśli użyjemy wysokopoziomowych języków, np. Ruby lub Python (jeśli oczywiści dana funkcjonalność występuje).&lt;/p&gt;
&lt;p&gt;Weźmy dla przykładu &lt;a href="http://projecteuler.net/index.php?section=problems&amp;amp;id=13"&gt;zadanie 13ste&lt;/a&gt;, w którym chodzi o zsumowanie stu 50-cio cyfrowych liczb. Do reprezentacji takich liczb potrzeba około 167 bitów, a więc taki typ (znany np. z C, Javy) jak &lt;code class="inline"&gt;long&lt;/code&gt; po prostu odpada. Nawet jak przebrniemy przez ten problem (możemy napisać własną obsługę dużych liczb lub użyć gotowej biblioteki, np. &lt;a href="http://gmplib.org/"&gt;gmp&lt;/a&gt;) to musimy poradzić sobie z przetworzeniem tekstu zawierającego te liczby. I znowu okaże się, że w przypadku języka C (i nie tylko) takie operacje są dosyć uciążliwe.&lt;/p&gt;
&lt;p&gt;Spójrzmy na rozwiązania tego samego problemu w językach Java i Ruby:&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;E013.java&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="java"&gt;import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;

public class E013 {
  public static BigInteger sumOf(Iterable&amp;lt;BigInteger&amp;gt; numbers) {
    BigInteger sum = new BigInteger("0");

    for (BigInteger number: numbers) {
      sum = sum.add(number);
    }
    return sum;
  }

  public static void main(String[] args) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader(new File("013.txt")));
    String line;

    ArrayList&amp;lt;BigInteger&amp;gt; numbers = new ArrayList&amp;lt;BigInteger&amp;gt;();
    while ((line = reader.readLine()) != null) {
      numbers.add(new BigInteger(line));
    }
    BigInteger sum = sumOf(numbers);
    System.out.println(sum.toString().substring(0, 10));
  }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;013.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;puts File.read("013.txt").split("\n").inject(0) {|sum, n| sum + n.to_i }.to_s[0, 10]
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Bo czasem jabłko jest lepsze od gruszki&lt;/h3&gt;
&lt;p&gt;Tak, to są równoważne programy! Oba robią to samo: wczytują kolejne linie z pliku &lt;code class="inline"&gt;013.txt&lt;/code&gt;, konwertują je na liczby i sumują. Być może dla programistów niezaznajomionych z Ruby, wersja w tym języku może wydawać się trudna w odczytaniu, ale zapewniam Was, że tak nie jest. Wystarczy szybkie przeskanowanie tej jednej linijki by przekonać się, że program składa się z pięciu kroków: wczytaj z pliku (&lt;code class="inline"&gt;File.read("013.txt")&lt;/code&gt;), skonwertuj do tablicy dzieląc wedle znaku nowej linii (&lt;code class="inline"&gt;split("\n")&lt;/code&gt;), zsumuj skonwertowane do liczby kolejne linie (&lt;code class="inline"&gt;inject(0) {|sum, n| sum + n.to_i }&lt;/code&gt;), skonwertuj do łańcucha (&lt;code class="inline"&gt;to_s&lt;/code&gt;) i pobierz pierwsze 10 znaków (&lt;code class="inline"&gt;[0, 10]&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Wspomniany inject, który w tym wypadku sumuje liczby (&lt;code class="inline"&gt;arr.inject(0){|sum, item| sum + item }&lt;/code&gt;, jest już klasycznym &lt;a href="/2007/10/12/idiomatyczny-ruby/"&gt;idiomem&lt;/a&gt; tego języka.&lt;/p&gt;
&lt;h3&gt;A czasem i nawet gruszka lepsza od jabłka&lt;/h3&gt;
&lt;p&gt;Z kolei weźmy &lt;a href="http://projecteuler.net/index.php?section=problems&amp;amp;id=5"&gt;zadanie 5te&lt;/a&gt;, w którym musimy znaleźć najmniejszą liczbę naturalną, która będzie podzielna bez reszty przez wszystkie liczby całkowite z zakresu 1..20. Pozostańmy przy naiwnym rozwiązaniu, które wynika wprost z zadania. Sprawdzając kolejne liczby 2, 3, 4, 5 będziemy testować czy dzieli się bez reszty przez liczby z zakresu 2..20 (1 omijamy bo wiemy, że każda liczba całkowita jest podzielna przez nią bez reszty). Zacznijmy tym razem od wersji w Rubym.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;005_1.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;puts 1.upto((2..20).inject(:*)).each {|n| break n if (2..20).all? {|e| n % e == 0} } 
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Odpalając ten program na ruby1.9 dostaję odpowiedź po ponad 3 minutach. Na ruby1.8 nawet nie sprawdzam - będzie na pewno jeszcze dłużej. Spójrzmy zatem na rozwiązanie w javie.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;E005.java&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="java"&gt;public class E005 {
  public static long product(int from, int to) {
    long result = 1;
    for (int i = from; i &amp;lt; to; i++) {
      result *= i;
    }
    return result;
  }

  public static boolean isDivisibleBy(long number, int[] divisors) {
    for (int divisor: divisors) {
      if ((number % divisor) != 0) {
        return false;
      }
    }
    return true;
  }

  public static void main(String[] args) {
    long min = 1;
    long max = product(2, 20);
    int[] divisors = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

    for (long i = min; i &amp;lt;= max; i++) {
      if (isDivisibleBy(i, divisors)) {
        System.out.println(i);
        break;
      }
    }
  }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Tym razem dostaję odpowiedź po około 10 sekundach. Oczywiście znowu wymagało to dłuższego kodu, ale zdecydowanie wolę czekać 10 sekund niż 3 minuty...&lt;/p&gt;
&lt;p&gt;Nie myślcie, że te dwa przykłady mają prowadzić do wielkiej konkluzji. Raczej chodzi o pokazanie, co tracimy/zyskujemy decydując się na dany język. Java i Ruby są tu tylko przykładem. Mógłbym równie dobrze pokazać rozwiązania w Pascalu, Php, Pythonie czy też Erlangu (myślę, że to mogłoby być bardzo ciekawe doświadczenie). Zachęcam Was spróbowania własnych sił. Spróbujcie rozwiązywać te same zadania w kilku językach i wyciągajcie własne wnioski.&lt;/p&gt;
&lt;h3&gt;Ucz się sztuki programowania od innych&lt;/h3&gt;
&lt;p&gt;Ktoś kiedyś powiedział, że najważniejszym źródłem wiedzy jesteśmy my. Możemy uczyć się na własną rękę, ale i tak najwięcej nauczymy się od innych. Ta prawda sprawdza się idealnie jeśli chodzi o programowanie (czy też wiedzę związaną z komputerami). Niezależnie od użytego języka, praktycznie każdy problem można rozwiązać na mnóstwo sposobów, które będą mniej lub bardziej dobre. Czasem to kwestia znajomości standardowej lub zewnętrznej biblioteki, dobrze wykorzystanego API czy też zastosowanego algorytmu.&lt;/p&gt;
&lt;p&gt;Dzięki tym zadaniom i rozwiązaniom innych mogłem przekonać się o potędze biblioteki &lt;code class="inline"&gt;prime&lt;/code&gt; dostępnej od wersji 1.9 Rubiego. Zadania, które wymagają operowania na liczbach pierwszych, czynnikach pierwszych lub podzielnikach liczb okazują się banalnie proste, jeśli mamy pod ręką taką bibliotekę (a z Ruby 1.9 mamy!).&lt;/p&gt;
&lt;p&gt;Przykładowe zadanie i wykorzystanie tej biblioteki:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;http://projecteuler.net/index.php?section=problems&amp;amp;id=3

The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;003_2.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby1.9"&gt;require "prime"

puts 600851475143.prime_division.max.first
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Nawet jeśli takie przykłady można skwitować stwierdzeniem "to tylko kwestia biblioteki" to dla mnie bardzo ważny jest fakt, że na poziomie języka i jego standardowej biblioteki mogę zrobić&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;2**1000 + 15
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;aniżeli&lt;/p&gt;
&lt;pre&gt;
&lt;code class="java"&gt;System.out.println(new BigInteger("2").pow(1000).add(new BigInteger("15")));
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zapraszam Was do wspólnego rozwiązywania zadań i umieszczania ich w &lt;a href="http://rubyonrails.pl/forum/f14-Projekt-Euler-w-Ruby"&gt;przygotowanym dziale na forum.rubyonrails.pl&lt;/a&gt;. Część moich rozwiązań znajdziecie w &lt;a href="http://github.com/Radarek/project_euler/tree/master"&gt;repozytorium gita na githubie&lt;/a&gt;.&lt;/p&gt;
</content><category term="java" label="Java" /><category term="programowanie" label="Programowanie" /><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="projekt-eulera" label="projekt eulera" /></entry><entry><title>Biblioteka FFI - łączymy Ruby z C</title><link href="http://radarek.jogger.pl/2009/03/04/biblioteka-ffi-laczymy-ruby-z-c/" /><id>http://radarek.jogger.pl/2009/03/04/biblioteka-ffi-laczymy-ruby-z-c/</id><updated>2009-03-04T01:46:47Z</updated><content type="html">&lt;p&gt;Przez ostatnie dni miałem okazję zapoznać się z biblioteką &lt;a href="http://kenai.com/projects/ruby-ffi"&gt;FFI&lt;/a&gt;. (Swoją drogą, &lt;a href="http://kenai.com"&gt;kenai.com&lt;/a&gt;, czyli strona na której jest hostowany projekt, to próba stworzenia przez firmę Sun systemu, podobnego do sourceforge, githuba itp. Całość zupełnie za darmo, o pełnych możliwościach można poczytać &lt;a href="http://kenai.com/projects/help/pages/KenaiOverview"&gt;na stronie projektu&lt;/a&gt;. Aplikacja jest oparta o framework Ruby on Rails i uruchamiana przy pomocy &lt;a href="http://jruby.codehaus.org/"&gt;JRubiego&lt;/a&gt;.) Biblioteka FFI służy do łatwego łączenia kodu Rubiego z bibliotekami C.&lt;/p&gt;
&lt;h3&gt;Rozszerzenia w C - różne podejścia&lt;/h3&gt;
&lt;p&gt;Zanim opiszę zalety i sposób wykorzystania FFI, chciałbym napisać kilka zdań na temat samych rozszerzeń C dla Rubiego. Otóż istnieje kilka możliwości w tej kwestii. Jedną z podstawowych jest wykorzystanie wewnętrznego API interpretera Rubiego napisanego w C (popularnie zwanego MRI lub cRuby). Jeśli zaglądałeś kiedykolwiek w kod źródłowy MRI i widziałeś źródła np. pliku &lt;a href="http://svn.ruby-lang.org/repos/ruby/trunk/array.c"&gt;array.c&lt;/a&gt;, to kod takiego rozszerzenia pisze się praktycznie tak samo. Z tego też powodu jesteśmy zmuszeni do poznania, choćby w minimalnym stopniu, wewnętrznego API C Rubiego (zobacz &lt;a href="http://www.eqqon.com/index.php/Ruby_C_Extension"&gt;http://www.eqqon.com/index.php/Ruby_C_Extension&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Aby ułatwić choć trochę tworzenie takich rozszerzeń powstał projekt &lt;a href="http://www.zenspider.com/ZSS/Products/RubyInline/Readme.html"&gt;RubyInline&lt;/a&gt;. Jego dwoma największymi zaletami jest możliwość osadzania kodu bezpośrednio w kodzie Rubiego, a także automatyczna kompilacja takiego kodu dopiero w momencie odpalenia (skompilowane rozszerzenie jest zapisywane w katalogu ~/.ruby_inline/ by nie kompilować go za każdym uruchomieniem). Pozostałe zasady zostają takie same (ciągle operujemy na tym samym API).&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_inline01.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "rubygems"
require "inline"

class MyMath
  inline do |builder|
    builder.c "
      long add(int a, int b) {
        return a + b;
      }"
  end
end
math = MyMath.new
puts math.add(100, 300)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;400
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Z projektów, które starają się rozwiązać podobne problemy, wymienię jeszcze &lt;a href="http://www.swig.org/"&gt;SWIG&lt;/a&gt; (który potrafi generować kod rozszerzenia na podstawie plików nagłówkowych .h) i &lt;a href="http://rice.rubyforge.org/"&gt;Rice&lt;/a&gt; (pozwala na wygodne mapowanie klas C++ na klasy Rubiego). Zainteresowanych odsyłam jednak na strony domowe.&lt;/p&gt;
&lt;p&gt;Wszystkie wymienione biblioteki wymagają napisania łączącego kodu (tzw. glue code) w C oryginalnej biblioteki C i języka Ruby. Być może nie wszyscy wiedzą, ale wraz z Ruby dostajemy bibliotekę, która pozwala na dynamiczne wywoływanie kodu z, już zbudowanej, współdzielonej biblioteki (pliki o rozszerzeniach &lt;code class="inline"&gt;so&lt;/code&gt;, &lt;code class="inline"&gt;dll&lt;/code&gt;, &lt;code class="inline"&gt;dynalib&lt;/code&gt; w zależności od platformy). Jest to biblioteka Ruby/DL. Oto przykład jej wykorzystania.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_dl01.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "dl/import"

module Libc
  extend DL::Importable
  dlload "/lib/libc.so.6"

  extern "int strlen(const char *)"
end

puts Libc.strlen("foo")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;3
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Mam nadzieję, że jeszcze się nie niecierpliwisz, że piszę o wszystkim tylko nie o FFI. Rzecz jest jednak bardzo ważna, ponieważ chciałem zwrócić uwagę na pewną cechę, łączącą wyżej wymienione biblioteki - &lt;strong&gt;działają tylko z MRI (cRuby).&lt;/strong&gt; Jeszcze do nie dawna nie było z tym problemów. Przy obecnym wysypie interpreterów Rubiego, fakt, że dana biblioteka działa tylko z jednym z nich, jest ogromną wadą. Pora zatem na przedstawienie tytułowego FFI, który jak się domyślacie, potrafi współpracować nie tylko z MRI.&lt;/p&gt;
&lt;h3&gt;FFI = Foreign Function Interface&lt;/h3&gt;
&lt;p&gt;Biblioteka ta pozwala na dynamiczne wywołanie metody z biblioteki współdzielonej, podobnie jak Ruby/DL, z tą różnicą, że na chwilę obecną dostępne są wersje działające z MRI 1.8/1.9, JRuby i Rubinius. Dodatkowo posiada bardzo ładne i wygodne API.&lt;/p&gt;
&lt;p&gt;Oto prosty przykład użycia tej biblioteki.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_ffi01.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "rubygems"
require "ffi"

module Libc
  extend FFI::Library

  attach_function :strlen, [:string], :int
end

puts Libc.strlen("foo")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;3
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jak już wspomniałem, FFI ma bardzo ładne API. Dotyczy to także callbacków (czyli w C wskaźniki do funkcji). Jak przystało na wersję w Rubym robimy to za pomocą bloków! Poniższy przykład wykorzystuje funkcję &lt;code class="inline"&gt;qsort&lt;/code&gt; ze standardowej biblioteki C, która do porównywania elementów tablicy wykorzystuje przekazany wskaźnik do funkcji.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_ffi02.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "rubygems"
require "ffi"

module Libc
  extend FFI::Library

  callback :cmp, [:pointer, :pointer], :int
  attach_function :qsort, [:pointer, :int, :int, :cmp], :void
end

ARRAY_SIZE = 5

p = MemoryPointer.new(:int, ARRAY_SIZE)
p.put_array_of_int32(0, [10, 20, 3, 9, 5])

Libc.qsort(p, ARRAY_SIZE, 4) do |p1, p2|
  p1.get_int32(0) &amp;lt;=&amp;gt; p2.get_int32(0)
end

puts p.get_array_of_int32(0, ARRAY_SIZE)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;3
5
9
10
20
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Mamy także możliwość bardzo łatwego mapowania struktur do klas Rubiego. Spróbujmy napisać własną bibliotekę dzieloną w C i użyć z poziomu Rubiego.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;mylib.c&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="c"&gt;#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

struct user_info {
  char *name;
  int age;
};

struct user_info *user_info_create() {
  struct user_info *ui = malloc(sizeof(struct user_info));
  ui-&amp;gt;name = NULL;
  ui-&amp;gt;age = 0;
}

void user_info_free(struct user_info *ui) {
  if (ui-&amp;gt;name) {
    free(ui-&amp;gt;name);
  }
  free(ui);
}

void user_info_randomize_age(struct user_info *ui) {
  ui-&amp;gt;age = random() % 100;
}

void user_info_print_array(struct user_info **users, int count) {
  struct user_info *ui;
  int i;

  for (i = 0; i &amp;lt; count; i++) {
    ui = users[i];
    printf("%s ma lat %d\n", ui-&amp;gt;name ? ui-&amp;gt;name : "(null)", ui-&amp;gt;age);
  }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Do skompilowania powyższego kodu (plik mylib.c) posłużyłem się poleceniem &lt;code class="inline"&gt;gcc -O2 -fPIC -shared -Wl,-soname,libsimplemath -o mylib.so mylib.c&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Naszym zadaniem jest zmapowanie struktury &lt;code class="inline"&gt;user_info&lt;/code&gt; do klasy w Rubym. Zaprezentowany poniżej kod nie powinien sprawiać trudności, chociaż sposób operowania na klasie &lt;code class="inline"&gt;FFI::MemoryPointer&lt;/code&gt; może wydawać się z początku dosyć dziwny.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_ffi03.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "rubygems"
require "ffi"

module Mylib
  extend FFI::Library

  ffi_lib File.dirname(__FILE__) + "/mylib." + FFI::Platform::LIBSUFFIX

  class UserInfo &amp;lt; FFI::Struct
    layout  :name, :pointer,
            :age, :int
  end

  attach_function :user_info_create, [], :pointer
  attach_function :user_info_free, [:pointer], :void
  attach_function :user_info_randomize_age, [:pointer], :void
  attach_function :user_info_print_array, [:pointer, :int], :void
end

users = []
5.times do |i|
  p = Mylib.user_info_create()
  ui = Mylib::UserInfo.new(p)
  ui[:age] = rand(50) + 10
  ui[:name] = FFI::MemoryPointer.from_string("foo%d" %i)
  users &amp;lt;&amp;lt; ui
end

users_ptr = FFI::MemoryPointer.new(:pointer, users.size)
users.each_with_index do |user, i|
  users_ptr[i].put_pointer(0, user)
end
Mylib.user_info_print_array(users_ptr, users.size)

ui = users.first
puts "\n#{ui[:name].read_string} ma lat #{ui[:age]}"
Mylib.user_info_randomize_age(ui)
puts "#{ui[:name].read_string} ma teraz lat #{ui[:age]}"
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;foo0 ma lat 38
foo1 ma lat 14
foo2 ma lat 57
foo3 ma lat 52
foo4 ma lat 17

foo0 ma lat 38
foo0 ma teraz lat 83
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zagadnienia związane z FFI są zbyt obszerne (choćby kwestia zwalniania pamięci) żeby opisać je tu wszystkie, zatem na tym poprzestanę. Po więcej przykładów odsyłam na wiki projektu FFI oraz dodatkowe artykuły w języku angielskim (linki podam na końcu wpisu).&lt;/p&gt;
&lt;h3&gt;Wydajność&lt;/h3&gt;
&lt;p&gt;Do pisania takich rozszerzeń może zachęcić nas także wydajność, ale nie powinniśmy tego robić pochopnie (ktoś chętny na przepisanie railsów na C?;-)). Dla świętego spokoju zrobiłem mały (i zapewne naiwny) test wydajności. Jak zwykle w takim wypadku nie bierz tego zbyt dosłownie, zawsze dokonuj własnych pomiarów.&lt;/p&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;libfib.c&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="c"&gt;long fib(int n) {
  if (n &amp;lt; 2) {
    return n;
  } else {
    return fib(n - 1) + fib(n - 2);
  }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;~ &lt;span class="filename"&gt;ext_ruby_ffi04.rb&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require "rubygems"
require "ffi"
require "inline"

class MylibFFI
  extend FFI::Library
  ffi_lib File.dirname(__FILE__) + "/libfib." + FFI::Platform::LIBSUFFIX

  attach_function :fib, [:int], :long
end

class MylibRuby
  def self.fib(n)
    if n &amp;lt; 2
      return n
    else
      return fib(n - 1) + fib(n - 2)
    end
  end
end

class MylibInline
  inline do |builder|
    builder.prefix %Q{
      long _fib(int n) {
        if (n &amp;lt; 2) {
          return n;
        } else {
          return _fib(n - 1) + _fib(n - 2);
        }
      }
    }
    builder.c_raw_singleton %Q{
      VALUE fib(int argc, VALUE *args, VALUE self) {
        if (argc != 1) {
          rb_raise(rb_eArgError, "1 argument expected");
        }
        VALUE n = args[0];

        return LONG2NUM(_fib(NUM2INT(n)));
      }
    }
  end
end

require "benchmark"

LOOP = 100000
n = 10

Benchmark.bmbm(15) do |make|
  puts "n = #{n}"

  make.report("pure ruby") do
    LOOP.times do
      MylibRuby.fib(n)
    end
  end

  make.report("ruby + inline") do
    LOOP.times do
      MylibInline.fib(n)
    end
  end

  make.report("ruby + FFI") do
    LOOP.times do
      MylibFFI.fib(n)
    end
  end
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;                     user     system      total        real
pure ruby       17.200000   5.690000  22.890000 ( 23.281636)
ruby + inline    0.130000   0.020000   0.150000 (  0.147392)
ruby + FFI       0.150000   0.010000   0.160000 (  0.171384)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jak widać nie powinno być dużej różnicy między klasycznym rozszerzeniem a takim napisanym przy pomocy FFI. Różnica ta jeszcze bardziej będzie się zacierać im więcej czasu upłynie bezpośrednio na poziomie kodu C.&lt;/p&gt;
&lt;h3&gt;Dla kogo FFI?&lt;/h3&gt;
&lt;p&gt;Chyba nie trzeba przekonywać nikogo o korzyściach płynących z wykorzystania tej biblioteki, zamiast jednego z wymienionych na początku wpisu sposobów. Brak dodatkowej kompilacji, krótszy kod (który najczęściej w rozszerzeniach polegał na przekształcaniu typów między C a Ruby), przenośność między różnymi interpreterami Rubiego, niezależność od wewnętrznego API interpretera (ile rozszerzeń w C działały bez zmian w ruby 1.9?) to jego główne atuty. Jeśli kiedykolwiek przyjdzie Ci napisać takie rozszerzenie to w pierwszej kolejności wypróbuj FFI.&lt;/p&gt;
&lt;p&gt;Linki.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://kenai.com/projects/ruby-ffi/"&gt;Strona projektu FFI&lt;/a&gt; (polecam przejrzeć źródła, w szczególności "speki")&lt;/li&gt;
&lt;li&gt;&lt;a href="http://kenai.com/projects/ruby-ffi/pages/Home"&gt;Przykłady na wiki FFI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.igvita.com/2009/01/15/bridging-mri-jruby-rubinius-with-ffi/"&gt;Bridging MRI, JRuby &amp;amp; Rubinius with FFI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.headius.com/2008/10/ffi-for-ruby-now-available.html"&gt;FFI for Ruby Now Available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.segment7.net/articles/2008/01/15/rubinius-foreign-function-interface"&gt;Rubinius' Foreign Function Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://lifegoo.pluskid.org/?p=370"&gt;On the Rubinius FFI&lt;/a&gt; - bardzo obszerny artykuł, polecam!&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/news/2008/12/ruby-ffi-on-mri-jruby"&gt;Ruby FFI Brings Native Library Access to JRuby, MRI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="c" label="C" /><category term="jruby" label="JRuby" /><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ffi" label="ffi" /><category term="extension" label="extension" /><category term="rozszerzenie" label="rozszerzenie" /><category term="inline" label="inline" /><category term="ruby-dl" label="ruby/dl" /></entry><entry><title>Ruby a metody z '?' i '!' w nazwie</title><link href="http://radarek.jogger.pl/2009/02/21/ruby-a-metody-z-i-w-nazwie/" /><id>http://radarek.jogger.pl/2009/02/21/ruby-a-metody-z-i-w-nazwie/</id><updated>2009-02-21T10:24:50Z</updated><content type="html">&lt;p&gt;&lt;img style="float: left;" src="http://farm4.static.flickr.com/3128/2886229996_147ec96702_t.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Jednym z częstych pytań, jakie rodzą się wśród adeptów programowania w Ruby, jest kwestia znaków '?' i '!' w nazwach metod. Pojawiają się także wątpliwości czy Ruby traktuje jakoś specjalnie te znaki, czy stoi za nimi jakaś czarna magia. Nawet wśród bardziej doświadczonych programistów Rubiego zauważam, że nie zawsze poprawnie jest interpretowana idea tych znaków. Tym razem wyjaśnię te i inne wątpliwości związane z tymi dwoma znakami. Zapraszam zatem do lektury (nie tylko wspomnianych adeptów).&lt;/p&gt;
&lt;p&gt;Pierwsza rzecz, na którą chciałem zwrócić uwagę jest fakt, że znaki te nie są w żaden specjalny sposób traktowane przez Ruby. Dla Rubiego to taki sam znam jak 'a', 'C', '0' czy '_'. Różnica jest tylko taka, że znak ten może występować tylko i wyłącznie na końcu nazwy (ale nie może być to jedyny znak w nazwie). Dlatego poprawne są nazwy &lt;code class="inline"&gt;zero?&lt;/code&gt;, &lt;code class="inline"&gt;deleted?&lt;/code&gt;, &lt;code class="inline"&gt;flatten!&lt;/code&gt;, &lt;code class="inline"&gt;map!&lt;/code&gt;, ale już nie &lt;code class="inline"&gt;foo?!&lt;/code&gt;, &lt;code class="inline"&gt;foo?bar&lt;/code&gt;, &lt;code class="inline"&gt;!bar&lt;/code&gt; itp.&lt;/p&gt;
&lt;h3&gt;ruby.awesome?&lt;/h3&gt;
&lt;p&gt;Na pierwszy rzut idzie łatwiejsza kwestia - pytajnik. Niepisana zasada mówi, że powinno się go używać dla metod, które są predykatami, czyli metodami które zwracają wartość true lub false. W innych językach najczęściej używa prefixu "is" w nazwie (np. &lt;code class="inline"&gt;is_active&lt;/code&gt; lub &lt;code class="inline"&gt;isDeleted&lt;/code&gt;). Oto kilka przykładów:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;a = [1, 2, 3]
if a.include?(2)
  puts "Found!"
end

# przykład z rails
if request.post?
  # ...
end

user.awesome = user.active? &amp;amp;&amp;amp; user.paid?
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;ruby.awesome!&lt;/h3&gt;
&lt;p&gt;Jakkolwiek kwestia pytajnika jest powszechnie rozumiana, tak z wykrzyknikiem jest gorzej. Jest to prawdopodobnie spowodowane tym, że przy przekazywaniu zasady, jaką powinno się kierować, zapomina się o bardzo ważnej części, bez której zasada ta ma inne znaczenie.&lt;/p&gt;
&lt;p&gt;Spotykam się bardzo często ze stwierdzeniem, że "wykrzyknika należy używać w nazwach metod, które są potencjalnie niebezpieczne lub destrukcyjnie". To nieprawda! Pełna wersja powinna brzmieć tak: wykrzyknika należy używać w nazwach metod, które są bardziej niebezpieczną wersją metody bez wykrzyknika. Właśnie ta druga część jest kluczowa. Ale co to znaczy "potencjalnie niebezpieczna"? Czy chodzi o możliwość zepsucia komputera? A może wykasowanie ważnych danych, albo wejście na przestępczą ścieżkę?;-) Sęk w tym, że nie da się odpowiedzieć na to pytanie, bo dla jednego niebezpieczne będzie kasowanie plików na dysku, a dla drugiego aktualizacja danych w bazie.&lt;/p&gt;
&lt;p&gt;Dlatego napisałem, że druga część jest kluczowa. Niektóre metody, które coś robią, mogą mieć dodatkowe wersje, które są w pewnym sensie bardziej niebezpieczne. Mając dwie wersje danej metody, możemy łatwiej określić co to znaczy "bardziej niebezpieczny", chociaż wciąż nie dostaniemy jednej odpowiedzi (ale nie o to w tym chodzi).&lt;/p&gt;
&lt;p&gt;Jak przykład weźmy metodę &lt;code class="inline"&gt;Array#sort&lt;/code&gt;. Metoda ta sortuje tablicę i zwraca &lt;strong&gt;nową&lt;/strong&gt;, posortowaną już, kopię. Istnieje wersja tej metody z wykrzyknikiem, tj. &lt;code class="inline"&gt;Array#sort!&lt;/code&gt;, która sortuje tablicę w miejscu, nadpisując oryginalną.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;arr = [2, 3, 1]
arr.sort
p arr

arr = [2, 3, 1]
arr.sort!
p arr
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Metoda &lt;code class="inline"&gt;Array#sort!&lt;/code&gt; sam w sobie nie jest niebezpieczna, ale w pewnym sensie jest bardziej niebezpieczna aniżeli wersja bez wykrzyknika bo, w przeciwieństwie do niej, zmienia obiekt odbiorcy (jest destrukcyjna).&lt;/p&gt;
&lt;p&gt;Jeszcze raz podkreślam jak bardzo ważny jest fakt istnienia wersji bez wykrzyknika. Gdyby tak nie było, to nasze programy naszpikowane byłyby wywołaniami metod z wykrzyknikiem. Ruby posiada bardzo wiele wbudowanych metod, które można by uznać za niebezpieczne (bo np. modyfikują obiekt w miejscu), a które nie mają w nazwie '!'. Przykład? Proszę: &lt;code class="inline"&gt;Array#clear&lt;/code&gt;, &lt;code class="inline"&gt;Array#concat&lt;/code&gt;, &lt;code class="inline"&gt;Array.push&lt;/code&gt;, &lt;code class="inline"&gt;String#insert&lt;/code&gt;. To tylko kilka przykładów metod, które zmieniają bezpośrednio obiekt. Inne metody, które mogłyby zostać uznane za niebezpieczne, to dla przykładu &lt;code class="inline"&gt;FileUtils.rm_rf&lt;/code&gt; czy też nawet &lt;code class="inline"&gt;Kernel.exit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Aktualizacja 21.02.2009, godz. 14:00.&lt;br&gt;
W rozmowie z &lt;a href="http://md6.org/"&gt;okim&lt;/a&gt;, słusznie zasugerował mi, że być może to moja interpretacja konwencji jest błędna. Doszukałem się zatem słów matza, autora Rubiego, na ten temat:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The bang (!) does not mean "destructive" nor lack of it mean non destructive either. The bang sign means "the bang version is more dangerous than its non bang counterpart; handle with care". Since Ruby has a lot of "destructive" methods, if bang signs follow your opinion, every Ruby program would be full of bangs, thus ugly.&lt;/p&gt;
&lt;p style="text-align: right;"&gt;Źródło: &lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325912"&gt;http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325912&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Pozytywnie&lt;/h4&gt;
&lt;p&gt;Przykładem prawidłowego wykorzystania tej techniki jest railsowa metoda &lt;code class="inline"&gt;ActiveRecord#save&lt;/code&gt;, która zwraca wartość true lub false w zależności od tego czy obiekt został zapisy do bazy czy nie. Metoda ta posiada także wersję "bardziej niebezpieczną" &lt;code class="inline"&gt;ActiveRecord#save!&lt;/code&gt;, która robi to samo co poprzednia, ale w przypadku niepowodzenia rzuca wyjątkiem. Uważam, to za świetne podejście, ponieważ programista ma większą elastyczność w sposobie wyboru obsługi błędów.&lt;/p&gt;
&lt;h4&gt;Negatywnie&lt;/h4&gt;
&lt;p&gt;Z kolei metoda &lt;code class="inline"&gt;Rails::Configuration#threadsafe!&lt;/code&gt; jest przykładem błędnej interpretacji tej zasady. Ta metoda nie ma wersji "bezpiecznej". Podejrzewam, że zamiarem autorów było zwrócenie uwagi (w końcu od tego mamy znaki interpunkcyjne) na niebezpieczeństwo jakie się za tym kryje. Mimo to podtrzymuję swoje zdanie. W końcu to nie jedyna opcja, której włączenie musi dobrze przemyśleć programista (dla przykładu &lt;code class="inline"&gt;config.action_mailer.raise_delivery_errors&lt;/code&gt;). W ten sposób do wcześniej wymienionych nazw, destrukcyjnych metod, należałoby dodać '!'.&lt;/p&gt;
&lt;p&gt;Mam nadzieję, że po tej lekturze zarówno '?', a zwłaszcza '!', będą poprawnie stosowane w nazwach metod. A Was proszę o prawidłowe przekazywanie tej zasady z pokolenia na pokolenie. Pomożecie? Pomożecie!&lt;/p&gt;
</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="metoda" label="metoda" /><category term="wykrzyknik" label="wykrzyknik" /><category term="pytajnik" label="pytajnik" /><category term="nazwa" label="nazwa" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #7 - obsługa kodowań znaków</title><link href="http://radarek.jogger.pl/2009/02/12/nowosci-i-zmiany-w-ruby-1-9-obsluga-kodowan-znakow/" /><id>http://radarek.jogger.pl/2009/02/12/nowosci-i-zmiany-w-ruby-1-9-obsluga-kodowan-znakow/</id><updated>2009-02-12T01:39:05Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;Muszę przyznać, że nosząc się z zamiarem napisania tego artykułu miałem blade pojęcie o kodowaniach znaków, choć wcześniej nie zdawałem sobie z tego sprawy. Wydawało mi się, że znając takie pojęcia jak ASCII, Unicode i umiejąc z nich korzystać na co dzień, wiem sporo o kodowaniach. Trudno się jednak dziwić takiemu myśleniu, gdyż większość z Was, zapewne tak jak i ja, korzysta z 1-2 kodowań i póki działa nie musi wiedzieć dlaczego. Wertując ogromną ilość wiadomości na liście mailingowej &lt;a href="http://www.ruby-forum.com/forum/14"&gt;ruby-core&lt;/a&gt; sporo dowiedziałem się, chociaż wciąż nie czuję się ekspertem w tej dziedzinie. Okazało się, że temat ten jest bardzo złożony i dotyczy wielu kwestii. Proszę mi zatem wybaczyć ewentualne niedociągnięcia, niezbyt dokładne opisanie niektórych z nich oraz dosyć chaotyczny styl.&lt;/p&gt;
&lt;h3&gt;Ruby 1.9 haz encodingz!&lt;/h3&gt;
&lt;p&gt;Kolejne zmiany jakie zaszły w wersji 1.9 Rubiego dotyczą obsługi kodowań znaków. Można bez przesady stwierdzić, że to najważniejsza ze zmian. Bądź co bądź mamy XXI wiek i brak natywnej obsługi kodowań jest niewybaczalny, a już na pewno nie przez środowisko "enterprise".&lt;/p&gt;
&lt;p&gt;Jak zapewne wszyscy wiedzą, łańcuchy znaków w Ruby 1.8 to ciągi bajtów i nic więcej. Prowadzi to do oczywistych zachowań, jednak bardzo niewygodnych. Obrazuje to poniższy program:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;s = "ąęć"
puts s.size
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;6
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Powyższy program, odpalony z Ruby 1.8.* wypisze na ekran liczbę "6". Także pozostałe metody klasy &lt;code class="inline"&gt;String&lt;/code&gt; pracują na bajach, zatem używając &lt;code class="inline"&gt;each_char&lt;/code&gt; iterujemy po bajtach, &lt;code class="inline"&gt;reverse&lt;/code&gt; odwracamy kolejność bajtów itd. Nie jest jednak prawdą (jak można wnioskować po wypowiedziach co poniektórych programistów), że bez natywnego wsparcia, nie można obsłużyć utf-8 czy innego kodowania znaków. Oczywiście można, co też pokazał railsowy gem activesupport.&lt;/p&gt;
&lt;p&gt;&lt;img class="single" src="http://farm2.static.flickr.com/1065/543388337_c38d49553e_o.png"&gt;&lt;/p&gt;
&lt;p&gt;Wracając jednak do wersji 1.9, od tej pory wszystkie bolączki typowe dla 1.8 zniknęły (oczywiście pojawiły się nowe, ale o tym za chwilę...). Spróbujmy odpalić ten sam program w 1.9:&lt;/p&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;code/ruby1.8_string.rb:1: invalid multibyte char (US-ASCII)
code/ruby1.8_string.rb:1: invalid multibyte char (US-ASCII)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Ha! Założę się, że chciałeś krzyknąć "przecież to logiczne, że wypisze na ekran liczbę 3", a tu mała niespodzianka. By program zadziałał, musimy podpowiedzieć interpreterowi jakiego kodowania użyliśmy do edycji naszego pliku (inaczej założy, że użyto kodowanie &lt;a href="http://en.wikipedia.org/wiki/ASCII"&gt;ASCII&lt;/a&gt;). Ponieważ użyłem kodowania utf-8, to dopiszę w pierwszej linii pliku &lt;code class="inline"&gt;# encoding: utf-8&lt;/code&gt; (gdyby w pierwszej linii miał tzw. &lt;a href="http://en.wikipedia.org/wiki/Shebang_(Unix)"&gt;shebang line&lt;/a&gt; to umieściłbym ten wpis w linii nr 2). Po dokonanej zmianie odpalam program i dostaję oczekiwaną liczbę 3.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

s = "ąęć"
puts s.size
puts s.bytesize
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;3
6
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Dodatkowo została wypisana wartość zwrócona przez metodę &lt;code class="inline"&gt;String#bytesize&lt;/code&gt;, której znaczenia nie trzeba chyba tłumaczyć nikomu. Panie i Panowie, od dzisiaj myślimy o obiektach klasy String jako o ciągu &lt;strong&gt;znaków&lt;/strong&gt;, a nie bajtów.&lt;/p&gt;
&lt;p&gt;Jak można się domyślić, powyższa zmiana wpływa na inne metody klasy &lt;code class="inline"&gt;String&lt;/code&gt;. Np. odwołanie &lt;code class="inline"&gt;str[5]&lt;/code&gt; dotyczy szóstego &lt;strong&gt;znaku&lt;/strong&gt; a nie bajtu, &lt;code class="inline"&gt;str[3, 2]&lt;/code&gt; to pobranie &lt;strong&gt;dwu znakowego&lt;/strong&gt; podłańcucha począwszy od czwartego &lt;strong&gt;znaku&lt;/strong&gt; i tak dalej...&lt;/p&gt;
&lt;h3&gt;Jeszcze o # encoding: utf-8&lt;/h3&gt;
&lt;p&gt;Jak już pokazałem, nagłówek ten służy by poinformować interpreter Rubiego o użytym kodowaniu. Jak na Rubiego przystało, mamy dosyć sporą dowolność w tej kwestii. Tak na prawdę Ruby szuka ciągu "coding", następnie ":" lub "=" i na końcu podany (aż do białego znaku, bądź nowej linii) ciąg uznaje za nazwę kodowania. Między każdą z części może pojawić się dowolna ilość spacji, wielkość liter nie ma znaczenia. Zatem wszystkie poniższe deklaracje zostaną poprawnie zinterpretowane:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# coding: UTF-8
# encoding: UTF-8
# -*- coding: UTF-8 -*-
# vim:set fileencoding=UTF-8:
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Ma to wielką zaletę ponieważ część z nich jest rozpoznawana przez edytory, np. emacs (trzecia deklaracja), vim (czwarta deklaracja). Jeśli plik nie zawiera takiej deklaracji (lub jest niepoprawna) zostanie użyte domyślne kodowanie ASCII.&lt;/p&gt;
&lt;h3&gt;Klasa Encoding&lt;/h3&gt;
&lt;p&gt;&lt;code class="inline"&gt;Encoding&lt;/code&gt; jest nową klasą, która reprezentuje kodowanie znaków. Każdy łańcuch znaków ma przyporządkowane kodowanie, do sprawdzenia którego należy posłużyć się metodą &lt;code class="inline"&gt;encoding&lt;/code&gt;, zwracającą odpowiednią instancję ten klasy.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: ascii

s = "Hello World!"
puts s.encoding.class
puts s.encoding.name
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;Encoding
US-ASCII
&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

s = "Hello World!"
puts s.encoding.class
puts s.encoding.name
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;Encoding
UTF-8
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Powyższy przykład pokazuje, że kodowanie znaków dla łańcuchów, które pochodzą wprost z kodu źródłowego, jest takie samo jak kodowanie znaków ustawione w nagłówku pliku.&lt;/p&gt;
&lt;h3&gt;Jakie kodowania są obsługiwane?&lt;/h3&gt;
&lt;p&gt;Jeśli chciałbyś sprawdzić, które kodowania Ruby potrafi obsłużyć, to użyj do tego metod &lt;code class="inline"&gt;Encoding#list&lt;/code&gt;, &lt;code class="inline"&gt;Encoding#name_list&lt;/code&gt;, &lt;code class="inline"&gt;Encoding.aliases&lt;/code&gt;. Ta pierwsza zwraca tablicę obiektów kodowań, druga tablicę nazw wszystkich kodowań o których wie Ruby, ostatnia zwraca hash z aliasami kodowań (niektóre kodowania znane są pod różnymi nazwami).&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

p Encoding.list
p Encoding.name_list
p Encoding.aliases
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;[#&amp;lt;Encoding:ASCII-8BIT&amp;gt;, #&amp;lt;Encoding:UTF-8&amp;gt;, #&amp;lt;Encoding:US-ASCII&amp;gt;, #&amp;lt;Encoding:Big5&amp;gt;, #&amp;lt;Encoding:CP949&amp;gt;, #&amp;lt;Encoding:Emacs-Mule&amp;gt;, #&amp;lt;Encoding:EUC-JP&amp;gt;, #&amp;lt;Encoding:EUC-KR&amp;gt;, #&amp;lt;Encoding:EUC-TW&amp;gt;, #&amp;lt;Encoding:GB18030&amp;gt;, #&amp;lt;Encoding:GBK&amp;gt;, #&amp;lt;Encoding:ISO-8859-1&amp;gt;, #&amp;lt;Encoding:ISO-8859-2&amp;gt;, #&amp;lt;Encoding:ISO-8859-3&amp;gt;, #&amp;lt;Encoding:ISO-8859-4&amp;gt;, #&amp;lt;Encoding:ISO-8859-5&amp;gt;, #&amp;lt;Encoding:ISO-8859-6&amp;gt;, #&amp;lt;Encoding:ISO-8859-7&amp;gt;, #&amp;lt;Encoding:ISO-8859-8&amp;gt;, #&amp;lt;Encoding:ISO-8859-9&amp;gt;, #&amp;lt;Encoding:ISO-8859-10&amp;gt;, #&amp;lt;Encoding:ISO-8859-11&amp;gt;, #&amp;lt;Encoding:ISO-8859-13&amp;gt;, #&amp;lt;Encoding:ISO-8859-14&amp;gt;, #&amp;lt;Encoding:ISO-8859-15&amp;gt;, #&amp;lt;Encoding:ISO-8859-16&amp;gt;, #&amp;lt;Encoding:KOI8-R&amp;gt;, #&amp;lt;Encoding:KOI8-U&amp;gt;, #&amp;lt;Encoding:Shift_JIS&amp;gt;, #&amp;lt;Encoding:UTF-16BE&amp;gt;, #&amp;lt;Encoding:UTF-16LE&amp;gt;, #&amp;lt;Encoding:UTF-32BE&amp;gt;, #&amp;lt;Encoding:UTF-32LE&amp;gt;, #&amp;lt;Encoding:Windows-1251&amp;gt;, #&amp;lt;Encoding:IBM437&amp;gt;, #&amp;lt;Encoding:IBM737&amp;gt;, #&amp;lt;Encoding:IBM775&amp;gt;, #&amp;lt;Encoding:CP850&amp;gt;, #&amp;lt;Encoding:IBM852&amp;gt;, #&amp;lt;Encoding:CP852&amp;gt;, #&amp;lt;Encoding:IBM855&amp;gt;, #&amp;lt;Encoding:CP855&amp;gt;, #&amp;lt;Encoding:IBM857&amp;gt;, #&amp;lt;Encoding:IBM860&amp;gt;, #&amp;lt;Encoding:IBM861&amp;gt;, #&amp;lt;Encoding:IBM862&amp;gt;, #&amp;lt;Encoding:IBM863&amp;gt;, #&amp;lt;Encoding:IBM864&amp;gt;, #&amp;lt;Encoding:IBM865&amp;gt;, #&amp;lt;Encoding:IBM866&amp;gt;, #&amp;lt;Encoding:IBM869&amp;gt;, #&amp;lt;Encoding:Windows-1258&amp;gt;, #&amp;lt;Encoding:GB1988&amp;gt;, #&amp;lt;Encoding:macCentEuro&amp;gt;, #&amp;lt;Encoding:macCroatian&amp;gt;, #&amp;lt;Encoding:macCyrillic&amp;gt;, #&amp;lt;Encoding:macGreek&amp;gt;, #&amp;lt;Encoding:macIceland&amp;gt;, #&amp;lt;Encoding:macRoman&amp;gt;, #&amp;lt;Encoding:macRomania&amp;gt;, #&amp;lt;Encoding:macThai&amp;gt;, #&amp;lt;Encoding:macTurkish&amp;gt;, #&amp;lt;Encoding:macUkraine&amp;gt;, #&amp;lt;Encoding:stateless-ISO-2022-JP&amp;gt;, #&amp;lt;Encoding:eucJP-ms&amp;gt;, #&amp;lt;Encoding:CP51932&amp;gt;, #&amp;lt;Encoding:GB2312&amp;gt;, #&amp;lt;Encoding:GB12345&amp;gt;, #&amp;lt;Encoding:ISO-2022-JP (dummy)&amp;gt;, #&amp;lt;Encoding:ISO-2022-JP-2 (dummy)&amp;gt;, #&amp;lt;Encoding:Windows-1252&amp;gt;, #&amp;lt;Encoding:Windows-1250&amp;gt;, #&amp;lt;Encoding:Windows-1256&amp;gt;, #&amp;lt;Encoding:Windows-1253&amp;gt;, #&amp;lt;Encoding:Windows-1255&amp;gt;, #&amp;lt;Encoding:Windows-1254&amp;gt;, #&amp;lt;Encoding:TIS-620&amp;gt;, #&amp;lt;Encoding:Windows-874&amp;gt;, #&amp;lt;Encoding:Windows-1257&amp;gt;, #&amp;lt;Encoding:Windows-31J&amp;gt;, #&amp;lt;Encoding:MacJapanese&amp;gt;, #&amp;lt;Encoding:UTF-7 (dummy)&amp;gt;, #&amp;lt;Encoding:UTF8-MAC&amp;gt;]
["ASCII-8BIT", "UTF-8", "US-ASCII", "Big5", "CP949", "Emacs-Mule", "EUC-JP", "EUC-KR", "EUC-TW", "GB18030", "GBK", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-8859-10", "ISO-8859-11", "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "ISO-8859-16", "KOI8-R", "KOI8-U", "Shift_JIS", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "Windows-1251", "BINARY", "IBM437", "CP437", "IBM737", "CP737", "IBM775", "CP775", "CP850", "IBM850", "IBM852", "CP852", "IBM855", "CP855", "IBM857", "CP857", "IBM860", "CP860", "IBM861", "CP861", "IBM862", "CP862", "IBM863", "CP863", "IBM864", "CP864", "IBM865", "CP865", "IBM866", "CP866", "IBM869", "CP869", "Windows-1258", "CP1258", "GB1988", "macCentEuro", "macCroatian", "macCyrillic", "macGreek", "macIceland", "macRoman", "macRomania", "macThai", "macTurkish", "macUkraine", "CP950", "stateless-ISO-2022-JP", "eucJP", "eucJP-ms", "euc-jp-ms", "CP51932", "eucKR", "eucTW", "GB2312", "EUC-CN", "eucCN", "GB12345", "CP936", "ISO-2022-JP", "ISO2022-JP", "ISO-2022-JP-2", "ISO2022-JP2", "ISO8859-1", "Windows-1252", "CP1252", "ISO8859-2", "Windows-1250", "CP1250", "ISO8859-3", "ISO8859-4", "ISO8859-5", "ISO8859-6", "Windows-1256", "CP1256", "ISO8859-7", "Windows-1253", "CP1253", "ISO8859-8", "Windows-1255", "CP1255", "ISO8859-9", "Windows-1254", "CP1254", "ISO8859-10", "ISO8859-11", "TIS-620", "Windows-874", "CP874", "ISO8859-13", "Windows-1257", "CP1257", "ISO8859-14", "ISO8859-15", "ISO8859-16", "CP878", "SJIS", "Windows-31J", "CP932", "csWindows31J", "MacJapanese", "MacJapan", "ASCII", "ANSI_X3.4-1968", "646", "UTF-7", "CP65000", "CP65001", "UTF8-MAC", "UTF-8-MAC", "UCS-2BE", "UCS-4BE", "UCS-4LE", "CP1251", "locale", "external", "internal"]
{"BINARY"=&amp;gt;"ASCII-8BIT", "CP437"=&amp;gt;"IBM437", "CP737"=&amp;gt;"IBM737", "CP775"=&amp;gt;"IBM775", "IBM850"=&amp;gt;"CP850", "CP857"=&amp;gt;"IBM857", "CP860"=&amp;gt;"IBM860", "CP861"=&amp;gt;"IBM861", "CP862"=&amp;gt;"IBM862", "CP863"=&amp;gt;"IBM863", "CP864"=&amp;gt;"IBM864", "CP865"=&amp;gt;"IBM865", "CP866"=&amp;gt;"IBM866", "CP869"=&amp;gt;"IBM869", "CP1258"=&amp;gt;"Windows-1258", "CP950"=&amp;gt;"Big5", "eucJP"=&amp;gt;"EUC-JP", "euc-jp-ms"=&amp;gt;"eucJP-ms", "eucKR"=&amp;gt;"EUC-KR", "eucTW"=&amp;gt;"EUC-TW", "EUC-CN"=&amp;gt;"GB2312", "eucCN"=&amp;gt;"GB2312", "CP936"=&amp;gt;"GBK", "ISO2022-JP"=&amp;gt;"ISO-2022-JP", "ISO2022-JP2"=&amp;gt;"ISO-2022-JP-2", "ISO8859-1"=&amp;gt;"ISO-8859-1", "CP1252"=&amp;gt;"Windows-1252", "ISO8859-2"=&amp;gt;"ISO-8859-2", "CP1250"=&amp;gt;"Windows-1250", "ISO8859-3"=&amp;gt;"ISO-8859-3", "ISO8859-4"=&amp;gt;"ISO-8859-4", "ISO8859-5"=&amp;gt;"ISO-8859-5", "ISO8859-6"=&amp;gt;"ISO-8859-6", "CP1256"=&amp;gt;"Windows-1256", "ISO8859-7"=&amp;gt;"ISO-8859-7", "CP1253"=&amp;gt;"Windows-1253", "ISO8859-8"=&amp;gt;"ISO-8859-8", "CP1255"=&amp;gt;"Windows-1255", "ISO8859-9"=&amp;gt;"ISO-8859-9", "CP1254"=&amp;gt;"Windows-1254", "ISO8859-10"=&amp;gt;"ISO-8859-10", "ISO8859-11"=&amp;gt;"ISO-8859-11", "CP874"=&amp;gt;"Windows-874", "ISO8859-13"=&amp;gt;"ISO-8859-13", "CP1257"=&amp;gt;"Windows-1257", "ISO8859-14"=&amp;gt;"ISO-8859-14", "ISO8859-15"=&amp;gt;"ISO-8859-15", "ISO8859-16"=&amp;gt;"ISO-8859-16", "CP878"=&amp;gt;"KOI8-R", "SJIS"=&amp;gt;"Shift_JIS", "CP932"=&amp;gt;"Windows-31J", "csWindows31J"=&amp;gt;"Windows-31J", "MacJapan"=&amp;gt;"MacJapanese", "ASCII"=&amp;gt;"US-ASCII", "ANSI_X3.4-1968"=&amp;gt;"US-ASCII", "646"=&amp;gt;"US-ASCII", "CP65000"=&amp;gt;"UTF-7", "CP65001"=&amp;gt;"UTF-8", "UTF-8-MAC"=&amp;gt;"UTF8-MAC", "UCS-2BE"=&amp;gt;"UTF-16BE", "UCS-4BE"=&amp;gt;"UTF-32BE", "UCS-4LE"=&amp;gt;"UTF-32LE", "CP1251"=&amp;gt;"Windows-1251", "locale"=&amp;gt;"UTF-8", "external"=&amp;gt;"UTF-8"}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Do każdego kodowania możemy dostać się także poprzez odpowiednią stałą &lt;code class="inline"&gt;Encoding::&amp;lt;NAZWA_KODOWANIA&amp;gt;&lt;/code&gt;, np: &lt;code class="inline"&gt;Encoding::UTF_8&lt;/code&gt;, &lt;code class="inline"&gt;Encoding&lt;/code&gt;. Jeśli chcemy pobrać obiekt kodowania na podstawie łańcucha znaków (bo np. pobraliśmy go z pliku) to możemy użyć metody &lt;code class="inline"&gt;Encoding.find&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: ascii

s = "hello"
puts s.encoding == Encoding::ASCII
puts s.encoding == Encoding::US_ASCII

%w(utf-8 ascii foo).each do |encoding_name|
  begin
    encoding = Encoding.find(encoding_name)
    puts encoding.name
  rescue ArgumentError =&amp;gt; e
    puts e.message
  end
end

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;true
true
UTF-8
US-ASCII
unknown encoding name - foo
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Tak jak wspomniałem, każde kodowanie może mieć jakieś aliasy. Prócz wspomnianej metody &lt;code class="inline"&gt;Encoding.aliases&lt;/code&gt; zwracającej hash, można je pobrać także metodą &lt;code class="inline"&gt;Encoding#names&lt;/code&gt; dla konkretnego obiektu kodowania.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

puts Encoding::UTF_8.names
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;UTF-8
CP65001
locale
external
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Nowa pseudo-stała __ENCODING__&lt;/h3&gt;
&lt;p&gt;Dodano nową pseudo-stałą o nazwie &lt;code class="inline"&gt;__ENCODING__&lt;/code&gt;, która zwraca obiekt kodowania jakie zostało użyte w pliku.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

puts "Ten plik został zakodowany przy użyciu kodowania: #{__ENCODING__.name}"
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;Ten plik został zakodowany przy użyciu kodowania: UTF-8
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;img class="single" src="http://farm1.static.flickr.com/27/40467714_d2dfbdeb24.jpg"&gt;&lt;/p&gt;
&lt;h3&gt;String raz jeszcze&lt;/h3&gt;
&lt;p&gt;Póki co pokazałem, że każdy łańcuch ma skojarzony z nim obiekt kodowania. Obiekt ten jest tylko etykietą łańcucha i nie wpływa bezpośrednio na jego wewnętrzną zawartość (czyli ciąg bajtów). Zwracam na to uwagę, ponieważ Ruby nie używa wewnętrznie żadnego konkretnego kodowania (dokładniej opiszę to kilka akapitów dalej). Ma to swoje wady i zalety. Wadą jest to, że trzeba mieć większą świadomość pisząc programy, które operują na stringach. Trzeba zdawać sobie sprawę, że w jednej chwili mogą istnieć stringi o różnych kodowaniach (różne środowiska, ustawienia lokalne, kodowania plików na dysku, kodowania plików źródłowych itp.). Z kolei zaletą jest ogromna elastyczność, która w efekcie może dać większe możliwości w tej kwestii niż zastosowane rozwiązania w Javie czy Pythonie.&lt;/p&gt;
&lt;h3&gt;String#force_encoding, String#bytes, String#valid_encoding?&lt;/h3&gt;
&lt;p&gt;Jak już wspomniałem, klasa String od wersji 1.9 reprezentuje ciąg znaków. Jednak wewnętrznie jest to wciąż ciąg bajtów, ustawione kodowanie odpowiada za sposób interpretacji tych bajtów. Ruby w żaden sposób nie modyfikuje (chyba że go o to poprosisz) wartości tych bajtów. Do wymuszenia zmiany tej interpretacji służy metoda &lt;code class="inline"&gt;String#force_encoding&lt;/code&gt;, która ustawia nowe kodowanie, ale mimo wszystko nie dokonuje żadnej konwersji bajtów (jeśli interesuje Cię zawartość na poziomie bajtów to użyj metody &lt;code class="inline"&gt;String#bytes&lt;/code&gt;, która zwraca odpowiedni iterator). Jest to przydatne na przykład w sytuacji gdy musimy wczytać jakieś dane, ale nie znamy kodowania, które zostało użyte. Jeśli po odczytaniu i sprawdzeniu kilku bajtów możemy określić kodowanie, to możemy o tym poinformować właśnie poprzez tą metodę. Praktycznym przykładem może być odczyt pliku html z dysku, w którym informacja o kodowaniu jest zawarta w nagłówku (&lt;code class="inline"&gt;&amp;lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&amp;gt;&lt;/code&gt;). Jeśli chcemy się upewnić, że wewnętrzna sekwencja bajtów jest poprawna z ustawionym kodowaniem, to możemy użyć do tego metody &lt;code class="inline"&gt;String#valid_encoding?&lt;/code&gt;. Pora na mały przykład z użyciem wymienionych metod.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

# String#force_encoding
# String#valid_encoding?
str_utf   = "foo" # kodowanie odziedziczone po źródle
str_ascii = str_utf.dup.force_encoding("ascii")
puts "#{str_utf}.valid_encoding? == %s" % str_utf.valid_encoding?
puts "#{str_ascii}.valid_encoding? == %s" % str_ascii.valid_encoding? # true, występowały same znaki ascii

str_utf = "Föö"
str_ascii = str_utf.dup.force_encoding("ascii")
puts "#{str_ascii}.valid_encoding? == %s" % str_ascii.valid_encoding? # false, znaki spoza zakresu ascii

str_utf = "Föö"
str_ascii = str_utf.dup.force_encoding("binary") # to samo co ascii-8bit
puts "#{str_ascii}.valid_encoding? == %s" % str_ascii.valid_encoding? # true, dowolny ciąg bajtów jest dozwolony

# String#bytes
str = "Föö"
puts "str(%s): #{str}" % str.encoding
puts " bajty(%d): %p" % [str.bytesize, str.bytes.to_a]
puts " znaki(%d): %p" % [str.size, str.chars.to_a]

str_ascii = str.force_encoding("ascii")
puts "str(%s): #{str}" % str.encoding
puts " bajty(%d): %p" % [str.bytesize, str.bytes.to_a]
puts " znaki(%d): %p" % [str.size, str.chars.to_a]
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;foo.valid_encoding? == true
foo.valid_encoding? == true
Föö.valid_encoding? == false
Föö.valid_encoding? == true
str(UTF-8): Föö
 bajty(5): [70, 195, 182, 195, 182]
 znaki(3): ["F", "ö", "ö"]
str(US-ASCII): Föö
 bajty(5): [70, 195, 182, 195, 182]
 znaki(5): ["F", "\xC3", "\xB6", "\xC3", "\xB6"]
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;String#encode&lt;/h3&gt;
&lt;p&gt;Inną bardzo przydatną metodą jest &lt;code class="inline"&gt;String#encode&lt;/code&gt;, która służy do przekonwertowania stringa w jednym kodowaniu do stringa w drugim. Jest to jedno z tych (raczej oczywistych) miejsc, w którym konwersja następuje na poziomie bajtów (inaczej ciężko mówić o konwersji). Oto przykład, w którym wykorzystamy tą metodę do konwersji znaków z utf-8 do iso-8859-2 i cp1250.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

s1 = "ąęć"
s2 = s1.encode("iso-8859-2")
s3 = s1.encode("cp1250")

puts "#{s1}"
puts " (%s)bajty: %p" % [s1.encoding, s1.bytes.to_a]
puts " (%s)bajty: %p" % [s2.encoding, s2.bytes.to_a]
puts " (%s)bajty: %p" % [s3.encoding, s3.bytes.to_a]
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;ąęć
 (UTF-8)bajty: [196, 133, 196, 153, 196, 135]
 (ISO-8859-2)bajty: [177, 234, 230]
 (Windows-1250)bajty: [185, 234, 230]
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Celowo wypisałem tylko bajty ponieważ co najwyżej jeden z wypisywanych łańcuchów wyświetliłby się poprawnie.&lt;/p&gt;
&lt;p&gt;Warto zwrócić na uwagę, że w przypadku gdy odpowiednia konwersja nie może zostać wykonana, jest rzucany wyjątek &lt;code class="inline"&gt;Encoding::UndefinedConversionError&lt;/code&gt;. Przykładowo&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

nuclear = "☢"
nuclear.encode("ascii") # powoduje Encoding::UndefinedConversionError
                        # ponieważ znak nie może zostać skonwertowany
                        # (brak odpowiednika)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;code/encode02.rb:4:in `encode': "\xE2\x98\xA2" from UTF-8 to US-ASCII (Encoding::UndefinedConversionError)
        from code/encode02.rb:4:in `&amp;lt;main&amp;gt;'
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;String#ascii_only?&lt;/h3&gt;
&lt;p&gt;Metoda, jak łatwo się domyślić, sprawdza czy dany łańcuch zawiera tylko znaki ASCII (zakres bajtów \x00-\x79, czyli 0-127).&lt;/p&gt;
&lt;p&gt;&lt;img class="single" src="http://farm4.static.flickr.com/3030/2722698216_59482bd904.jpg"&gt;&lt;/p&gt;
&lt;h3&gt;Literały&lt;/h3&gt;
&lt;p&gt;Jak już widziałeś wcześniej, literały stringów dziedziczą kodowanie znaków po kodowaniu ustawionym w nagłówku pliku, w którym się znajdują. Jest to połowiczna prawda w przypadku literałów wyrażeń regularnych (&lt;code class="inline"&gt;Regexp&lt;/code&gt;) oraz symboli (&lt;code class="inline"&gt;Symbol&lt;/code&gt;), w wypadku których zostanie użyte kodowanie ASCII jeśli wszystkie znaki są właśnie z tego zakresu. Głównie chodzi o ułatwienie pracy z tymi typami (w przypadku symboli mogłoby dojść do sytuacji, w której dany symbol miałbym kodowanie zależne od tego jakie kodowanie miałby plik, w którym wystąpił wystąpił po raz pierwszy).&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

def show_encoding(str)
  puts "'#{str}' is #{str.encoding.name}"
end

show_encoding "cat"
show_encoding "∂og"

show_encoding :cat
show_encoding :∂og

show_encoding /cat/
show_encoding /∂og/
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;'cat' is UTF-8
'∂og' is UTF-8
'cat' is US-ASCII
'∂og' is UTF-8
'(?-mix:cat)' is US-ASCII
'(?-mix:∂og)' is UTF-8
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Z kolei jeśli plik zawiera kodowanie nie-unicodowe, ale dany łańcuch zawiera sekwencję "\uDDDD" to jego kodowanie ustawiane jest na UTF-8.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: ascii

def show_str(s)
  puts "#{s}, encoding: #{s.encoding}"
end

s1 = "foo"
s2 = "\u2622"

show_str(s1)
show_str(s2)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;foo, encoding: US-ASCII
☢, encoding: UTF-8
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Wydajność&lt;/h3&gt;
&lt;p&gt;Kolejnym aspektem, na który trzeba zwrócić uwagę, jest wydajność. Nie jestem w stanie przetestować wszystkich operacji związanych z łańcuchami znaków, a na których wydajność ma wpływ kodowanie znaków. Mogę natomiast podpowiedzieć, że należy zwracać uwagę na operacje, które wymagają dostępu do znaków o konkretnym indeksie. Są to m.in. takie metody jak &lt;code class="inline"&gt;String#[]&lt;/code&gt;, &lt;code class="inline"&gt;String#index&lt;/code&gt;, &lt;code class="inline"&gt;String#insert&lt;/code&gt;, &lt;code class="inline"&gt;String#slice&lt;/code&gt;. Dla kodowań o stałej szerokości znaków (np. &lt;a href="http://pl.wikipedia.org/wiki/UTF-32/UCS-4"&gt;UTF-32&lt;/a&gt;) oraz łańcuchów znaków, które zawierają tylko 7 bitowe znaki ASCII (czyli takie, dla których &lt;code class="inline"&gt;String#ascii_only?&lt;/code&gt; zwraca &lt;code class="inline"&gt;true&lt;/code&gt;) złożoność dostępu wynosi O(1), dla pozostałych wynosi O(n). Jeśli nie jest się pewnym czy dostęp jest O(1) czy O(n) wystarczy sprawdzić to prostym benchmarkiem.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8
require "benchmark"

SIZE = 100_000
INDEX = SIZE / 2
N = 1000

s1 = "foo" * SIZE
s2 = "fóó" * SIZE

Benchmark.bm(10) do |make|
  make.report(s1.encoding.to_s) do
    N.times do
      char = s1[INDEX]
    end
  end

  make.report(s2.encoding.to_s) do
    N.times do
      char = s2[INDEX]
    end
  end
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;                user     system      total        real
UTF-8       0.000000   0.000000   0.000000 (  0.000939)
UTF-8       0.340000   0.000000   0.340000 (  0.342168)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Dla łańcucha "foo" zawierającego tylko znaki ASCII, pomimo iż był zakodowany przy użyciu kodowania UTF-8 (a które ma zmienną długość znaków), dostęp po indeksie okazał się bardzo szybki. Z kolei dla łańcucha "fóó" dostęp nie mógł być zoptymalizowany i trwał o wiele dłużej, ponieważ łańcuch zawierał znaki spoza zbioru ASCII.&lt;/p&gt;
&lt;p&gt;Moje słowa o optymalizacji dostępu dla kodowań o stałej szerokości znaków może potwierdzić następujący program:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

require "benchmark"

SIZE = 100_000
INDEX = SIZE / 2
N = 1000

s1 = "foo".encode("utf-32be") * SIZE
s2 = "fóó".encode("utf-32be") * SIZE

Benchmark.bm(10) do |make|
  make.report(s1.encoding.to_s) do
    N.times do
      char = s1[INDEX]
    end
  end

  make.report(s2.encoding.to_s) do
    N.times do
      char = s2[INDEX]
    end
  end
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;                user     system      total        real
UTF-32BE    0.000000   0.000000   0.000000 (  0.000301)
UTF-32BE    0.000000   0.000000   0.000000 (  0.000301)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Warto zwrócić także uwagę, że jeśli iterujemy po wszystkich znakach w łańcuchu to lepiej jest skorzystać z iteratora &lt;code class="inline"&gt;String#each_char&lt;/code&gt;, zamiast samemu zwiększać licznik i odwoływać się przez &lt;code class="inline"&gt;String#[]&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

require "benchmark"

SIZE = 5_000
INDEX = SIZE / 2
N = 1000

s = "fóó" * SIZE

Benchmark.bm(20) do |make|
  make.report("String#each_char") do
    s.each_char do |c|
      a = c
    end
  end

  make.report("String#[i]") do
    s.size.times do |i|
      a = s[i]
    end
  end
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;                          user     system      total        real
String#each_char      0.000000   0.010000   0.010000 (  0.004285)
String#[i]            0.340000   0.000000   0.340000 (  0.336463)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Operacje, które nie wymagają dostępu do znaków o konkretnych indeksach (np. konkatenacja łańcuchów) powinny być tak samo wydajne jak do tej pory. Chociaż, tak jak już wspomniałem, dobrze jest upewnić się robiąc odpowiednie testy wydajnościowe.&lt;/p&gt;
&lt;h3&gt;Encoding.default_external i Encoding.default_internal&lt;/h3&gt;
&lt;p&gt;Wspomniałem na początku, że Ruby w przeciwieństwie do Pythona, Javy czy innych języków nie stosuje żadnej reprezentacji wewnętrznej łańcuchów. Wynika to z faktu nieistnienia jednego, uniwersalnego kodowania znaków, które pozwalałoby na reprezentację wszystkich znaków na świecie. Nie jest nim nawet Unicode, chociaż takie są jego założenia. Z jednej strony daje to większą elastyczność, z drugiej wymaga więcej pracy od programisty. Jeśli pracujemy tylko z jednym kodowaniem to problemu nie ma. Wczytując np. dane z pliku określamy, że jego kodowanie to utf-8 i takie właśnie kodowanie otrzymują wczytywane z niego dane (chociaż już tu możemy zapytać: dlaczego muszę za każdym razem określać to kodowanie?).&lt;/p&gt;
&lt;p&gt;Gorzej jednak, jeśli zmuszeni jesteśmy pracować na danych o różnych kodowaniach. Wyobraź sobie taką sytuację. Piszesz program, który wczytuje dane z plików o kodowaniach iso-8859-2, cp1250 i utf-8. Dane te następnie zapisywane są w bazie w kodowaniu utf-8. Sprawa wydaje się w miarę prosta. Wczytując dane z plików o 2 pierwszych kodowaniach konwertujesz dane do utf-8 i takie zapisujesz do bazy. Jeśli w przyszłości będziesz chciał zmienić kodowanie bazy danych na inne to będziesz musiał także zmienić kodowanie do którego konwertujesz dane z wczytywanych plików. Mógłbyś to oczywiście załatwić jakąś stałą, ale mimo wszystko może to wprowadzać niepotrzebne zamieszanie.&lt;/p&gt;
&lt;p&gt;By ułatwić nam pracę, zostały wprowadzone dwa pojęcia, tj. kodowanie wewnętrzne (internal encoding) i kodowanie zewnętrzne (external encoding). To pierwsze określa domyślne kodowanie do którego są konwertowane wszystkie wczytywane z zewnątrz dane w procesie programu. To drugie określa domyślne kodowanie używane podczas wczytywanie zewnętrznych danych. Dla przykładu jeśli ustawimy kodowanie zewnętrzne na iso-8859-2, zaś kodowanie wewnętrzne na utf-8 to przy operacji &lt;code class="inline"&gt;File.open(filename).read&lt;/code&gt; Ruby domniema, że kodowaniem tego pliku jest utf-8, ale wczytane dane przekonwertuje do iso-8859-2.&lt;/p&gt;
&lt;p&gt;Do ustawiania i pobierania tych kodowań służą metody &lt;code class="inline"&gt;Encoding.default_external&lt;/code&gt;, &lt;code class="inline"&gt;Encoding.default_external=&lt;/code&gt;, &lt;code class="inline"&gt;Encoding.default_internal&lt;/code&gt;, &lt;code class="inline"&gt;Encoding.default_internal=&lt;/code&gt;. Domyślnie kodowanie wewnętrzne nie jest ustawione, zatem dane nie są konwertowane w żaden sposób. Z kolei domyślne kodowanie zewnętrzne jest ustawiane na takie jakie mamy ustawione w ustawieniach lokalnych naszego systemu. Pora na mały przykład.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

def show(encoding)
  puts "Zmieniam Encoding.default_internal na #{encoding}"
  Encoding.default_internal = encoding

  data = File.read("/etc/passwd")
  puts "data.encoding: #{data.encoding}"
end

puts "Domyślnym kodowaniem zewnętrznym jest: #{Encoding.default_external}"
puts "Domyślnym kodowaniem wewnętrznym jest: #{Encoding.default_internal}"

show(nil)
show("utf-8")
show("iso-8859-2")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;Domyślnym kodowaniem zewnętrznym jest: UTF-8
Domyślnym kodowaniem wewnętrznym jest: 
Zmieniam Encoding.default_internal na 
data.encoding: UTF-8
Zmieniam Encoding.default_internal na utf-8
data.encoding: UTF-8
Zmieniam Encoding.default_internal na iso-8859-2
data.encoding: ISO-8859-2
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jeśli chcemy dla konkretnego pliku określić inne niż domyślne kodowanie wewnętrzne i zewnętrzne to powinniśmy podać odpowiednie parametry do metody &lt;code class="inline"&gt;File.open&lt;/code&gt;. Nie powinniśmy natomiast zmieniać raz ustawionych (np. przy starcie programu) wartości &lt;code class="inline"&gt;Encoding.default_internal&lt;/code&gt; i &lt;code class="inline"&gt;Encoding.default_external&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# encoding: utf-8

Encoding.default_internal = "utf-8"
Encoding.default_external = "utf-8"
file = File.open("/etc/passwd")
puts "file.external_encoding: #{file.external_encoding}"
puts "file.internal_encoding: #{file.internal_encoding}"
puts "file.read.encoding: #{file.read.encoding}"

file = File.open("/etc/passwd", external_encoding: "ascii", internal_encoding: "iso-8859-2")
puts "file.external_encoding: #{file.external_encoding}"
puts "file.internal_encoding: #{file.internal_encoding}"
puts "file.read.encoding: #{file.read.encoding}"
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="shell"&gt;file.external_encoding: UTF-8
file.internal_encoding: 
file.read.encoding: UTF-8
file.external_encoding: US-ASCII
file.internal_encoding: ISO-8859-2
file.read.encoding: ISO-8859-2
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Uff, to z pewnością jeden z najdłuższych wpisów jakie kiedykolwiek umiesciłem na tym blogu. Z pewnością nie opisałem każdego z możliwych aspektów dotyczących kodowań. Istnieje wiele różnych pułapek, na które trzeba uważać. Liczę na to, że po powyższej lekturze, na spokojnie usiądziecie sam na sam z interpreterem Rubiego w wersji 1.9.1 i spróbujecie pobawić się tym co Wam pokazałem. Gdybyście mięli jakieś inne wątpliwości, ciekawe pytania to piszcie w komentarzach.&lt;/p&gt;
&lt;p&gt;Linki&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://blog.nuclearsquid.com/writings/ruby-1-9-encodings"&gt;Working with Encodings in Ruby 1.9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.grayproductions.net/articles/understanding_m17n"&gt;Understanding M17n&lt;/a&gt; - cała seria wpisów Jamesa Gray'a na temat M17N w Ruby&lt;/li&gt;
&lt;/ul&gt;
</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /><category term="encoding" label="encoding" /><category term="unicode" label="unicode" /><category term="utf" label="utf" /><category term="utf-8" label="utf-8" /><category term="utf-16" label="utf-16" /><category term="utf-32" label="utf-32" /><category term="iso-8859-2" label="iso-8859-2" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #6 - iteratory (klasa Enumerator)</title><link href="http://radarek.jogger.pl/2008/12/14/nowosci-i-zmiany-w-ruby-1-9-iteratory-enumerator/" /><id>http://radarek.jogger.pl/2008/12/14/nowosci-i-zmiany-w-ruby-1-9-iteratory-enumerator/</id><updated>2008-12-14T02:10:08Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Zacznę od (jak mi się wydaje) najważniejszej zmiany. Chodzi mianowicie o zewnętrzne iteratory, których do tej pory jakby nie było. Napisałem jakby, ponieważ mogliśmy skorzystać z klasy &lt;code class="inline"&gt;&lt;a href="http://www.ruby-doc.org/core/classes/Generator.html"&gt;Generator&lt;/a&gt;&lt;/code&gt;, która potrafiła przekonwertować wewnętrzny iterator na zewnętrzny. Niestety było to niewygodne, a przy okazji dość niewydajne (implementacja tej klasy jest oparta na kontynuacjach - sic!). Zewnętrzne iteratory przydają się podczas iteracji kilku kolekcji na raz, zwłaszcza jeśli nie posiadają dostępu indeksowego.&lt;/p&gt;
&lt;p&gt;Oto przykład iteracji po dwóch kolekcjach (celowo jedna z nich to hash, który nie ma dostępu po indeksie) z wykorzystaniem zewnętrznego iteratora.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;numbers = [10, 100, 123]
pairs = { "one" =&amp;gt; "foo", "two" =&amp;gt; "bar", "three" =&amp;gt; "baz" }

e1 = numbers.each
e2 = pairs.each

loop do
  p [e1.next, e2.next]
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jeśli zastanawiasz się dlaczego program nie wpada w nieskończoną pętlę to odpowiadam, że metoda &lt;code class="inline"&gt;Enumerable#next&lt;/code&gt; rzuca wyjątek &lt;code class="inline"&gt;StopIteration&lt;/code&gt;, który jest po cichu przechwytywany przez pętlę &lt;code class="inline"&gt;loop&lt;/code&gt; co powoduje jej zakończenie.&lt;/p&gt;
&lt;p&gt;Zwróć uwagę, że wywołanie &lt;code class="inline"&gt;each&lt;/code&gt; bez podania bloku powoduje zwrócenie obiektu zewnętrznego iteratora (klasa &lt;code class="inline"&gt;Enumerator&lt;/code&gt;) i to on potrafi wyciągać kolejne elementy kolekcji (metoda &lt;code class="inline"&gt;next&lt;/code&gt;). Obiekt ten jest instancją klasy &lt;code class="inline"&gt;Enumerator&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;enum_for wbudowane w język&lt;/h3&gt;
&lt;p&gt;Muszę przyznać, że dopiero niedawno przyjrzałem się metodzie &lt;code class="inline"&gt;Object#enum_for&lt;/code&gt; (również dostępna pod aliasem &lt;code class="inline"&gt;Object#to_enum&lt;/code&gt;). Jest to bardzo ciekawa metoda, która pozwala na utworzenie nowego iteratora na podstawie istniejącego. Pozwala to nam na wskazanie nazwy metody, która będzie dostępna pod standardową metodą iteracji &lt;code class="inline"&gt;each&lt;/code&gt;. Daje nam to nowe możliwości, ponieważ cała gama metod z modułu &lt;code class="inline"&gt;Enumerable&lt;/code&gt; będzie pracować z nową definicją tej metody. Być może sam opis nie wyjaśnia problemu, myślę, że prosty przykład pomoże Ci zrozumieć jak to działa.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;a = [1, 2, 3, 4]
# wypisuje wszystkie 3 elementowe kombinacje
puts "Kombinacje #{a}"
a.combination(3).each do |c|
 p c
end

e = a.enum_for(:combination, 3)
# znajdujemy wszystkie 3 elementowe kombinacje, których suma jest parzysta
p e.find_all {|c| c.inject(:+) % 2 == 0 }
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;W wersji 1.8 Rubiego by mieć dostęp do enum_for należy dołączyć bibliotekę "enumerator". Od wersji 1.9 jest ona częścią core, więc jest dołączana automatycznie.&lt;/p&gt;
&lt;p&gt;Zauważ, że zwrócony obiekt jest także instancją klasy &lt;code class="inline"&gt;Enumerator&lt;/code&gt; (w ruby 1.8 &lt;code class="inline"&gt;enum_for&lt;/code&gt; zwracał instancję klasy &lt;code class="inline"&gt;Enumerable::Enumerator&lt;/code&gt;). Zatem (mam nadzieję że jeszcze pamiętać) możemy go potraktować jako zewnętrzny iterator.&lt;/p&gt;
&lt;p&gt;Z iteratorami wiąże się jeszcze jedna bardzo ciekawa zmiana. Otóż większość metod modułu &lt;code class="inline"&gt;Enumerable&lt;/code&gt; zwraca iterator jeśli nie zostanie podany blok kodu, podobnie jak to było w przypadku metody &lt;code class="inline"&gt;each&lt;/code&gt; w pierwszym listingu. Wykorzystując ten fakt w ostatnim przykładzie mogłem obyć się bez &lt;code class="inline"&gt;enum_for&lt;/code&gt; w bardzo prosty sposób:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;a = [1, 2, 3, 4]
p a.combination(3).find_all {|c| c.inject(:+) % 2 == 0 }
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Czyż to nie piękne? W ten sposób możemy tworzyć własne iteratory i łączyć je we wszystkie możliwe sposoby.&lt;/p&gt;
&lt;h3&gt;map_with_index, select_with_index - zawsze czegoś brakuje&lt;/h3&gt;
&lt;p&gt;Kolejną dosyć częstą bolączką iteratorów był brak dostępu do indeksu iteracji. Dostępny mieliśmy tylko &lt;code class="inline"&gt;each_with_index&lt;/code&gt;. Tutaj klasa &lt;code class="inline"&gt;Enumerator&lt;/code&gt; po raz kolejny okazuje się zbawienna. Otóż metoda &lt;code class="inline"&gt;Enumerator#with_index&lt;/code&gt; tworzy nowy iterator, ale wzbogacony właśnie o indeks. A skoro teraz możemy tworzyć iteratory na podstawie każdej metody...&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;file = File.open(__FILE__)
file.each_line.with_index do |line, i|
  puts "#{i}: #{line}"
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;I w ten oto sposób każdy znany Ci iterator może zostać wzbogacony o indeks. Pozostałe metody tej klasy to &lt;code class="inline"&gt;rewind&lt;/code&gt; (przewija pozycję iteracji na początek) oraz &lt;code class="inline"&gt;with_object&lt;/code&gt; (działa jak &lt;code class="inline"&gt;with_index&lt;/code&gt;, ale przekazuje podany obiekt).&lt;/p&gt;
&lt;p&gt;Istnieje także możliwość utworzenia iteratora za pomocą konstruktora &lt;code class="inline"&gt;Enumerable.new&lt;/code&gt;. Pokażę przykład, a Wam zostawię już przeanalizowanie go.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;fib = Enumerator.new do |y|
 a = b = 1
 loop do
   y &amp;lt;&amp;lt; a
   a, b = b, a + b
 end
end

p fib.take(10) #=&amp;gt; [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
&lt;/code&gt;
&lt;/pre&gt;</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /><category term="enumerator" label="enumerator" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #5 - bloki, domknięcia, nowa lambda</title><link href="http://radarek.jogger.pl/2008/12/12/nowosci-i-zmiany-w-ruby-1-9-5-bloki-domkniecia-nowa-lambda/" /><id>http://radarek.jogger.pl/2008/12/12/nowosci-i-zmiany-w-ruby-1-9-5-bloki-domkniecia-nowa-lambda/</id><updated>2008-12-12T16:13:12Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Kontynuując serię wpisów o nowościach w ruby 1.9 tym razem opiszę zmiany dotyczące bloków, domknięć oraz nowej składni lambdy.&lt;/p&gt;
&lt;p&gt;Zacznijmy od nowej składni dla lamby, za pomocą operatora &lt;code class="inline"&gt;-&amp;gt;&lt;/code&gt;. Przykład pokazuje tworzenie dwóch identycznych domknięć za pomocą starego sposobu i nowego.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;mul2_first  = lambda {|x| x * 2 }
mul2_second = -&amp;gt;(x) { x * 2 }

# można także ominąć () ale w ten sposób raczej pogarszamy czytelność
mul2_third  = -&amp;gt; x { x * 2 }
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jak widać nowa składnia pozwala na bardziej zwięzłe tworzenie domknięć, chociaż można także popsuć czytelność, np. tak:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;foo(:mult =&amp;gt; -&amp;gt; x, y = 2 { x * y })
# z nową składnią Hashy i nawiasami dużo lepiej
foo(mult: -&amp;gt;(x, y = 2) { x * y })
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zatem po raz kolejny trzeba podkreślić, że to od Ciebie zależy jak bardzo czytelny będzie kod.&lt;/p&gt;
&lt;h3&gt;Parametry domyślne i &amp;amp;block - a jednak da się&lt;/h3&gt;
&lt;p&gt;Każdy kto używał domknięć prędzej czy później napotykał na ich ograniczenia w Ruby. Nie można było używać domyślnych wartości, a także przyjmować bloków kodu. Było to czasem irytujące, bo zmuszeni byliśmy czasem pisać tak:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;named_scope :recent,
  lambda {|*args| {:conditions =&amp;gt; ["released_at &amp;gt; ?", (args.first || 2.weeks.ago)]} }
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zamiast:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;named_scope :recent,
  lambda {|date = 2.weeks.ago| {:conditions =&amp;gt; ["released_at &amp;gt; ?", date]} }

# przykład z nową składnią lambdy i hasha
named_scope :recent,
  -&amp;gt;(date = 2.weeks.ago){ {conditions: ["released_at &amp;gt; ?", date]} }
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Ta druga forma jest już na szczęście możliwa i dotyczy to zarówno bloków jak i domknięć. Podobnie jest z przekazywaniem bloku.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def foo(&amp;amp;block)
  puts "foo called"
  block.call("from_foo") { puts "block from foo called" }
end

foo do |msg, &amp;amp;block|
  puts "got msg: #{msg}"
  puts "yielding"
  block.call
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Argumenty bloków są lokalne&lt;/h3&gt;
&lt;p&gt;Parametry bloków są teraz lokalne w stosunku do niego. Oznacza to tyle, że jeśli poza blokiem istnieje zmienna o takiej samej nazwie to jest to całkowicie inna zmienna. Najłatwiej to zabserwować różnicę w zachowaniu poniższego programu w ruby 1.8 i 1.9.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;i = "foo"

puts "Przed blokiem: i = #{i}"
3.times do |i|
  puts "i = #{i}"
end

puts "Poza blokiem: i = #{i}"
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Oto co wypisze program uruchomiony z wersją 1.8:&lt;/p&gt;
&lt;pre&gt;
Przed blokiem: i = foo
i = 0
i = 1
i = 2
Poza blokiem: i = 2
&lt;/pre&gt;
&lt;p&gt;Wersja 1.9 natomiast spowoduje wypisanie:&lt;/p&gt;
&lt;pre&gt;
Przed blokiem: i = foo
i = 0
i = 1
i = 2
Poza blokiem: i = foo
&lt;/pre&gt;
&lt;h3&gt;Parametr bloków jak parametry metod&lt;/h3&gt;
&lt;p&gt;Być może nie wiedziałeś, ale do tej pory można było (zwłaszcza jak ktoś lubi pisać "dziwny" kod) pisać tak:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {}
a = [1, 2, 3]
a.each_with_index do |@a, h["a"]|
  p [@a, h["a"]]
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Działa to dlatego, że działa tutaj semantyka przypisania, tj. kolejne przypisanie są realizowane poprzez &lt;code class="inline"&gt;@a = val1&lt;/code&gt;, &lt;code class="inline"&gt;h["a"] = val2&lt;/code&gt; itp.&lt;/p&gt;
&lt;p&gt;Od wersji 1.9 parametry bloków mają taką samą semantykę jak parametry metod, zatem można tylko używać "normalnych" parametrów (zmienne). Możesz nawet umieścić parametry po parametrach opcjonalnych... Dobra, nie było tematu :).&lt;/p&gt;
&lt;h3&gt;proc == Proc.new&lt;/h3&gt;
&lt;p&gt;W końcu &lt;code class="inline"&gt;proc&lt;/code&gt; jest aliasem do &lt;code class="inline"&gt;Proc.new&lt;/code&gt; (do tej pory był aliasem do ... &lt;code class="inline"&gt;lambda&lt;/code&gt;). Została także dodana metoda &lt;code class="inline"&gt;Proc#lambda?&lt;/code&gt;, która sprawdza czy obiekt domknięcia ma semantykę proc czy lambda.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;closure1 = proc { }
puts closure1.lambda? #=&amp;gt; false

closure2 = Proc.new { }
puts closure2.lambda? #=&amp;gt; false

closure3 = lambda { }
puts closure3.lambda? #=&amp;gt; true

closure4 = -&amp;gt;{}
puts closure4.lambda? #=&amp;gt; true

def make_closure(&amp;amp;block); return block; end
closure5 = make_closure {}
puts closure5.lambda? #=&amp;gt; false
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;.() - nowy sposób na wywołanie domknięcia&lt;/h3&gt;
&lt;p&gt;Dodano nowy sposób na wywołanie domknięcia, tj. za pomocą &lt;code class="inline"&gt;closure.()&lt;/code&gt;. Poniższy przykład pokazuje stare sposoby oraz nowy.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;closure = lambda {|s| puts "#{s}" }

closure.call("foo")
closure["bar"] # nie polecam, wygląda jak tablica...

# nowy sposób
closure.("baz") # zostanie wywołany .call
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Zmienne lokalne dla domknięć&lt;/h3&gt;
&lt;p&gt;Nowością w 1.9 jest możliwość wyspecyfikowania parametrów bloków/domknięć, które mają być lokalne. Chodzi zwłaszcza o sytuację, kiedy na "zewnątrz" bloku istnieje już zmienna o takiej samej nazwie, a my nie chcemy jej użyć (za to chcemy z jakiegoś dziwnego powodu użyć takiej samej nazwy). Robi się to poprzez wylistowanie zmiennych po średniku na liście parametrów (jednak należy podkreślić, że nie jest to parametr, a zmienna lokalna bloku). Mam wrażenie, że jest to trochę zbędne...&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;d = 2
a = lambda{|;d| d = 1}
a.call()
puts d # =&amp;gt; 2
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Myślę, że w ten sposób opisałem wszystkie najważniejsze zmiany dotyczące bloków i domknięć.&lt;/p&gt;
</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /><category term="proc" label="proc" /><category term="lambda" label="lambda" /></entry><entry><title>Blip pragnie Ciebie!</title><link href="http://radarek.jogger.pl/2008/12/09/blip-pragnie-ciebie/" /><id>http://radarek.jogger.pl/2008/12/09/blip-pragnie-ciebie/</id><updated>2008-12-09T13:50:33Z</updated><content type="html">&lt;p&gt;W ramach promowania tego co dobre (no dobra, tak na prawdę to chcę dostać koszulkę ;-)) chciałbym zaproponować Wam serwis &lt;a href="http://blip.pl"&gt;Blip&lt;/a&gt;. Obejrzyj poniższy screencast, żeby zobaczyć jaka jest jego idea. Zapraszam także do &lt;a href="http://blip.pl/users/radarek/dashboard"&gt;śledzenia mnie&lt;/a&gt; (musisz być zalogowany, w przeciwnym wypadku zostaniesz przekierowany na stronę główną), głównie jeśli interesuje Cię cokolwiek związanego z Rubym (często dodaję linki o tej tematyce). Zapraszam do oglądnięcia.&lt;/p&gt;
&lt;p&gt;&lt;object width="437" height="293" type="application/x-shockwave-flash" data="http://www.viddler.com/simple_on_site/7b493f09"&gt;&lt;param name="movie" value="http://www.viddler.com/simple_on_site/7b493f09"&gt;
&lt;param name="quality" value="high"&gt;
&lt;param name="wmode" value="transparent"&gt;
&lt;param name="allowFullScreen" value="true"&gt;
&lt;param name="allowScriptAccess" value="always"&gt;&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;Serwis jest oparty o technologie Rubiego (np. Ruby on Rails). Póki co nie doszły mnie słuchy, żeby jego developerzy narzekali na wydajność. Jak widać da się! (serwis wbrew pozorom "ma co robić")&lt;/p&gt;
</content><category term="blog" label="Blog" /><category term="blip" label="blip" /><category term="reklama" label="reklama" /><category term="ruby" label="ruby" /><category term="rails" label="rails" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #4 - BasicObject jako klasa podstawowa</title><link href="http://radarek.jogger.pl/2008/12/08/nowosci-i-zmiany-w-ruby-1-9-4-basicobject-jako-klasa-podstaw/" /><id>http://radarek.jogger.pl/2008/12/08/nowosci-i-zmiany-w-ruby-1-9-4-basicobject-jako-klasa-podstaw/</id><updated>2008-12-08T22:56:24Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Kolejną zmianą, o której warto wspomnieć (i przy okazji zapamiętać) jest dodanie klasa bazowej &lt;code class="inline"&gt;BasicObject&lt;/code&gt; dla klasy &lt;code class="inline"&gt;Object&lt;/code&gt;. Do tej pory &lt;code class="inline"&gt;Object&lt;/code&gt; była główną klasą w hierarchi, więc nie miała nadklasy o czym można było się przekonać w taki sposób:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;puts Object.superclass
# nil dla Ruby 1.8
# BasicObject dla Ruby 1.9
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Ale co jeśli tworzymy własną klasę &lt;code class="inline"&gt;MyClass&lt;/code&gt;? Czy jej nadklasą będzie &lt;code class="inline"&gt;Object&lt;/code&gt; czy &lt;code class="inline"&gt;BasicObject&lt;/code&gt;? Okazuje się, że tu nie zaszła żadna zmiana, tj. klasą bazową pozostała ta pierwsza klasa. Jak zwykle możemy się o tym przekonać bardzo prosto.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class MyClass
end

puts MyClass.superclass
#=&amp;gt; Object w 1.8 i  1.9
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zmiana jest w zasadzie kosmetyczna. O jej celu zaraz napiszę, wcześniej dwa diagramy by łatwiej Ci było zapamiętać nową zmianę (tak na prawdę chciałem sobie narysować diagram).&lt;/p&gt;
&lt;p&gt;&lt;img class="single" alt="ruby 1.8 class hierarchy" src="http://img505.imageshack.us/img505/7910/rubyclasshierarchy18xe7.png"&gt;&lt;/p&gt;
&lt;p&gt;Hierarchia dziedziczenia w 1.8&lt;/p&gt;
&lt;p&gt;&lt;img class="single" alt="ruby 1.9 class hierarchy" src="http://img255.imageshack.us/img255/5729/rubyclasshierarchy19rw4.png"&gt;&lt;/p&gt;
&lt;p&gt;Hierarchia dziedziczenia w 1.9&lt;/p&gt;
&lt;h3&gt;Po co kolejna nadklasa?&lt;/h3&gt;
&lt;p&gt;Na pierwszy rzut oka zmiana wydaje się dziwna, w końcu wciąż domyślnie dziedziczymy z &lt;code class="inline"&gt;Object&lt;/code&gt; i jest to jedyna klasa dziedzicząca z &lt;code class="inline"&gt;BasicObject&lt;/code&gt;. Sęk w tym, że nie ma żadnej przeszkody by utworzyć klasę dziedziczącą właśnie z tej nowej klasy. Ta klasa powstała by zapewnić możliwość utworzenia klasy z jak najmniejszą liczbą metod, co często się przydaje w tworzeniu wszelakich klas proxy. Jim Weirich tworząc rake &lt;a href="http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc"&gt;nazwał taką "pustą" klasę BlankSlate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Oto przykładowa implementacja takiej klasy.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Proxy &amp;lt; BasicObject
  def initialize(obj)
    @obj = obj
  end

  def method_missing(sym, *args, &amp;amp;block)
    ::Kernel.puts "Sending #{sym}(#{args.join(',')}) to obj"
    return @obj.send(sym, *args, &amp;amp;block)
  end
end

proxy_string = Proxy.new("Ala ma kota")
puts proxy_string.length
proxy_string.reverse!
puts proxy_string
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Program wypisuje na wyjściu:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;Sending length() to obj
11
Sending reverse!() to obj
Sending respond_to?(to_ary) to obj
Sending to_s() to obj
atok am alA
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Zwróć uwagę na &lt;code class="inline"&gt;::Kernel.puts&lt;/code&gt;. Po pierwsze bez :: nie zostałaby znaleziona stała &lt;code class="inline"&gt;Kernel&lt;/code&gt;. Po drugie wywołanie metody &lt;code class="inline"&gt;puts&lt;/code&gt; musiało zostać określone w taki sposób, gdyż klasa &lt;code class="inline"&gt;BasicObject&lt;/code&gt; nie posiada takiej metody (to z kolei wynika z faktu, że nie miksuje modułu &lt;code class="inline"&gt;Kernel&lt;/code&gt;). Zatem próboa wywołania &lt;code class="inline"&gt;puts "..."&lt;/code&gt; skończyłoby się na nieskończonej rekurencji &lt;code class="inline"&gt;method_missing&lt;/code&gt;. Proste, prawda?&lt;/p&gt;
&lt;p&gt;Kolejną zmianę w języku Ruby mamy za sobą. Do następnego razu!&lt;/p&gt;
</content><category term="blog" label="Blog" /><category term="techblog" label="Techblog" /><category term="ruby" label="ruby" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #3 - zmiany odnośnie argumentów metod</title><link href="http://radarek.jogger.pl/2008/12/06/nowosci-i-zmiany-w-ruby-1-9-3-zmiany-odnosnie-argumentow-met/" /><id>http://radarek.jogger.pl/2008/12/06/nowosci-i-zmiany-w-ruby-1-9-3-zmiany-odnosnie-argumentow-met/</id><updated>2008-12-06T16:59:25Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Kontynuując mały cykl tym razem chciałbym opisać zmianę dotyczącą parametrów metod. Do tej pory deklaracja metody wyglądała następująco: &lt;code class="inline"&gt;def fun(r&lt;sub&gt;1&lt;/sub&gt;, r&lt;sub&gt;2&lt;/sub&gt;, .., r&lt;sub&gt;m&lt;/sub&gt;, o&lt;sub&gt;1&lt;/sub&gt;=v&lt;sub&gt;1&lt;/sub&gt;, o&lt;sub&gt;2&lt;/sub&gt;=v&lt;sub&gt;2&lt;/sub&gt;, ..., o&lt;sub&gt;n&lt;/sub&gt;=v&lt;sub&gt;n&lt;/sub&gt;[, *rest][, &amp;amp;block])&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Suchy zapis jest często ciężki w odbiorze, zatem opisując słownie mamy: lista parametrów wymaganych (może być pusta), lista parametrów opcjonalnych (może być pusta), opcjonalny argument zbierający pozostałe parametry, opcjonalny parametr bloku. Prezentuje to przykładowy kod:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def foo(a, b = 1, *rest, &amp;amp;block)
  p [a, b, rest, block]
end

foo(10)
foo(10, 20)
foo(10, 20, 30)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Uruchamiając powyższy program otrzymamy:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[10, 1, [], nil]
[10, 20, [], nil]
[10, 20, [30], nil]
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Parametry wymagane z prawej strony listy?&lt;/h3&gt;
&lt;p&gt;Nowością, jakby nie patrzeć dosyć kontrowersyjną, jest możliwość umieszczenia wymaganych parametrów &lt;strong&gt;po&lt;/strong&gt; parametrach opcjonalnych oraz &lt;code class="inline"&gt;*rest&lt;/code&gt;, ale przed &lt;code class="inline"&gt;&amp;amp;block&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Zatem formalnie zapisując wygląda to tak: &lt;code class="inline"&gt;def fun([r&lt;sub&gt;1&lt;/sub&gt;, r&lt;sub&gt;2&lt;/sub&gt;, .., r&lt;sub&gt;m&lt;/sub&gt;][, o&lt;sub&gt;1&lt;/sub&gt;=v&lt;sub&gt;1&lt;/sub&gt;, o&lt;sub&gt;2&lt;/sub&gt;=v&lt;sub&gt;2&lt;/sub&gt;, ..., o&lt;sub&gt;n&lt;/sub&gt;=v&lt;sub&gt;n&lt;/sub&gt;][, *rest][, r&lt;sub&gt;m+1&lt;/sub&gt;, r&lt;sub&gt;m+2&lt;/sub&gt;, ..., r&lt;sub&gt;m+l&lt;/sub&gt;][, &amp;amp;block])&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Opisując słownie mamy: lista parametrów wymaganych (może być pusta), lista parametrów opcjonalnych (może być pusta), opcjonalny argument zbierający pozostałe parametry, lista parametrów wymaganych (może być pusta), opcjonalny parametr bloku. Prezentuje to przykładowy kod:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def foo(a, b = 1, *rest, c, &amp;amp;block)
  p [a, b, rest, c, block]
end

foo(10, 20)
foo(10, 20, 30)
foo(10, 20, 30, 40)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Powyższy przykład tworzy na wyjściu:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[10, 1, [], 20, nil]
[10, 20, [], 30, nil]
[10, 20, [30], 40, nil]
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Na początku wydaje się bardzo ciężkie do rozszyfrowania, który parametr pójdzie do którego argumentu, ale po kilku chwilach okazuje się, że nie jest to takie trudne. Należy zwrócić uwagę na kilka rzeczy:&lt;br&gt;
1) musimy przekazać przynajmniej tyle parametrów ile jest wymaganych (czyli m+l)&lt;br&gt;
2) jeśli nie użyto argumentu przechwytującego resztę parametrów (*rest) to można przekazać&lt;br&gt;
maksymalnie n+m+l parametrów&lt;br&gt;
3) nie można przeplatać argumentów opcjonalnych z wymaganymi&lt;br&gt;
4) argumenty wymagane r&lt;sub&gt;1&lt;/sub&gt;..r&lt;sub&gt;m&lt;/sub&gt; są dopasowywane od lewej strony parametrów wywołania&lt;br&gt;
5) argumenty wymagane r&lt;sub&gt;m+1&lt;/sub&gt;..r&lt;sub&gt;m+l&lt;/sub&gt; są dopasowywane od prawej strony parametrów wywołania&lt;br&gt;
6) pozostałe argumenty są dopasowywane do argumentów opcjonalnych i argumentu przechwytującego (*rest)&lt;/p&gt;
&lt;p&gt;Zwróć uwagę, że punkty 1, 2, 3, 4, 6 obowiązywały do tej pory. Zatem tak na prawdę doszła tylko jedna zasada.&lt;/p&gt;
&lt;h3&gt;WTF? OMFG! LOL!&lt;/h3&gt;
&lt;p&gt;Domyślam się co chcesz powiedzieć. Zapewne coś w stylu "WTF, o co tu chodzi, po co, dlaczego? Nie widzę w tym w ogóle sensu.". Ja też podobnie na to zareagowałem (do tej pory nie spotkałem się z czymś takim). Zamiast jednak rozpaczać możemy podejść do tego na dwa sposoby (my jako społeczność). Po pierwsze można to zupełnie zignorować, udawać, że tego nie było i nie ma, nie używać i namawiać innych żeby tego nie robili. Możemy też siąść i spróbować opracować rzeczywiste zastosowania. W końcu nie raz z dziwnych pomysłów powstawały ciekawe rzeczy. A przecież Ruby posiada wiele różnych, w pewnym sensie "egzotycznych", konstrukcji, które czekają na specjalną okazję by je zastosować. W końcu ile razy użyłeś w kodzie &lt;code class="inline"&gt;Object#instance_eval&lt;/code&gt;, &lt;code class="inline"&gt;Module#const_missing&lt;/code&gt;, &lt;code class="inline"&gt;Module#nesting&lt;/code&gt;, &lt;code class="inline"&gt;Proc#binding&lt;/code&gt;? Prawdopodobnie niewiele, a może nawet w ogóle. Dlatego nie rozpaczajmy, tylko pomyślmy do czego możemy wykorzystać tą nową konstrukcję.&lt;/p&gt;
&lt;p&gt;Koichi SASADA pokazał ostatnio na ruby-core jedno z zastosowań, które dotyczy metody &lt;code class="inline"&gt;[]=&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class MyArray &amp;lt; Array
  def []=(*args, value)
    p [args, value]
  end
end

arr = MyArray.new
arr[1, 2, 3] = "foo"
arr[100] = "bar"
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Program wypisuje:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[[1, 2, 3], "foo"]
[[100], "bar"]
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;W przypadku metod &amp;lt;nazwa&amp;gt;= jako ostatni parametr (który i tak jest wymagany) dostajemy wartość po prawej stroni przypisania. Jak widzisz możliwość umieszczenia wymaganego parametru po prawej stronie już była wcześniej w języku ;-).&lt;/p&gt;
&lt;p&gt;Myślę, że jednym z zastosowań może być użycie pseudo "nazwanych argumentów", czyli hasha z opcjami.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# stary sposób
def before_filter(*actions)
  options = actions.last.is_a?(Hash) ? actions.pop : {}
  # ...
end

before_filter :authenticate, :authorize, :except =&amp;gt; "list"
before_filter :authenticate, :authorize

# nowy sposób
def before_filter(*actions, options)
  # ...
end

before_filter :authenticate, :authorize, except: "list"
before_filter :authenticate, :authorize, {}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Niestety nowy sposób też ma swoją wadę, bo trzeba podać jawnie pusty hash w przypadku braku dodatkowych opcji (chociaż dla niektórych to może być zaleta).&lt;/p&gt;
&lt;p&gt;Z parametrami metod wiąże się jeszcze jedna zmiana. Otóż argumenty można grupować za pomocą &lt;code class="inline"&gt;()&lt;/code&gt;, co spowoduje użycie semantyki przypisania równoległego do podanych parametrów. Pokazuje to ostatni już przykład.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def foo(a, (b, c))
  p [a, b, c]
end

foo(1, 2)         # a = 1, b = 2, c = nil
foo(1, [])        # a = 1, b = nil, c = nil
foo(1, [2, 3])    # a = 1, b = 2, c = 3
foo(1, [2, 3, 4]) # a = 1, b = 2, c = 3

def bar((head1, *rest1), (head2, *rest2))
  p [head1, rest1, head2, rest2]
end

bar([], [])                 # head1 = nil, rest1 = [], head2 = nil, rest2 = []
bar([1], 2)                 # head1 = 1, rest1 = [], head2 = 2, rest2 = []
bar([1, 2, 3], [4, 5, 6])   # head1 = 1, rest1 = [2, 3], head2 = 4, rest2 = [5, 6]
&lt;/code&gt;
&lt;/pre&gt;</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #2 - nowa składnia Hasha</title><link href="http://radarek.jogger.pl/2008/12/02/nowosci-i-zmiany-w-ruby-1-9-2-nowa-skladnia-hasha/" /><id>http://radarek.jogger.pl/2008/12/02/nowosci-i-zmiany-w-ruby-1-9-2-nowa-skladnia-hasha/</id><updated>2008-12-02T19:03:39Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Tym razem chciałbym pokazać Wam nową zmianę w składni języka, która dotyczy także Hasha. Zmiana ta dotyczy nowej składni dla literału hasha, którego kluczami są symbole. Do tej pory taki hash definiowaliśmy w następujący sposób:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {:foo =&amp;gt; 1, :bar =&amp;gt; 2}
p h
# =&amp;gt; {:foo=&amp;gt;1, :bar=&amp;gt;2}&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Od wersji 1.9 można to zrobić trochę inaczej&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {foo: 1, bar: 2}
p h
# =&amp;gt; {:foo=&amp;gt;1, :bar=&amp;gt;2}&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jeszcze raz podkreślam, że nową składnię można zastosować tylko jeśli kluczami są symbole. Dla innych kluczy musisz użyć dotychczasowej składni. Wykonując mały eksperyment możesz przekonać się, że można równocześnie stosować nowy i stary sposób...&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {foo: 1, bar: 2, "baz" =&amp;gt; 3}
p h
# =&amp;gt; {:foo=&amp;gt;1, :bar=&amp;gt;2, "baz" =&amp;gt; 3}&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;...jednak z oczywistych powodów odradzam takie praktyki.&lt;/p&gt;
&lt;p&gt;Przekazując hash jako jedyny lub ostatni parametr metody można ominąć nawiasy {}. Dzięki temu zyskujemy na czytelności bo zamiast&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;User.find(:all, {:order =&amp;gt; "login", :limit =&amp;gt; 5})
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;możemy napisać&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;User.find(:all, :order =&amp;gt; "login", :limit =&amp;gt; 5)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;a stosując nową składnie&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;User.find(:all, order: "login", limit: 5)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Prawda, że przejrzyście to wygląda? A dwukropek dużo łatwiej się stawia niż znak "spacja, znak równości, znak większości".&lt;/p&gt;
&lt;h3&gt;Parametry nazwane - czy to już to?&lt;/h3&gt;
&lt;p&gt;Niektóre języki (np. Python) posiadają obsługę tzw. nazwanych parametrów. Dzięki temu wywołując możemy takie parametry podać w dowolnej kolejności. Od strony wywołania wygląda to podobnie jak ostatni przykład. Wbudowana obsługa takich parametrów pozwala na weryfikację przekazanych parametrów przez sam język. Dzięki nowej składni hashy możemy w pewien sposób symulować takie parametry, ale musimy wykonać dodatkową pracę by zweryfikować przekazane klucze.&lt;/p&gt;
&lt;p&gt;Na pełną obsługę parametrów nazwanych musimy niestety poczekać przynajmniej do wersji 2.0.&lt;/p&gt;
</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /></entry><entry><title>Nowości i zmiany w Ruby 1.9 #1 - ordered Hash</title><link href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9-1-ordered-hash/" /><id>http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9-1-ordered-hash/</id><updated>2008-11-30T20:13:24Z</updated><content type="html">&lt;div class="info"&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;" alt="ruby 1.9 changes approved - logo"&gt;
&lt;p&gt;Wpis ten jest jedną z części cyklu pt "Nowości i zmiany w Ruby 1.9". Pełną listę wpisów znajdziesz pod adresem &lt;a href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/"&gt;http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Od wersji 1.9 Ruby zachowuje kolejność elementów w Hashu ("ordered Hash", czyli Hash zachowujący kolejność). Oznacza to, że iteracja po elementach hasha następuje dokładnie w takiej samej kolejności w jakiej były &lt;strong&gt;wstawiane elementy&lt;/strong&gt;. Podkreślam słowo &lt;strong&gt;"wstawiane"&lt;/strong&gt;, co nie ma związku z sortowaniem kluczy (np. leksykograficznie).&lt;/p&gt;
&lt;p&gt;Poniższy kod pokazuje różnicę między 1.8 i 1.9.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {"c" =&amp;gt; 3, "a" =&amp;gt; 1, "b" =&amp;gt; 2}
h.each do |k, v|
  p [k, v]
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;1.8&lt;/h3&gt;
&lt;pre&gt;
$ ruby1.8 -v &amp;amp;&amp;amp; ruby1.8 ordered_hash01.rb 
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
["a", 1]
["b", 2]
["c", 3]
&lt;/pre&gt;
&lt;h3&gt;1.9&lt;/h3&gt;
&lt;pre&gt;
$ ruby1.9.1 -v &amp;amp;&amp;amp; ruby1.9.1 ordered_hash01.rb 
ruby 1.9.1 (2008-11-30 revision 20428) [x86_64-linux]
["c", 3]
["a", 1]
["b", 2]
&lt;/pre&gt;
&lt;p&gt;
&lt;p&gt;Kolejność kluczy nie zmienia się gdy nadpisujemy istniejący element. Jeśli tego chcemy musimy najpierw usunąć stary klucz i dodać go jeszcze raz. Obrazuje to poniższy przykład.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;h = {}
h["a"] = "foo"
h["b"] = "bar"
p h
# {"a"=&amp;gt;"foo", "b"=&amp;gt;"bar"}

h["a"] = nil
p h
# {"a"=&amp;gt;nil, "b"=&amp;gt;"bar"}

h.delete("a")
h["a"] = "foo"
p h
# {"b"=&amp;gt;"bar", "a"=&amp;gt;"foo"}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Jeśli chodzi o kwestię wydajności to zapamiętywanie kolejności kluczy ponosi za sobą dosyć niewielki koszt. W kwestii pamięci są to jedynie dwa wskaźniki przypadające na jeden element hasha (by pamiętać następny i poprzedni). Koszt operacji dodawania/usuwania wzrósł o koszt dodania/usunięcia takiego elementu z listy dwukierunkowej mając już wskaźnik do niego. Jest to zatem kwestia kilku "przepięć" wskaźników (złożoność O(1)).&lt;/p&gt;
&lt;h3&gt;Praktyczne użycie&lt;/h3&gt;
&lt;p&gt;Przykładowo implementując model w RoR/Merb chcemy definiować tłumaczenie dla nazw jakiegoś wyliczeniowego atrybutu (np. status). Mogłoby to wyglądać tak:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;# model
class User
  STATUS_LABELS = {
    "active"    =&amp;gt; "aktywny",
    "deleted"   =&amp;gt; "usunięty"
  }
end

# widok
&amp;lt;%= select :user, :status, User::STATUS_LABELS %&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Dzięki temu, że kolejność kluczy jest zapamiętywana lista wyboru wyświetli elementy w takiej samej kolejności.&lt;/p&gt;
</content><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /></entry><entry><title>Nowości i zmiany w Ruby 1.9</title><link href="http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/" /><id>http://radarek.jogger.pl/2008/11/30/nowosci-i-zmiany-w-ruby-1-9/</id><updated>2008-11-30T16:12:14Z</updated><content type="html">&lt;p&gt;&lt;img src="http://img150.imageshack.us/img150/3792/ruby19approvedhf8.jpg" style="float: left; margin: 0 1em 0 0;"&gt; &lt;a href="http://radarek.jogger.pl/2007/12/31/ruby-1-9-wydany-opis-glownych-zmian-i-nowosci/"&gt;Blisko rok temu pisałem&lt;/a&gt; o zmianach jakie zaszły w wersji 1.9 Rubiego. W kilku następnych wpisach opiszę bardziej szczegółowo najważniejsze z nich. Prócz samego pokazania na czym dana zmiana/nowość polega postaram się także pokazać jakiś użyteczny kod, który wykorzystuje nowy "ficzer". Jak zwykle będą osoby, które pokochają nowe elementy języka, a także takie, które będą kręcić nosem. Tym bardziej, że o niektórych modyfikacjach języka można powiedzieć, że są kontrowersyjne (a przynajmniej po pierwszym spojrzeniu). W takich wypadkach opiszę także jak należy prawidłowo (co oczywiście będzie tylko moim punktem widzenia) używać konstrukcji lub czego się wystrzegać. Innymi słowy: to czego nie znajdziecie w "suchym" manualu (tudzież Changelogu).&lt;/p&gt;
&lt;p&gt;Lista wpisów dotyczących zmian w Ruby 1.9 (aktualizowana na bieżąco):&lt;/p&gt;
&lt;ul class="classic"&gt;
&lt;li&gt;&lt;a href="/2008/11/30/nowosci-i-zmiany-w-ruby-1-9-1-ordered-hash/"&gt;#1 - ordered Hash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2008/12/02/nowosci-i-zmiany-w-ruby-1-9-2-nowa-skladnia-hasha/"&gt;#2 - nowa składnia Hasha&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2008/12/06/nowosci-i-zmiany-w-ruby-1-9-3-zmiany-odnosnie-argumentow-met/"&gt;#3 - zmiany odnośnie argumentów metod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2008/12/08/nowosci-i-zmiany-w-ruby-1-9-4-basicobject-jako-klasa-podstaw/"&gt;#4 - BasicObject jako klasa podstawowa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2008/12/12/nowosci-i-zmiany-w-ruby-1-9-5-bloki-domkniecia-nowa-lambda/"&gt;#5 - bloki, domknięcia, nowa lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2008/12/14/nowosci-i-zmiany-w-ruby-1-9-iteratory-enumerator/"&gt;#6 - iteratory (klasa Enumerator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2009/02/12/nowosci-i-zmiany-w-ruby-1-9-obsluga-kodowan-znakow/"&gt;#7 - obsługa kodowań znaków&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="ruby" label="Ruby" /><category term="ruby1-9" label="ruby1.9" /><category term="1-9" label="1.9" /></entry><entry><title>Python (Ruby) Challenge #2, poziomy 6-10</title><link href="http://radarek.jogger.pl/2008/11/21/python-ruby-challenge-2-poziomy-6-10/" /><id>http://radarek.jogger.pl/2008/11/21/python-ruby-challenge-2-poziomy-6-10/</id><updated>2008-11-21T18:38:28Z</updated><content type="html">&lt;p&gt;&lt;a href="/2007/05/29/python-ruby-challenge/"&gt;Jakiś czas temu starałem się zachęcić Was&lt;/a&gt; do zabawy pod nazwą &lt;a href="http://www.pythonchallenge.com/"&gt;"Python Challenge"&lt;/a&gt;. Umieściłem wtedy rozwiązania pierwszych 5 zagadek. Brak większego odzewu spowodował, że do tej pory nie opisałem rozwiązań kolejnych zagadek. Uważam jednak, że temat jest wart odświeżenia.&lt;/p&gt;
&lt;p&gt;Jeśli jeszcze nie wiesz jaka jest idea tej zabawy to opiszę Ci ją w kilku zdaniach. Idea jest następująca. Zabawę zaczynasz od &lt;a href="http://www.pythonchallenge.com/pc/def/0.html"&gt;strony startowej&lt;/a&gt;. Twoim celem jest odgadnięcie adresu następnego etapu. Podpowiedzi należy szukać w obrazku widocznym na stronie jak i w samym jej źródle. Przykładowo w zagadce oznaczonej numerem zero widać monitor, na którym jest napis "2&lt;sup&gt;38&lt;/sup&gt;". Jeśli obliczymy wartość tego wyrażenia a następnie podmienimy w adresie URL 0 na tą wartość dostaniemy się do następnego etapu. Następny poziomy są co raz trudniejsze.&lt;/p&gt;
&lt;p&gt;Chociaż strona sugeruje, że etapy należy pokonywać przy pomocy Pythona to w praktyce możemy użyć dowolnego języka/narzędzia. Tylko 2 (lub coś koło tego) zagadki wymagają Pythona (ze względu na specyficzne moduły, np do serializacji obiektów). Dodam tylko, że osobiście uważam, że taki rodzaj zabawy jest także świetnym sposobem na naukę języka.&lt;/p&gt;
&lt;h3&gt;Rozwiązania poziomów 6-10&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Uwaga!&lt;/strong&gt; Poniżej zamieszczam rozwiązania kolejnych poziomów (6-10). Nie czytaj dalej jeśli nie chcesz psuć sobie zabawy.&lt;/p&gt;
&lt;h4&gt;Poziom 6&lt;/h4&gt;
&lt;p&gt;Otwierając źródło w pierwszej linijce widzimy:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="html"&gt;&amp;lt;html&amp;gt; &amp;lt;!-- &amp;lt;-- zip --&amp;gt;
...
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Strzałka wskazuje na "html", co sugeruje by zamienić końcówkę adresu URL z "html" na "zip". Plik channel.zip pobieramy na dysk. Otwierając plik możemy zauważyć, że archiwum zawiera pliki o nazwach będących numerami. Zaglądamy do którego z plików i widzimy mniej więcej taką treść:&lt;/p&gt;
&lt;pre&gt;
Next nothing is 57831
&lt;/pre&gt;
&lt;p&gt;Co oczywiście powinno przypominać nam jeden z wcześniejszych poziomów. Jeśli dobrze się przyjrzymy to zauważymy także plik readme.txt o zawartości:&lt;/p&gt;
&lt;pre&gt;
welcome to my zipped list.

hint1: start from 90052
hint2: answer is inside the zip
&lt;/pre&gt;
&lt;p&gt;Pierwsza myśl podpowiada nam by rozpakować archiwum i zaczynając od pliku 90052.txt dotrzeć do takiego, który nie będzie zawiera informacji o następnym. Niestety tak znaleziony plik zawiera tylko:&lt;/p&gt;
&lt;pre&gt;
Collect the comments.
&lt;/pre&gt;
&lt;p&gt;Okazuje się, że do plików w archiwum zip można dodawać notatki (komentarze). I to w nich znajduje się rozwiązanie zagadki. Ja skorzystałem z gema &lt;a href="http://rubyforge.org/projects/zipruby"&gt;zipruby&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require 'rubygems'
require 'zipruby'

file = "90052.txt"; out = ""
Zip::Archive.open('channel.zip') do |zip|
  loop do
    zf = zip.fopen(file)
    out &amp;lt;&amp;lt; zf.comment
    file = zf.read.match(/Next nothing is (\d+)/)[1] + ".txt" rescue break
    zf.close
  end
end

puts out

&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
****************************************************************
****************************************************************
**                                                            **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE NN      NN  **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE  NN    NN   **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE       NN  NN    **
**   OOOOOOOO XX    XX YY        GGG       EEEEE     NNNN     **
**   OOOOOOOO XX    XX YY        GGG       EEEEE      NN      **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE         NN      **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE     NN      **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE     NN      **
**                                                            **
****************************************************************
 **************************************************************
&lt;/pre&gt;
&lt;p&gt;Ale to nie "hockey" a "oxygen" jest rozwiązaniem...&lt;/p&gt;
&lt;h4&gt;Poziom 7&lt;/h4&gt;
&lt;p&gt;Szybkie spojrzenie na obrazek i możemy zauważyć, że:&lt;br&gt;
na obrazku znajduje się pasek w odcieniach szarości&lt;br&gt;
pasek zaczyna się od piksela 0 (współrzędna y), a kończy na 607&lt;br&gt;
pasek podzielony jest na regularne bloki o rozmiarach 7x9 pikseli (prócz pierwszego bloku)&lt;br&gt;
Ponieważ kolory szare mają taką samą wartość dla kanałów RGB oraz ich wartości (dla obrazków 24bit) są z przedziału 0-255 to możemy przypuszczać, że kolejne bloki reprezentują znaki ASCII.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require 'rubygems'
require 'open-uri'
require 'RMagick'

image = Magick::Image.read('http://www.pythonchallenge.com/pc/def/oxygen.png')[0]
x, y = 0, 50
while x &amp;lt;= 607
  print((image.pixel_color(x, y).red &amp;gt;&amp;gt; (Magick::QuantumDepth - 8)).chr)
  x += 7
end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Uruchamiając dostajemy:&lt;/p&gt;
&lt;pre&gt;
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
&lt;/pre&gt;
&lt;p&gt;Musimy więc jeszcze podobny zabieg zrobić z podanym ciągiem liczb:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;#smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
puts [105, 110, 116, 101, 103, 114, 105, 116, 121].map {|e| e.chr}.join
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Rozwiązaniem jest:&lt;/p&gt;
&lt;pre&gt;
integrity
&lt;/pre&gt;
&lt;h4&gt;Poziom 8&lt;/h4&gt;
&lt;p&gt;Obrazek przedstawia pszczołę. Po najechaniu na nią kursorem okazuje się, że jest to link. Niestety po kliknięciu jesteśmy poproszeni o podanie loginu i hasła. Zaglądamy więc do źródła:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!--
un: 'BZh91AY&amp;amp;SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07&amp;lt;]\xc9\x14\xe1BA\x06\xbe\x084'
pw: 'BZh91AY&amp;amp;SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13&amp;lt;]\xc9\x14\xe1BBP\x91\xf08'
--&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Wpisując w google wspólny początek obu tych łańcuchów ("BZh91AY&amp;gt;SY") powinniśmy bez trudu dostrzec, że ma to związek z kompresją bzip2.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def bunzip2(binary)
  open("|bunzip2", "r+") do |cmd|
    cmd.write(binary)
    cmd.close_write
    puts cmd.gets
  end
end

un = "BZh91AY&amp;amp;SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07&amp;lt;]\xc9\x14\xe1BA\x06\xbe\x084"
pw = "BZh91AY&amp;amp;SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13&amp;lt;]\xc9\x14\xe1BBP\x91\xf08"

bunzip2(un)
bunzip2(pw)

#huge file
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Program wypisuje:&lt;/p&gt;
&lt;pre&gt;
huge
file
&lt;/pre&gt;
&lt;p&gt;Co jest szukanym loginem i hasłem.&lt;/p&gt;
&lt;h4&gt;Poziom 9&lt;/h4&gt;
&lt;p&gt;Tytuł mówi "connect the dots". Jednak nie chodzi o punkty na obrazku (aczkolwiek obrazek jest kluczową podpowiedzią). W źródle znajdziemy ciąg liczb oznaczonych etykietami "first:" i "second:". Biorąc po 2 liczby należy rysować punkty.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require 'rubygems'
require 'open-uri'
require 'RMagick'

content = open("http://www.pythonchallenge.com/pc/return/good.html", :http_basic_authentication =&amp;gt; ["huge", "file"]).read
first = content[/first:\n(.*?)\n\n/m, 1].split(/,\n?/).map {|n| n.to_i }
second = content[/second:\n(.*?)\n\n/m, 1].split(/,\n?/).map {|n| n.to_i }

max_coordinate = (first + second).max
image = Magick::Image.new(max_coordinate, max_coordinate) do
  self.background_color = 'white'
end

draw = Magick::Draw.new
draw.fill('white')
draw.stroke('black')

draw.polygon(*first)
draw.polygon(*second)
draw.draw(image)

image.display
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://img201.imageshack.us/img201/1141/zrzutekranucz6.png"&gt;&lt;img class="single" src="http://img201.imageshack.us/img201/1141/zrzutekranucz6.th.png" alt="bull"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Jak nie trudno się domyślić, szukany wyraz to: bull.&lt;/p&gt;
&lt;h4&gt;Poziom 10&lt;/h4&gt;
&lt;p&gt;Kliknięcie w byka na obrazku prowadzi do pliku sequence.txt o treści:&lt;/p&gt;
&lt;pre&gt;
a = [1, 11, 21, 1211, 111221, 
&lt;/pre&gt;
&lt;p&gt;Nasze zadanie to obliczyć len(a[30]), czyli długość 30 elementu tej sekwencji. Nie jest łatwo odgadnąć jak powstaje ten ciąg, ale od czego mamy google? Wpisanie tych elementów do wyszukiwarki doprowadzi nas do wyjaśnienia znaczenia tego ciągu. Oto jak powstają elementy tej sekwencji:&lt;br&gt;
liczba - opis słowny&lt;br&gt;
1 - jedna jedynka (11)&lt;br&gt;
11 - dwie jedynki (21)&lt;br&gt;
21 - jedna dwójka, jedna jedyna (1211)&lt;br&gt;
1211 - jedna jedynka, jedna dwójka, dwie jedynki (111221)&lt;br&gt;
itd&lt;/p&gt;
&lt;p&gt;Ma to nawet swoją nazwę &lt;a href="http://en.wikipedia.org/wiki/Look-and-say_sequence"&gt;Look-and-say sequence&lt;/a&gt; (a także "Morris Number Sequence").&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;def look_and_say_sequence(n)
  return "1" if n == 0
  return look_and_say_sequence(n - 1).scan(/((.)(\2*))/).map{|m| "#{m[0].size}#{m[1]}"}.join
end

puts look_and_say_sequence(30).length
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Szukane rozwiązanie:&lt;/p&gt;
&lt;pre&gt;
5808
&lt;/pre&gt;</content><category term="python" label="Python" /><category term="ruby" label="Ruby" /><category term="techblog" label="Techblog" /><category term="challenge" label="challenge" /><category term="game" label="game" /><category term="programming" label="programming" /><category term="riddles" label="riddles" /></entry></feed>

