0×0018 – better programmer

I’m a geek. I truely love programming and constantly try to improve my skills. Whenever someone asks me to describe myself in three words I always say: ambitious, hard-working and honest. Recently I wondered what I should do to become a better programmer. Firstly I thought that great programmers are fluent in programming languages they use, they know well many frameworks etc. Then I realized that it’s essential to know all your tools well, but you don’t become the great programmer only by learning all tricks in C or Hibernate. Programming languages, libraries and frameworks change so rapidly that everything that you know today might be useless in five years from now. The great programmer should be adaptive and able to learn quickly. There are many techniques which might help to learn more efficiently but it’s not the most important issue in this post.

In my opinion being a great programmer is not about coding – it’s about solving problems. You always have to solve problems no matter which programming language or operating system you use. Great programmers are not paid for writting code – they are paid for finding solutions of nontrival problems. This is why I decided to improve my problem solving skills and start solving TopCoder problems. Yeah… it’s easy to say but much more difficult to put over when you work as a Junior JEE Developer, write your MSc. thesis, organize wedding and study (currently I attend to four courses at my university). Being honest when you have so much duties as I have it’s difficult even to find time to rest or meet with your friends. But wait! The lack of time shouldn’t be a reason to yield! I think that it’s better to solve simple problems everyday than much more complicated once a month. This is why I decided to expend up to ten minutes everyday to solve one Div 2 250 problem. It might seem stupid or boring to most of you but as I said: it’s better to do something simple regularly than to do nothing. My goal is to solve as many 250 points problems as possible until my graduation. We will see whether I will succeed in my decision ;)

0×0017 – new / old domain

I decided to change my blog domain to: www.adamsznajder.pl. The old adress will be valid only for a few next days.

0×0016 – process substitution

I really hate creating temporary files. I sometimes forget to delete then after usage, it’s always a lot of typing when using them etc. This is why when I heard the first time about the process substitution I decided to use it whenever it’s possible. Suprisingly many people don’t know this mechanism so I decided to write a short post about it.

Let’s assume that you want to find the differences between two outputs:

Bash
grep -rl "search1" . > /tmp/output1
grep -rl "search2" . > /tmp/output2
diff /tmp/output1 /tmp/output2
rm /tmp/output1
rm /tmp/output2

Huh, that’s a lot of writing, isn’t it? Let’s try to do the same with process substitution:

Bash
diff <(grep -rl "search1" .) <(grep -rl "search2" .)

That was much easier :) But what did really happen? The shell created two separate grep processes and forwarded their output to temporary pipes (created one per process). Diff didn’t recevie as a parameter grep outputs but pipes’ paths and used them as the data source. There is also a simple way to ensure about that:

Bash
echo <(grep -rl "search" .)
cat <(grep -rl "search" .)

As a result of the first command you should get the pipe’s path. The second command should merely print out the grep result (pipe’s content). Using process substitution is great but has one big disadvantage: you can use that files only once. When the pipe is empty there is nothing to read so you cannot use it mutiple times.

0×0015 – wyrażenia lambda

Funkcje lambda

Funkcje lambda często określa się również mianem anonimowych. Jest to związane z faktem, że nie mają one przypisanej nazwy i wykorzystywane są najczęściej jednokrotnie w kodzie. Eliminuje to potrzebę wymyślania nazw dla funkcji pomocniczych. Działają one dokładnie w taki sam sposób jak zwykłe funkcje. Oprócz braku nazwy różnią się jednak kilkoma ograniczeniami. Nie jest bowiem możliwe np. wykorzystywanie w nich strażników. Co ważniejsze jednak możliwe jest w nich zdefiniowanie tylko jednego wzorca dopasowań. Oznacza to, że przekazując argumenty do nich musimy być pewni, że nie dojdzie do tragedii. Funkcje lambda tworzone są z wykorzystaniem operatora \. Zaraz po nim podawane są argumenty funkcji. Część właściwa funkcji nie znajduje się w przeciwieństwie do normalnych funkcji za znakiem =, lecz za sekwencją ->. Zaprezentowane powyżej funkcje mają takie samo zadanie: znaleźć nieparzyste elementy w liście oraz zamienić je na parzyste poprzez odjęcie liczby 1.

Haskell
-- plik
-- przykład wykorzystujący funkcję pomocniczą
changeOdd xs =  map makeNotOdd xs
                where makeNotOdd x = if (odd x) then
                                    x-1
                                  else x
-- przykład wykorzystujący funkcję lambda 
changeOdd2 xs = map (\x -> if (odd x) then x-1 else x) xs
--interpreter
> changeOdd [1..5]
[0,2,2,4,4]
> changeOdd2 [1..5]
[0,2,2,4,4]
> changeOdd []
[]
> changeOdd2 []
[]

Co ciekawe funkcje lambda mają zarówno zwolenników jak i przeciwników. Jest to związane bezpośrednio z czytelnością tworzonego kodu. Zdecydowanie łatwiej jest bowiem domyśleć się co robi kod napisany w tradycyjny sposób niż z wykorzystaniem funkcji lambda (widać to z resztą na wydruku). Czytelność kodu wpływa nie tylko na łatwość jego zrozumienia przez współpracowników, ale również zdecydowanie zwiększa łatwość debugowania aplikacji. Mnie funkcje lambda jarają, ale używajcie sobie ich jak wolicie ;)

Częściowe dopasowanie funkcji

Istnieje jeszcze jedna dość ciekawa właściwość Haskella. Chodzi tu o tzw. częściowe dopasowanie funkcji (ang. partial function application). Możliwe jest bowiem definiowanie funkcji stworzonych poprzez częściowe zdefiniowanie zachowania już istniejących. Spójrzmy na poniższy przykład:

Haskell
-- plik
multiplyMap = map (*2)

Czytając kod od lewej do prawej mamy w głowie następujący obraz sytuacji:

  • Tworzymy funkcję multiplyMap
  • Funkcja nie przyjmuje argumentów
  • Wywołujemy w jej ramach funkcję map, która pomnoży każdy element listy przez dwa..

Hola, hola! Jakiej listy? W końcu multiplyMap nie przyjmuje argumentów! Ten map też wygląda na pierwszy rzut oka nieprawidłowo. Jak wiemy funkcja ta przyjmuje dwa argumenty, a tu mamy tylko jeden. Na tym właśnie polegają czary częsciowego dopasowania funkcji. multiplyMap jest tak jakby “nakładką” na istniejącą funkcję map. Nie jest nią jednak dosłownie ze względu na różnice typów. Stwierdzenie to zostało wykorzystane jedynie do lepszego zobrazowania sobie co powyższy kod robi. Funkcja multiplyMap przyjmuje w każdym razie jeden argument i bezpośrednio przekazuje go do predefiniowanej funkcji. Spójrzmy na próbę wykorzystania powższego kodu:

Haskell
-- interpreter
> multiplyMap
 
<interactive>:42:1:
    No instance for (Show ([Integer] -> [Integer]))
      arising from a use of `print'
    Possible fix:
      add an instance declaration for (Show ([Integer] -> [Integer]))
    In a stmt of an interactive GHCi command: print it
> :type multiplyMap 
-- Uwaga: przyjmujemy listę i zwracamy listę
multiplyMap :: [Integer] -> [Integer]
> :type map
-- Uwaga: przyjmujemy funkcję jednoargumentową oraz listę i zwracamy listę 
map :: (a -> b) -> [a] -> [b]
> multiplyMap [1..5]
[2,4,6,8,10]
> map (*2) [1..5]
[2,4,6,8,10]

0×0014 – trochę filozoficznie

Dziś będzie trochę mniej technicznie. Można wręcz powiedzieć, że filozoficznie ze sporą domieszką psychologii. Myślę, że moje (i nie tylko moje) przemyślenia nadają się do umilenia porannej kawy, podróży komunikacją miejską albo posiłku. O czym dziś będę pisał? Trochę o sobie, trochę o innych osobach zainteresowanych programowaniem, a może i ogólnie naukami ścisłymi. Wydaje mi się bowiem, że osoby poświęcające swój czas nowym technologiom wyrabiają z czasem u siebie pewne postawy oraz nawyki, których nie są do końca świadomi. Dotyczy to większości osób. Sprawia to, że geekiem człowiek staje się w pewnym stopniu hmm… niechcący. Od razu zaznaczam, że słowem geek nie nazywam stereotypowych introwertycznych gości we flanelowych koszulach i wielkich okularach. Chodzi mi raczej o osoby “ogarnięte życiowo”, które pasjonują się tym co robią i starają się realizować swoje zadania najlepiej jak potrafią. Słowo to jest pozbawione jakiegokolwiek pejoratywnego wydźwięku.

Weźmy pierwszą rzecz, która mi się nasuwa na myśl: godziny pracy. Zastanów się kiedy najchętniej pracujesz. Kiedy najlepiej Ci się uczy albo pisze kod? Mnie osobiście między 21, a 2 w nocy. Współdomowników doprowadza to czasem do szału, ale wtedy na serio świetnie mi się pracuje. Dlaczego? Nie mam zielonego pojęcia. Zjawisku temu zostało poświęcone wiele artykułów w serwisach branżowych. Wymieniają one kilka czynników. Jednym z nich jest cisza w domu i tu zgadzam się z tą teorią w 100%. Wszyscy śpią, mogę się skupić i zająć swoją robotą. Nikt nie pierze, nie ogląda telewizji, nie rozmawia, nie łazi za moimi plecami. Jestem tylko ja i moje taski do wykonania. No dobra, ale przecież cicho jest też, gdy wszyscy są w pracy/szkole – po bożemu między 8-16. Komentatorzy twierdzą, że wpływ na to ma oświetlenie. W nocy monitor oraz lampka pełnią jedyne źródło światła w pomieszczeniu. Dzięki temu nic nie rozprasza: ani to co się dzieje za oknem, ani sterta papierów na biurku. Ja bym do tego dodał jeszcze jeden aspekt: po 23 zaczyna spadać aktywność w Sieci. Nikogo nie korci już by spojrzeć na ulubione forum, na fejsa, albo innego czorta. Po prostu prawie nic tam się już nie dzieje. Poza tym na ramieniu siedzi znany z kreskówek aniołek i szepcze: “miałeś to dziś zrobić, a dzień już się kończy, daruj sobie fejsa”. True story :)

Inna sprawa (ale powiązana w pewien sposób): zastanawialiście się kiedyś, dlaczego programując stwierdzenie “kładę się/zrobię za 10 min” oznacza często co najmniej godzinę? Wydaje mi się, że pisząc oprogramowanie lubimy odznaczać taski jako wykonane. Nikt nie znosi przerywać pisania kodu w środku realizowania jakiegoś elementu aplikacji. Dlaczego? Oderwanie się od komputera w trakcie rozwiązywania problemu oznacza, że pójdziemy spać ze świadomością, że następnego dnia mamy coś do zrobienia. To smutne żegnać dzień planując robotę już na następny, nie? Co jednak ważniejsze w trakcie pracy nad jakimś zagadnieniem mamy wszystko poukładane w głowie: co zrobić, jak zrobić, na co uważać. Przypomina to trochę pracę artysty malarza, który widzi oczami wyobraźni swój obraz. Przerwanie malowania i zajęcie się czymś innym oznacza, że wizja ta ulatnia się z głowy. Identycznie jest z programowaniem. Nikt zatem dobrowolnie nie przerwie rozwiązywania problemu, który jest już rozłożony na czynniki pierwsze. Oderwanie się od pracy oznaczałoby bowiem konieczność przeanalizowania wszystkiego w głowie ponownie. Znów musielibyśmy się wdrożyć w problem. No tak, ale dlaczego od razu nie powiemy: “idę spać za godzinę”. Myślę, że jest to równie często związane z chęcią “spławienia” rozmówcy jak i z przecenieniem swoich możliwości. Gdy napiszemy dany fragment kodu to okazuje się, że gdzieś popełniliśmy błąd. Szukanie tego błędu zajmuje czasem dużo czasu i w ten oto sposób 10 min staje się godziną.

Wydaje mi się, że chęć skupienia się tylko i wyłącznie na rozwiązywanym problemie wpływa w pewnym stopniu też na interakcje z innymi ludźmi. Wyobraźmy sobie sytuację: pracujemy nad interesującym zagadnieniem, a tu nagle dryyyyn dryyyyn – telefon. Co robimy jako osoba zajęta czymś absorbującym? Oczywiście odbieramy, ale często niechętnie. Zaobserwowałem trzy główne sposoby przeprowadzania rozmowy:

  • Udajemy, że słuchamy co mówi rozmówca zgodnie z zasadą: “jednym uchem wlatuje, a drugim wylatuje”. Stosujemy przy tym pomruki i przytakiwania wskazujące, że zgadzamy się ze słowami rozmówcy i zrobimy co sobie tylko zażyczy (z przyznaniem się do tego, że nie słuchamy i oddaniem się za karę w niewolę na wieczne zmywanie naczyń włącznie).
  • Staramy się jak najszybciej załatwić sprawę i zminimalizować ilość słów wypowiadaną przez obie strony (doprowadzając drugą stronę czasami do złości stwierdzeniem: “muszę kończyć, bo jestem zajęty”).
  • Odchodzimy od komputera, dajemy odpocząć oczom oraz innym częściom ciała i idziemy zrobić sobie np. herbatę skupiając się na rozmowie.

Każdy się pewnie przekonał, że pierwsze dwa podejścia mogą mieć przykre konsekwencje. W związku z powyższym osobiście radzę stosować zawsze ostatnią strategię w myśl zasady: “ludzie przed pracą”. Fakt faktem znów wracamy do tego, że w nocy telefonów nie ma => lepiej się pracuje.

Kolejna sprawa: chyba nie pomylę się, gdy powiem, że żaden programista nie lubi zajmować się rzeczami, które nie stanowią dla niego wyzwania. Wymigiwanie się od nierozwijających zajęć to praktyka stosunkowo powszechna. Kto z Was lubił na studiach pisać testy jednostkowe? A dokumentację? Co z wykliwaniem diagramów w UML? Jeżeli nie ma chętnego na podjęcie się taska to realizuje się go co prawda niechętnie, ale mimo wszystko poprawnie. Innym dobrym przykładem jest instalowanie znajomym i rodzinie systemu operacyjnego (w 99,99% przypadków od pewnej firmy na M). Osobiście lubię pomagać i robię to na serio chętnie. Uwierzcie, że nie jestem aspołeczny i kocham swoich najbliższych oraz pomagać im w miarę swoich możliwości. Usłyszałem gdzieś jednak stwierdzenie, że kazać programiście instalować Windowsa to jak architektowi nosić cegły na budowie. No cóż… Trudno się z tym nie zgodzić. Co więc robi w takim przypadku typowy geek? Organizuje sobie pracę w taki sposób, aby coś robić ciekawego, a system instalował tak jakby przy okazji.

Ciekawość. Uch, to jest coś co na serio wyróżnia osobę, która pracuje tylko dla kasy od programisty pasjonata. Nie chodzi mi tu bynajmniej o ciekawość w stylu: z kim Kasia Cichopek była na bankiecie lub kto wygra kolejną edycję Tańca z gwiazdami. Osobiście nie rozróżniam Kingi Rusin od Nataszy Urbańskiej. Serio. Nie wydaje mi się, aby ta wiedza była mi do czegokolwiek potrzebna. Chodzę do teatru, do kina, czytam dużo książek, uwielbiam słuchać muzyki. Interesuje mnie jednak to co jest moim zdaniem najważniejsze, czyli treść i forma. Chcę miło spędzić czas z najbliższymi, rozerwać się i odchamić. Interesowanie się kim jest aktor X, z kim sypia, jakim autem jeździ uważam za stratę czasu. Ten czas można przecież wykorzystać do poznania tylu fascynujących rzeczy! Każdy geek ma na serio dziesiątki jak nie setki zagadnień, którymi chciałby się kiedyś zająć. Geek nie zna takiego pojęcia jak nuda. Jeżeli ma chwilę czasu, bliscy są zajęci, nie jest zmęczony, a inne zajęcia nie wchodzą w grę (w moim przypadku sport albo oglądanie sportu) to w większości przypadków zacznie zgłębiać tajniki czegoś z tej listy. Z ciekawością związany jest jeszcze jeden problem. Geeka cholernie łatwo czymś zainteresować. Zwłaszcza jeżeli wymaga to logicznego myślenia i ma chwilę czasu. Ciekawy oznacza w słowniku tych osób coś niebanalnego i wymagającego zastanowienia się. Daj takiemu geekowi jakiś problem algorytmiczny, powiedz mu dlaczego warto go rozwiązać i masz gościa z głowy na kilka dni.

Język. Geek ma już tak wszystkie klepki poprzestawiane, że wszystko mu się kojarzy z technologiami. Kilka przykładów:

  • Drzewo. Dla geeka pierwsza myśl to struktura danych, dla normalych ludzi roślinka.
  • Węzeł. Dla geeka element grafu, dla normalnych ludzi odpowiednio poprzelatany sznur.
  • Graf. Dla geeka struktura danych, dla normalnych ludzi malunek na ścianie.
  • Wskaźnik. Dla geeka zmienna przechowująca adres innej zmiennej w pamięci, dla normalnych ludzi coś wykorzystywanego przy prezentowaniu.
  • Bramka. Dla geeka logiczna, dla normalnego człowieka miejsce gdzie trzeba kopnąć piłkę.
  • Pamięć. Dla geeka w komputerze, dla normalnego człowieka w głowie.
  • Ada. Dla geeka język programowania, dla normalnego człowieka imię kobiety.
  • Sieć. Dla geeka komputerowa, dla normalnego człowieka narzędzie do łapania ryb.

Mógłbym wymieniać tak i wymieniać. Przykładów jest na serio ogrom. Czy to coś złego? Wydaje mi się, że nie jeżeli nie utrudnia to kontaktów z innymi ludźmi. Siedzimy w technologii po uszy, ale jak długo stringi kojarzą Ci się z kobiecymi majtkami, a nie zbiorem łańcuchów znakowych, to wszystko z Tobą chłopie w porządku :)

Wiem, że sporo wymienionych tu spostrzeżeń dotyczy osób także niezainteresowanych nowymi technologiami. Wydaje mi się jednak, że większość powyższych cech dotyczy geeków w jakiś szczególny sposób. Myślę, że warto zdawać sobię sprawę, że czasami takie zachowanie może być niezrozumiałe dla naszych bliskich. Ludzie ci kochają nas m.in. za to co nas od nich różni. Należy jednak pamiętać, że mają też ograniczone rezerwy cierpliwości i tolerancji. Fakt, że żona pozwala Ci lutować w dopiero co posprzątanym pomieszczeniu nie oznacza, że można przekładać pasje nad wyjście z nią i poświęcanie jej uwagi. “Ludzie przed pracą”.