"The Grid. A digital frontier. I tried to picture clusters of information as they move through the computer. What did they look like? Ships? Motorcycles? Were the circuits like freeways? I kept dreaming of a world I thought I’d never see. And then, one day, I got in." — Tron: Legacy

2012-11-22

Clojure Elementary 4


"If I had a nickel for every time I've written "for (i = 0; i < N; i++)" in C I'd be a millionaire."

- Mike Vanier



Po części trzeciej, czas na ostatnią, czwartą część łamigłówek o podstawowym stopniu trudności. W kolejnej serii przejdę do stopnia trudności określanego jako Easy.


Taka mała zagadka: Ilu programistów .NET potrzeba, by zabić karalucha?
(odpowiedź na końcu wpisu.).

Problem 27: for the win


Zadanie polega na odgadnięciu wyniku działania makra for.
Makro to bierze za pierwszy argument wektor powiązań (podobnie jak let i opcjonalnie trzy modyfikatory:
- :let [ powiązania dla dodatkowych zmiennych pomocniczych ]
- :when ( test ) - zmienne w iteracji zostaną przekazane po spełnieniu testu
- :while ( test ) - for zakończy działanie po negatywnym wyniku testu

Powiązania (nie dotyczy :let) muszą być sekwencjami np.: (for [x '(3 4) z [1 2]] (...) ). Elementy sekwencji będą łączone na zasadzie każdy z każdym. To jest dla każdego elementu x zostanie przypisany każdy element z i taka postać przekazana jako parametry do bloku wykonania. Blok wykonania zwraca wartość, z której po każdej iteracji będzie budowana sekwencja. Tak, że for służy do budowania sekwencji na podstawie innych sekwencji.

W teście T1 mam for, które będzie się wykonywać dla każdego x z sekwencji zbudowanej przy pomocy funkcji range). W tym przypadku range zwróci kolekcję liczb od 0 do 39 włącznie z krokiem 1. Iterując po każdym elemencie zostaje sprawdzony warunek :when, który zwraca prawdę jeżeli reszta z dzielenia elementu x kolekcji przez 4 (funkcja rem będzie równa 1. Dzięki temu zostanie zbudowana kolekcja elementów, których reszta z dzielenia przez 4 jest 1. Wynik w ostateczności powinien wyglądać tak: (1 5 9 13 17 21 25 29 33 37).

W teście T2 x jest nieskończoną kolekcją zwróconą przez funkcję iterate. Funkcja ta bierze za pierwszy argument funkcję, a za drugi wartość początkową. Funkcja anonimowa zwraca wartość argumentu powiększonego o 4. Stąd x będzie mieć postać: (0 4 8 12 ... ). Modyfikator :let tworzy zmienną pomocniczą z, która będzie mieć wartość elementu x powiększonego o 1. Modyfikator :while sprawdza, czy z jest mniejsze od 40, jeżeli nie, to kończy działanie for

W teście T3 for iteruje po parach liczb utworzonych poprzez utworzenie kolekcji liczb
(0 1 2 3 ... 19) podzielonej na kolekcję dwuelementowych list : ( (0 1) (2 3) (4 5) .... (18 19) ).
Wynikiem jest lista, której elementy to sumy par liczb.
Pary i większe kolekcje liczb można przypisywać do zmiennych lokalnych w rózny sposób, np:

(let [ x 1 y 2 ] (+ x y))
i
(let [ [x y] '(1 2) ] (+ x y))
vs
(let [ x (first '(1 2)) y (second '(1 2)) ] (+ x y))
I zadanie:

T1: (= __ (for [x (range 40)
            :when (= 1 (rem x 4))]
        x))
T2: (= __ (for [x (iterate #(+ 4 %) 0)
            :let [z (inc x)]
            :while (< z 40)]
        z))
T3: (= __ (for [[x y] (partition 2 (range 20))]
        (+ x y)))

E1: '(1 5 9 13 17 21 25 29 33 37).
E2: [1 5 9 13 17 21 25 29 33 37].

Problem 28: Logical falsity and truth


W Clojure w testach logicznych tylko nil i false zwracają logiczny fałsz, wszystko inne ewaluuje się do logicznej prawdy. Funkcja if Służy do wykonywania dwóch rodzajów kodu w zależności od wyniku przekazanego do niej testu. Za pierwszy argument bierze test, który zwraca prawdę lub fałsz. Jeżeli wynikiem testu będzie prawda, zostanie wykonany kod podany w drugim argumencie w przeciwnym wypadku zostanie wykonany kod w trzecim. Jeżeli trzeciego argumentu nie będzie, a test sugeruje na jego wykonanie, to if zwróci nil.

T1: (= __ (if-not false 1 0))
T2: (= __ (if-not nil 1 0))
T3: (= __ (if true 1 0))
T4: (= __ (if [] 1 0))
T5: (= __ (if [0] 1 0))
T6: (= __ (if 0 1 0))
T7: (= __ (if 1 1 0))
  
E1: 1

Problem 29: Map defaults


W przypadku pozyskiwania wartości rezydującej pod kluczem mapy, oprócz sposobu jaki był przedstawiony w problemie nr 26 jest jeszcze jedna metoda na poradzenie sobie z sytuacją w której dany klucz nie istnieje. Można do wyrażenia pobierającego wartość spod klucza dodać wartość domyślną, która zostanie zwrócona, gdy klucz nie istnieje, np.:

(:k {:a 0, :b 1, :c nil} :not_exist)
Zwróci :not_exist.

A co jeżeli chcemy utworzyć mapę i nadać kluczom jakaś domyślną wartość?
Zadanie polega na utworzeniu funkcji, która pobierze sekwencję kluczy, wartość domyślną i utworzy z nich mapę. Mapę w najprostszy sposób tworzy się poprzez (hash-map klucz dane) lub {klucz dane}. Wiadomo, że przez elementy sekwencji można iterować przez użycie map lub for. Funkcja map bierze za parametr funkcję, którą stosuje dla każdego elementu sekwencji. Stąd mając domyślą wartość klucza i sekwencję kluczy można zwrócić sekwencję map. Złączyć to można poleceniem conj i funkcją apply lub reduce.

Rozpiszę główne etapy:
1. map ... : ({:a 0} {:b 0} {:c 0})
2. apply conj ... : {:a 0 :b 0 :c 0}
z czego apply tworzy listę w sposób: (conj {:a 0} {:b 0} {:c 0})
reduce: (conj {:a 0} {:b 0}) -> (conj {:a 0 :b 0} {:c 0}) ...

Zadanie:

(= (__ 0 [:a :b :c]) {:a 0 :b 0 :c 0})
(= (__ "x" [1 2 3]) {1 "x" 2 "x" 3 "x"})
(= (__ [:a :b] [:foo :bar]) {:foo [:a :b] :bar [:a :b]})

E1: #(reduce conj (map (fn [d] {d %1}) %2))
E2: #(apply conj (map (fn [d] {d %1}) %2))
------------------------------------------------------------------------------------------------------------
Odpowiedź na zagadkę: Dwóch. Jeden trzyma karalucha, by nie uciekł, a drugi instaluje na nim Windows.

Brak komentarzy:

Prześlij komentarz