14 września 2010

Alfabet w Clojure - zagadka z kanału @learnclojure

Logo ClojureDzisiaj będzie niezwykle krótko, ale bardzo intensywnie mentalnie. Nauka Clojure sprawia, że coraz bardziej "żałuję", że się za ten język zabrałem - coraz więcej problemów przypominających zagadki algorytmiczne, w których nigdy nie byłem orłem (albo nie odkryłem w sobie powołania), albo wręcz zejście na tematy bardzo abstrakcyjne matematycznie. Możnaby rzec, że programowania "produkcyjnego" zostało niewiele (zresztą nigdy w moim wykonaniu nie było), a "teoretyczne" wyraźnie zwyżkuje.

Od dzisiejszego wieczoru, kanał @learnclojure na twitterze przeszedł pod opiekę autora książki "The Joy of Clojure". Książka niebawem pojawi się w sprzedaży, a dzięki uprzejmości wydawnictwa Manning, mam ją przyjemność czytać od kilku tygodni i szczerze cierpię z powodu nadmiaru informacji, których nie mogę w żaden sposób strawić w tempie, jakim bym sobie życzył - powiedzmy, zamiast tradycyjnego 5 x 45 minut tygodniowo nauki Clojure (jak to miało miejsce za czasów szkolnych z chociażby matematyką), zamarzyło mi się podwoić to. I boli, bo zintensyfikowanie wysiłków przy nauce Clojure (przez m.in. zwiększenie czasu dla niego), wcale nie przekłada się na zwiększenie przyswajania wiedzy o nim w strawnej postaci. Czuję, że zaczynam wykorzystywać te miejsca w moim umyśle, które do tej pory były rezerwowane na pogrywanie sobie w Civilization, albo Traviana :-)

Jeśli ktoś nie wierzy, że nauka języka programowania może boleć i chciałby poczuć klimaty, w których przychodzi zmagać się z "oczywistościami" dla osób, które pojęły już te tajniki (jak ma miejsce często przy rozmowach o Javie w moim przypadku, kiedy to nie mogę zrozumieć, że Java może być i jest dla wielu trudna), niech przyjrzy się poniższej konstrukcji (zaczerpnięte z dzisiejszego statusu na kanale @learnclojure) i odpowie na pytanie: "Co będzie wynikiem wykonania?".
user=> (->> (iterate inc (int \a)) (take 26) (map char))
;...jaki będzie wynik?
user=> (map char (range (int \a) (int \{)))
;...wynik jak wyżej i pytanie to samo
Szacunek dla tego, komu tego typu cudeńka nie sprawiają problemu. Ja, kiedykolwiek widzę podobne konstrukcje, zwijam się z bólu, a głowa eksploduje. Pora trawić.

W ramach uzupełnienia, trochę dokumentacji, która może pomóc w zrozumieniu powyższego.
user=> (doc ->>)
-------------------------
clojure.core/->>
([x form] [x form & more])
Macro
  Threads the expr through the forms. Inserts x as the
  last item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  last item in second form, etc.
nil
user=> (doc iterate)
-------------------------
clojure.core/iterate
([f x])
  Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
nil
user=> (doc inc)
-------------------------
clojure.core/inc
([x])
  Returns a number one greater than num.
nil
user=> (doc int)
-------------------------
clojure.core/int
([x])
  Coerce to int
nil
user=> (doc take)
-------------------------
clojure.core/take
([n coll])
  Returns a lazy sequence of the first n items in coll, or all items if
  there are fewer than n.
nil
user=> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
  Returns a lazy sequence consisting of the result of applying f to the
  set of first items of each coll, followed by applying f to the set
  of second items in each coll, until any one of the colls is
  exhausted.  Any remaining items in other colls are ignored. Function
  f should accept number-of-colls arguments.
nil
user=> (doc char)
-------------------------
clojure.core/char
([x])
  Coerce to char
nil
user=> (doc range)
-------------------------
clojure.core/range
([] [end] [start end] [start end step])
  Returns a lazy seq of nums from start (inclusive) to end
  (exclusive), by step, where start defaults to 0, step to 1, and end
  to infinity.
nil

4 komentarze:

  1. Odpowiem enigmatycznie: wynikiem w obu przypadkach będzie to samo, co wynikiem

    (seq "abcdefghijklmnopqrstuvwxyz")

    ;-)

    OdpowiedzUsuń
  2. Osobiście, przy pierwszym spojrzeniu, czytelniejszy jest dla mnie drugi zapis (z map) - uzyskanie literału znakowego dla odpowiednich liczb. Zrozumienie makra ->> przychodzi z czasem...

    OdpowiedzUsuń
  3. Też myślałem, że ->> jest trudne, ale kiedy wiadomo, że wcześniejsze jest drugim parametrem aktualnego, to w zasadzie sprawa się upraszcza. Mimo wszystko zawsze pozostaje pytanie, czy to tylko uproszczenie ala DSL, a ostatecznie będzie tak samo szybko jak w Javie z 200 liniami (specjalnie wyolbrzymiłem z tymi liniami) i gdzie się ta wiedza może przydać. Wciaż borykam się z tym problemem.

    OdpowiedzUsuń
  4. Jedną z zalet już sam wymieniłeś. Po co spędzać czas nad konstrukcją pętli i if-ów, skoro można ten sam problem zapisać w sposób szybki i zgrabny za pomocą programowania funkcyjnego i makr. Przynajmniej takie jest moje zdanie.

    OdpowiedzUsuń