Archive for Programming

Prawie jak lambda, w Javie

2011-06-02 20:52

Wiadomo powszechnie, że Java listenerami stoi i typowe jest używanie jest różnego rodzaju klas wewnętrznych, które są następnie podawane jako interfejsy do wywołań zwrotnych. W ten sposób obsługuje się różnego rodzaju zdarzenia, począwszy od interakcji użytkownika z programem aż po ważne notyfikacje pochodzące z systemu operacyjnego.
Gdy klasa zawierające takie handlery dostatecznie się rozrośnie, pojawia się oczywiście potrzeba jej zrefaktorowania i podzielenia na dwie lub większą liczbę mniejszych. W trakcie tego procesu czasami chciałoby się też wydzielić owe klasy wewnętrzne, obsługujące zdarzenia – i wtedy możemy napotkać pewien kłopot. Kłopocik właściwie ;)

Dotyczy on zależności między klasami wewnętrznymi a klasą je otaczającą. Ponieważ mówimy o niestatycznych niestatycznych klasach wewnętrznych, typowe jest odwoływanie się do składników klasy otaczającej z klasy zewnętrznej. Mówiąc bardziej po ludzku, implementacja np. zdarzenia kliknięcia przycisku może sięgać do obiektu okna/dialogu/itp., zawierającego tenże przycisk:

  1. private final OnClickListener buttonsListener = new View.OnClickListener() {
  2.     @Override
  3.     public void onClick(View v) {
  4.         if (v.getId() == R.id.exit_button) finish();
  5.     }
  6. }

Przeniesienie całej powyższej klasy w inne miejsce nastręcza wówczas problem, gdyż odwołuje się ona do metody klasy ją otaczającej (czyli finish()). Należałoby więc posiadać obiekt tej klasy, zapewnić aby rzeczona metoda była dostępna (co nie zawsze jest wskazane), aby wywoływać ją z właściwymi parametrami – które też trzeba jakoś przekazać – i tak dalej… Krótko mówiąc na naszej drodze do ładnie zorganizowanego kodu naraz pojawia się sporo przeszkód.

Czy nie dałoby się po prostu jakoś przekazać tego “kawałka kodu”, tych kilku czy kilkunastu instrukcji opakowanych w coś, co można podać w inne miejsce programu?… Okazuje się, że jak najbardziej, a rozwiązaniem na problem refaktorowanych klas wewnętrznych jest… więcej klas wewnętrznych ;-) Nic nie stoi bowiem na przeszkodzie, aby rzeczony fragment procedury zdarzeniowej zapakować w metodę klasy implementującej interfejs Runnable. Tak, dokładnie ten interfejs który zazwyczaj kojarzy się z wątkami i klasą Thread. W rzeczywistości reprezentuje on “cokolwiek co da się uruchomić”, więc jak ulał pasuje w charakterze rozwiązania problemu.
Aplikując je do powyższego przykładu, możemy wydzielić powyższy listener (potencjalnie wraz z kilkunastoma podobnymi) i kazać mu jedynie wywoływać metodę run jakiegoś obiektu Runnable. W niej zaś umieszczamy nasz pierwotny kod i przekazujemy do nowo wydzielonej klasy:

  1. Buttons.setExitAction(new Runnable() {
  2.     @Override
  3.     public void run() { finish(); }
  4. }

Znawcy tematu powiedzieliby, że w ten sposób utworzyliśmy domknięcie (closure), bo w inne miejsce programu przekazaliśmy fragment kodu wraz z jego kontekstem; w tym przypadku jest to chociażby referencja this na rzecz której wywoływany jest finish. Fani programowania funkcyjnego stwierdzą z kolei, że ta technika jest kiepską imitacją wyrażeń lambda i najprawdopodobniej też będą mieli rację.
Nie zmienia to jednak faktu, że jeśli programujemy w starej (nie)dobrej Javie, to technika ta może być po prostu użyteczna – niezależnie od tego, jaki paradygmat za nią stoi. Dlatego też chciałem dzisiaj się nią podzielić.

Tags: , , , ,
Author: Xion, posted under Programming » 9 comments

Wielki atraktor Wikipedii

2011-05-25 23:29

Za sprawą najnowszego komiksu z xkcd – a właściwie tekstu, który pojawia się po najechaniu myszką na obrazek – w Internecie robi karierę mem na temat rzekomej “zbieżności” artykułów Wikipedii do hasła ‘filozofia’. Zbieżność ta była zdefiniowana przez kolejne kliknięcia w pierwszy (z paroma wyjątkami) link w każdym artykule i kolejne powtarzanie tego procesu. W końcu powinniśmy trafić na wspomniany tekst o filozofii.
Zabawną cechą tego memu jest fakt, iż stosuje się do niego swego rodzaju zasada nieoznaczoności: samym swoim istnieniem (i popularnością) oddziałuje on na zjawisko, które zdaje się opisywać. W końcu mówimy tutaj o serwisie, którego zawartość edytują tysiące użytkowników, a dzięki całej tej plotce ich uwaga jest tym bardziej skupiona na tekstach leżących na ścieżce do tej nieszczęsnej ‘filozofii’… jeśli takowa faktycznie istnieje.

A czy istnieje? No właśnie :) Traktując to jako swego rodzaju pouczającą zabawę, postanowiłem zbadać sprawę i wysmażyłem odpowiedni program, który potrafi automatycznie wykonywać procedurę opisaną na początku, tj. kolejno przechodzić przez teksty w Wikipedii i “klikać” w pierwsze linki z ich treści. Szybko zorientowałem się wtedy, że twierdzenie o zbieżności do Philosophy jest tylko wycinkiem większej, bardziej interesującej całości.
Okazało się bowiem, że Wikipedia ma coś w rodzaju uniwersalnego atraktora, którego ‘filozofia’ jest częścią. Ma on postać cyklu zmieniającego się w czasie (z powodu edycji, oczywiście) i obejmuje interesujący zestaw pojęć, które semantycznie są połączone zależnością rekurencyjną. Obserwując kształt owego cyklu w dłuższym przedziale czasowym, można by zapewne wyodrębnić terminy zupełnie podstawowe, które są w nim obecne stale lub wypadają tylko na krótką chwilę. Kto wie, może w ten sposób udało by się dokonać jakiegoś fundamentalnego odkrycia z dziedziny filozofii, na przykład odpowiedzieć na pytanie o życie, Wszechświat i całą resztę? ;-)

Nieco mniej ambitnym wyzwaniem jest zbadanie, jak daleko od atraktora znajdują się pewne hasła. Innymi słowy, ile kliknięć dzieli nas od wpadnięcia w cykl zapętlających się pojęć, jeśli zaczynamy od danego artykułu? Jeśli zastanowimy się nad tym chwilę, to można dojść do wniosku, że odpowiedź byłaby miarą odwrotności “stopnia abstrakcji” danego pojęcia. Skoro bowiem w atraktorze znajdują się terminy bardzo ogólne, oddalanie się od niego powinno nas przybliżać do konkretów. Czy tak jest w rzeczywistości?…
Wygląda na to, że faktycznie coś w tym jest. Poniżej przedstawiam tendencyjne wyniki sprawdzenia pewnej liczby bynajmniej wcale nie losowo wybranych haseł. i obliczenia odległości każdej z nich od Czarnej Dziury Abstrakcji:

blog 14 Internet 7 programming 8
compiler 6 Turing machine 5 Angry Birds 18
Sun 4 velociraptor 2 Boeing 747 22
South Park 28 Darth Vader 8 Julius Caesar 34

Nie jestem wprawdzie do końca pewien, co oznacza to, że Angry Birds są niemal dwa razy bardziej abstrakcyjne od Juliusza Cezara, ale na pewno musi to coś znaczyć ;]

Tags: , , ,
Author: Xion, posted under Culture, Internet, Programming » 3 comments

Czytelne wywołania funkcji

2011-05-23 21:22

Ktokolwiek, kto programował dłużej w Windows API zna bardzo dobrze klasyczną sekwencję instrukcji, składającą się na zarejestrowanie nowej klasy okna i jego utworzenie. Jej częścią jest między innymi wywołanie funkcji CreateWindow lub CreateWindowEx, które przyjmują odpowiednio 11 lub 12 parametrów. Mimo że nie są one rekordzistkami pod tym względem (bije je chociażby CreateFont z 14 argumentami), to i tak mogą się “poszczycić” dużym potencjałem w zaciemniania kodu i czynienia go trudnym w zrozumieniu lub modyfikacji.
Niestety, takie lub nieco mniej drastyczne przypadki można spotkać w wielu językach, platformach i technologiach. Odchodzą one daleko od rozsądnego zalecenia, aby liczba parametrów funkcji nie przekraczała dwóch lub trzech, z ewentualnym uwzględnieniem this/self/Me. Jak sobie z nimi radzić, aby wynikowy kod zawierający tak rozrośnięte wywołania był jeszcze w jakikolwiek sposób czytelny?…

Otóż należy postarać się, aby każdy z wielu argumentów był identyfikowalny czymś więcej niż tylko pozycją w ciągu oddzielonym przecinkami. Dobrze tutaj sprawdza się feature niektórych języków programowania zwany argumentami słownikowymi. Umożliwia on “przypisywanie” w wywołaniu wartości parametrów do ich nazw. Pozwala to na zmianę ich kolejności, ale przede wszystkim dodaje czytelną etykietę dla każdego argumentu. Przykład takiego słownikowego wywołania w Pythonie widać poniżej:

  1. # posortowanie listy łańcuchów od najdłuższego
  2. sorted(strings, key = len, reverse = True)

Teoretycznie podobny efekt można osiągnąć także w językach nieposiadających wspomnianej opcji. Podejrzewam zresztą, że sposób ten jest pierwszym, jaki większości przyszedł do głowy. Chodzi tu o zwyczajne opatrzenie każdego argumentu odpowiednim komentarzem. Wiele przykładów tak właśnie traktuje argumenty wspomnianej funkcji CreateWindow(Ex):

  1. hWindow = CreateWindowEx(NULL,                   // rozszerzony styl
  2.                           windowClass.c_str(), // klasa okna
  3.                           "My Window",       // tekst na p. tytułu
  4.                           WS_OVERLAPPEDWINDOW,   // styl okna
  5.                           20,         // współrzędna X
  6.                           20,         // współrzędna Y
  7.                           600,         // szerokość
  8.                           500,         // wysokość
  9.                           NULL,                  // okno nadrzędne
  10.                           NULL,                  // menu
  11.                           hInstance,             // instancja aplikacji
  12.                           NULL);                 // dodatkowe dane

Ale rzeczywisty kod to nie przykład z tutoriala, a nadmiar kolorowych komentarzy niekoniecznie musi dobrze wpływać na przejrzystość całej instrukcji. W dodatku wciąż jesteśmy skazani na domyślną kolejność parametrów, a wszelkie rozbieżności między argumentami a ich opisem (bardzo mylące!) nie są wykrywane przez kompilator…

Co można zatem zrobić? Odpowiedź jest prosta: należy napisać kod, który sam się dokumentuje ;-) A rozwijając tę myśl do czegoś bardziej konkretnego: powinniśmy zauważyć, że absolutnie każdy język posiada możliwość opisywania nie tylko parametrów funkcji, ale ogóle jakichkolwiek wyrażeń. Nazywa się to… dokładnie tak – deklaracją zmiennych:

  1. const char* windowTitle = "My Window";
  2. POINT pos = { 20, 20 };
  3. SIZE size = { 600, 500 };
  4. DWORD style = WS_OVERLAPPEDWINDOW;
  5. hWindow = CreateWindowEx(NULL, windowClass.c_str(), windowTitle,
  6.        style, pos.x, pos.y, size.cx, size.cy,
  7.        NULL, NULL, hInstance, NULL);

Przy takim rozwiązaniu niepotrzebne są już żadne dodatkowe wyjaśnienia, bo wszystko widać tu doskonale. Wywołanie stało się czytelne, bo każdy z parametrów jest po prostu swoją nazwą lub nieistotnym NULL-em. Warto też zauważyć, że w typowym kodzie wiele z tych nazw byłoby już zdefiniowanych wcześniej, bo np. byłyby argumentami funkcji otaczającej to wszystko. Ilość dodatkowych deklaracji niekoniecznie musiałaby więc być zbliżona do długości listy parametrów wywołania.

Powyżej widać zatem, że nawet z wyjątkowo rozrośniętymi funkcjami można sobie całkiem nieźle poradzić. Nie traktujmy tego jednak jako zachęty do wydłużania list argumentów naszych własnych funkcji. Zdecydowanie lepiej jest użyć struktury (jak to robi się np. przy tworzeniu urządzenia DirevtX) czy nawet wzorca Builder bez jego abstrakcyjnej części.

Tags: , , ,
Author: Xion, posted under Programming » 6 comments

Drugi problem, czyli API do wyrażeń regularnych

2011-05-19 22:54

Napotykając problem, niektórzy ludzie myślą: “Użyję wyrażeń regularnych!”
W rezultacie mają dwa problemy.

Jamie Zawinski @ alt.religion.emacs

Ten słynny cytat jest, według mnie, lekkim niedoszacowaniem. Decydując się na skorzystanie z wyrażeń regularnych, z miejsca dostajemy bowiem dwa problemy z nimi samymi :) Pierwszych z nich jest sama składnia, która dla nieprzyzwyczajonego oka jest cokolwiek nietrywialna. To głównie ona jest wskazywana jako główna trudność w sprawnym i efektywnym używaniu regexów.
Dzisiaj jednak chciałem zwrócić uwagę na ten drugi, rzadziej zauważany problem. Otóż samo wyrażenie to nie wszystko, trzeba je jeszcze odpowiednio użyć w naszym kodzie. I tutaj mogą zacząć się schody, bo w różnych językach programowania sprawa ta wygląda często odmiennie. Na szczęście jest da się tu też wskazać podobieństwa i spróbować dokonać uogólnienia.

Podstawowym elementem interfejsu programistycznego do wyrażeń regularnych jest zwykle obiekt wzorca (pattern), czyli samego wyrażenia. Zawiera on jego postać skompilowaną, którym jest mniej lub bardziej skomplikowana (w zależności od składni) konstrukcja przypominająca automat stanów. Zbudowanie tej wewnętrznej reprezentacji jest konieczne, aby przeprowadzić jakąkolwiek operację (np. wyszukiwania czy dopasowania). Jeśli więc planujemy skorzystać z jednego wyrażenia w celu przetworzenia większej liczby tekstów, dobrze jest posługiwać się gotowym, skompilowanym obiektem.
Ten ogólny opis dobrze przenosi się na rzeczywiste języki programowania, w których możemy znaleźć takie klasy jak:

Tekstową postać wyrażeń regularnych podajemy zwykle do konstruktorów wyżej wymienionych klas, względnie używamy jakichś statycznych lub globalnych funkcji z odpowiednich pakietów. Przy okazji warto też wspomnieć o problemie escape‘owania znaków specjalnych w wyrażeniach, który w mocno niepożądany sposób interferuje z analogicznym mechanizmem w samych językach programowania. Ponieważ w obu przypadkach używa się do tego znaku backslash (\), w wyrażeniach wpisywanych do kodu należy go podwoić:

  1. boost::regex exp("\\w+"); // kompiluje wyrażenie \w+

W C# i Pythonie można tego uniknąć, stosując mechanizm surowych napisów (raw strings). Programiści C++ i Javy nie mają niestety tego szczęścia ;)

Gdy mamy już obiekt skompilowanego wyrażenia, możemy użyć go do jakichś pożytecznych celów. Jeśli są one proste – jak choćby sprawdzenie, czy jakiś ciąg ma formę określoną regeksem – to możemy zazwyczaj obejść się jednym prostym wywołaniem:

  1. IPV4_REGEX = re.compile(r"^([12]?\d{1,2}\.){3}[12]?\d{1,2}$")
  2. def is_ipv4_addr(text):
  3.     return bool(IPV4_REGEX.match(text))

Bardziej skomplikowane jest wyszukiwanie wszystkich dopasowań wyrażenia w danym tekście, zwłaszcza jeśli przy okazji chcemy dobrać się do fragmentów znalezionych podciągów. Tutaj zaczynają objawiać się pewne różnice między poszczególnymi językami, ale ogólny schemat pozostaje ten sam. Opiera się on na skonstruowaniu odpowiedniej pętli przelatującej po kolejnych dopasowaniach i operowaniu na obiekcie, który takie dopasowanie (match) reprezentuje:

Obiekt dopasowania udostępnia zazwyczaj kilka przydatnych metod i właściwości, jak choćby zakres indeksów znalezionego ciągu. Są też tam fragmenty, które “wpadły” w podgrupy strukturalne (subsequences, subgroups, capture groups, itp.), na które nasze wyrażenie było podzielone. Chodzi tu o jego części ujęte w nawiasy okrągłe; to, jakie konkretne znaki zostały dopasowane do każdego z nich zostaje bowiem zapamiętane w obiekcie match.
Między innymi dzięki temu faktowi możliwe jest określanie bardzo ogólnych wzorców do wyszukania w tekście, a następnie przeglądanie tego, co udało nam się znaleźć i podejmowanie decyzji na podstawie jakichś znaczących elementów dopasowania. W ten sposób możemy przetwarzać teksty o stopniu skomplikowania znacznie przekraczającym to, co w teorii daje się opisać wyrażeniami regularnymi. Żeby nie pozostać gołosłownym, zaprezentuję na przykład prosty sposób na konwersję tekstu zawierającego często spotykane na forach znaczniki BBCode (takie jak [url] czy [img]) na jego odpowiednik HTML-owy, gotowy do wyświetlenia.

  1. import re
  2.  
  3. # wyrażenie dopasowujące tagi BBCode, np. [b]foo[/b]
  4. BBTAG_RE = re.compile(r"\[\s*(\w+)\s*\](.*)\[/\s*\1\s*\]")
  5.  
  6. # funkcja zamieniająca pojedynczy tag BBCode na HTML
  7. SIMPLE_BBTAGS = { 'b': 'strong', 'i': 'em', 'u': 'u' }
  8. def _bbtag_to_html(match):
  9.     tag = match.group(1).lower()
  10.     content = match.group(2)
  11.      
  12.     if tag in SIMPLE_BBTAGS.keys():
  13.         html_tag = SIMPLE_BBTAGS[tag]
  14.         return "<%s>%s</%s>" % (html_tag, content, html_tag)
  15.     if tag == 'url':
  16.         return '<a href="%s">%s</a>' % (content, content)
  17.     if tag == 'img':
  18.         return '<img src="%s" alt="">' % content
  19.      
  20.     return ""
  21.  
  22. # właściwa funkcja
  23. def bbcode_to_html(text):
  24.     return BBTAG_RE.sub(_bbtag_to_html, text)

Najważniejsza jego część to wykonywane w funkcji _bbtag_to_html przetwarzanie obiektu typu re.MatchObject zawierającego dane o znalezionym, pojedynczym tagu. Pobieramy tam jego nazwę i zawartość, które zostały dopasowane jako odpowiednio: pierwsza i druga podgrupa wyrażenia. Samo przeglądanie tekstu w poszukiwaniu tagów i ich zastępowanie jest wykonywane wbudowaną funkcją re.RegexObject.sub, która ukrywa szczegóły wspomnianej wcześniej pętli.

Mam nadzieję, że powyższy przykład dowodzi, że możliwe jest zastosowanie wyrażeń regularnych bez znaczącego wzrostu liczby problemów do rozwiązania :) Jakkolwiek dziwnie to zabrzmi, korzystanie z regeksów może bowiem niekiedy przyczynić się do wzrostu czytelności wynikowego kodu, przynajmniej dla bardziej doświadczonych programistów. Jest tak ze względu na duże podobieństwa nie tylko między różnymi wariantami składni wyrażeń, ale też między bibliotekami do ich obsługi w różnych językach programowania, które to dzisiaj starałem się przedstawić.

Tags: , , , , ,
Author: Xion, posted under Programming » 2 comments

Indeksowanie w Pythonie

2011-05-15 16:34

Przeglądając jakiś rzeczywisty kod w języku Python można często natknąć się na nietypowe wykorzystanie operatora nawiasów kwadratowych. Tradycyjnie znaki te służą do indeksowania tablic, co w językach kompilowanych bezpośrednio do kodu maszynowego równa się prostej operacji na wskaźnikach:

  1. int tab[N];
  2. // ...
  3. assert( tab[i] == *(tab + i) );

Ponieważ jednak Python nie jest takim językiem, jego twórcy pozwolili sobie na to, by zawarte w nim kilogramy warstw abstrakcji oferowały dodatkową funkcjonalność również przy tak trywialnym zagadnieniu. W rezultacie indeksowanie tablic (a właściwie list, w tym i łańcuchów znaków) jest tu operacją, która często ukrywa w sobie znacznie bardziej skomplikowaną logikę niż to widać na pierwszy rzut oka.

Zacznijmy od tego, że w dopuszczalnymi indeksami są nie tylko dodatnie, ale i ujemne liczby całkowite. Oznaczają one dostęp do końcowych elementów tablicy: -1 do pierwszego od końca, -2 do drugiego, i tak dalej. Być może nie wydaje się logiczne to, że elementy tab[0] i tab[-1] są na przeciwnych krańcach listy podczas gdy ich indeksy różnią zaledwie o jeden. Uzasadnieniem jest tu odniesienie do indeksowania od końca w innych językach, czyli tab[tab.length() - i]. W Pythonie po prostu pomija się jawne zapisanie odwołania do długości tablicy.

Znacznie bardziej interesującym aspektem indeksowania jest użycie dwukropka (:). W zasadzie to zamienia on wówczas całą operację na “krojenie” (slice) tablicy, bo pozwala on na na wybór nie jednego elementu, a całego przedziału. Dokładniej mówiąc tab[i:j] oznacza fragment tablicy wyznaczony półotwartym zakresem indeksów [i; j). Kawałek ten zawiera więc tab[i], ale pomija tab[j]; jest to analogiczne chociażby do iteratorów begin() i end() w kontenerach STL.
To właśnie slicing jest tą nietypową operacją, która dla niewprawnego oka wygląda cokolwiek zagadkowo. Jest tak zwłaszcza wtedy, gdy wykorzystuje ona możliwość pominięcia jednego z krańców przedziału, który to jest wówczas “dociągany” do odpowiedniego krańca całej listy.

Łącząc wszystkie te zawiłości możemy już rozszyfrować większość często występujących przypadków użycia indeksowania w Pythonie:

  1. tab[1:] # tablica bez pierwszego elementu
  2. tab[:-1] # tablica bez ostatniego elementu
  3. tab[:n] # co najwyżej n początkowych elementów tablicy
  4. tab[-n:] # n > 0 końcowych elementów tablicy
  5.  
  6. # początkowy ciąg aż do wystąpienia znaku @
  7. username = email[:email.index('@')]
  8.  
  9. # końcowy ciąg począwszy od ostatniej kropki
  10. extension = filename[filename.rindex('.'):]

Dwa ostatnie przykłady pokazują też, że tego rodzaju operacje są bardzo przydatne podczas przetwarzania łańcuchów znaków, które to “przypadkiem” są również swego rodzaju tablicami.

Tags: , , ,
Author: Xion, posted under Programming » 6 comments

Sprawcie sobie piłeczkę

2011-05-06 19:23

Wśród niezbędnych gadżetów programistycznych często wymieniany jest kubek kawy. Trzeba jednak przyznać, że zazwyczaj chodzi tutaj o jego zawartość: szybko wypijaną i pospiesznie uzupełnianą. Nie twierdzę oczywiście, że pusty kubek nosi jakiekolwiek znamiona przydatności podczas kodowania. Stanowi on jednak przykład na to, że na biurku programisty typowo znajdzie się zawsze coś więcej niż tylko klawiatura, myszka i ekran.


Mój egzemplarz pochodzi ze
spotkania HTML5 w Chrome Web Store

Może być tam chociażby mała, kauczukowa piłeczka. Model standardowy jest zwykle w jednolitym kolorze, ma średnicę około sześciu centymetrów i oznaczony jest mniej lub bardziej znajomym logo, wskazującym na jego pochodzenie. To oczywiście nie jest przypadek, ponieważ najprostszym sposobem na wejście w posiadanie tego wyrobu gumowego jest wzięcie udziału w spotkaniu, wykładzie, konferencji czy innego rodzaju evencie, gdzie takie fanty rozdawanego są darmo.

Po co jednak koder miałby trzymać pod ręką coś takiego? Otóż dlatego, że – jak zdołałem stwierdzić – piłeczka taka ma całe mnóstwo zastosowań bezpośrednio związanych z programowaniem. Mówiąc bardziej zrozumiałym językiem, jej funkcjonalność jest niezwykle bogata, gdyż obsługuje ona szeroki wachlarz różnych przypadków użycia. Oto niektóre z nich:

  • Piłeczkę można podrzucać. To pożyteczne zajęcie trenuje refleks i koordynację ruchową, a także pozwala w pożyteczny sposób zabić czas oczekiwania na ukończenie jakiejś dłuższej operacji. Krótko mówiąc, piłeczka jest narzędziem wspomagającym kompilację. Istnieje dowiedziona (przez amerykańskich naukowców) zależność między wielkością i stopniem skomplikowania projektów, nad którymi pracuje programista, a jego umiejętnościami żonglowania.
  • Piłeczkę można ściskać. Wspomaga to koncentrację i pozwala na łatwiejsze skupienie się podczas poszukiwania rozwiązań problemów, na jakie natrafiamy w trakcie kodowania. Nie bez znaczenia jest też fakt, że ćwiczenie to działa pozytywnie w profilaktyce RSI.
  • Piłeczką można rzucać o ścianę. Jest to rzecz jasna metoda wspomagająca debugowanie, której adekwatność jest skorelowana z czasem poświęconym na szukanie błędu. Oprócz absorbowania negatywnej energii piłeczka odbijająca się od ściany pozwala też na chwilowe oderwanie się od przeczesywania kodu w poszukiwaniu usterki, co z kolei często umożliwia uświadomienie sobie pomyłki, która legła u jej podstaw.
  • Piłeczka toczy się i spada z biurka. Ta jej cecha to idealny przykład pozornego buga, pod którym tak naprawdę kryje się użyteczny feature. Jeśli bowiem piłeczka spadnie nam z biurka i potoczy się w odległe miejsce, konieczne będzie doprowadzenie jej do porządku. Tego nie da się raczej zrobić zdalnie, więc będzie musieli wstać z krzesła i zrobić kilka kroków, a to zawsze jest pożądane.

Widzimy zatem, że kauczukowa piłeczka posiada niezaprzeczalne zalety i jest wybitnie użytecznym narzędziem programistycznym. Jeśli więc będziemy mieli okazję wejścia w jego posiadanie, zalecam skorzystanie z niej – zwłaszcza, że rzecz jest bardzo często rozprowadzana jako freeware :)

Tags: , ,
Author: Xion, posted under Programming » 9 comments

Funkcja join()

2011-04-29 21:24

Będąc w zgodzie z podzielanym przez siebie poglądem o kluczowej a często niedocenianej roli “małych” algorytmów, dzisiaj wezmę pod lupę funkcję do łączenia napisów, znaną większości jako join. Ta przydatna operacja występuje w wielu językach i bibliotekach, a jej brak w pozostałych jest zwykle wyraźnie odczuwalny (tak, Java, o tobie mówię). Dobrze użyty join – zwłaszcza w połączeniu z pewnymi specyficznymi mechanizmami językowi – potrafi zapobiec pisaniu niepotrzebnych pętli, znacząco redukując code bloat.

Ale po kolei. join to operacja polegająca na złączeniu kolekcji napisów w jeden, odpowiednio sklejony łańcuch. Łączenie polega na tym, że w wyniku pomiędzy elementami kolekcji wstawiony jest pewien określony ciąg (“klej”). Najlepiej widać to na przykładzie:

  1. array = ["Ala", "ma", "kota"]
  2. text = str.join(" ", array)
  3. assert text == "Ala ma kota"

Łatwo zauważyć, że join jest w gruncie rzeczy przeciwieństwem funkcji split, którą nieprzypadkowo kiedyś już opisywałem :)

W czym przejawia się przydatność tej operacji? Przede wszystkim rozwiązuje ona “problem ostatniego przecinka” przy wypisywaniu list. Tradycyjnie obchodzi się go mniej więcej tak:
for (int i = 0; i < (int)strings.length(); ++i) { std::cout << strings[i]; if (i + 1 < (int)strings.length()) std::cout << ", "; }[/cpp] Instrukcja if w tej pętli nie jest oczywiście szczytem elegancji. Gdybyśmy mieli tu funkcję join wszystko byłoby o wiele czytelniejsze:
std::cout << join(", ", strings);[/cpp] Drugą zaletą joina jest jego dobra współpraca z modnymi ostatnio, funkcyjnymi rozszerzeniami wielu języków, pozwalająca w zwięzły sposób przetwarzać kolekcje obiektów. Jeśli na przykład mamy słownik (tudzież mapę/hash), to zamiana go na tekstowy odpowiednik klucz=wartość jest prosta:

  1. import os
  2. def join_dict(d):
  3.      # os.linesep to separator wierszy właściwy dla systemu
  4.     return str.join(os.linesep, map(lambda item: "%s=%s" % item, d.items()))
  5.  
  6. data = { "fullscreen": 1, "width": 800, "height": 600 }
  7. print join_dict(data)
  8. # fullscreen=1
  9. # width=800
  10. # height=600

Oczywiście jest tak wówczas, gdy na widok słowa kluczowego lambda nie uciekamy z krzykiem ;-)

Na koniec tej krótkiej pogadanki wypadałoby jeszcze zaprezentować przykładową implementację omawianej funkcji. Ponieważ – jak napomknąłem wcześniej – doskwierał mi ostatnio jej brak w Javie, więc kod będzie w tym właśnie języku:

  1. public static String join(final Collection<?> s, final String delimiter) {
  2.  
  3.     final StringBuilder builder = new StringBuilder();
  4.     final Iterator<?> iter = s.iterator();
  5.     while (iter.hasNext()) {
  6.         builder.append(iter.next());
  7.         if (!iter.hasNext()) break;
  8.         builder.append(delimiter);
  9.     }
  10.     return builder.toString();
  11. }

Z dokładnością do szczegółów generycznych kolekcji i operacji na stringach, powyższą implementację powinno się dać łatwo przetłumaczyć także na C++.

Tags: , , , ,
Author: Xion, posted under Programming » 7 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.