"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 2




Jest wieczorek i jest chwilka czasu, więc kontynuuję to, co zacząłem w poprzednim wpisie.

 Problem 9: Intro to Maps


Mapa (inaczej zwana hash-map) to słownikowa struktura danych, która przechowuje dwa uporządkowane rodzaje danych, to jest: klucz, który posiada unikalną wartość w całej mapie i odpowiadającą mu wartość. Zarówno klucz jak i przypisane do niego dane mogą być dowolnego typu. Mapa jest strukturą nieuporządkowaną i nie zachowuje kolejności par klucz-dane. Podobnie jak w strukturze Set. Mapę można traktować jako funkcję, która bierze za parametr klucz i zwraca wartość przypisaną do klucza. Jeżeli użyję klucza Clojure (słowo zaczynające się od dwukropka) jako funkcji i podam mu mapę jako parametr, wyrażenie zwróci wartość pod kluczem w mapie. Jeżeli klucz nie istnieje wyrażenie w obu przypadkach zwraca nil. Mapę można odróżnić po okrągłych nawiasach klamrowych.
Zadanie polega na odgadnięciu wyniku podania mapie klucza jako parametr i kluczowi mapy.
T1: (= __ ((hash-map :a 10, :b 20, :c 30) :b))
T2: (= __ (:b {:a 10, :b 20, :c 30}))
E1: 20
E2: ({1 20} 1)
E3: (:k {:k 20})

Problem 10: Maps: conj


Działanie funkcji conj na zbiorze typu mapa. W przypadku mapy funkcja conj bierze za pierwszy argument mapę, a kolejne argumenty dane ułożone w pary, np. dwuelementowy wektor lub mapę. Nie da się zapodać Seta jako parametr ze względu na niewiadomą kolejność jego elementów.

T1: (= {:a 1, :b 2, :c 3} (conj {:a 1} __ [:c 3]))
E1: {:b 2}
E2: [:b 2]
E3: (hash-map :b 2)

Problem 11: Intro to Sequences


Sekwencja w Clojure to uogólniona struktura danych, która może być listą, wektorem lub mapą. W związku z tym, że Clojure to Lisp wymagane jest by można było się poruszać po elementach struktur danych z użyciem funkcji first, last, second itd, które operują na listach. Chodzi o to by nie rozdrabniać się i tworzyć różnych funkcji dla wektorów, list i map.

Zadanie polega na odgadnięciu wyniku działania poszczególnych funkcji testowych. Funkcja first zwraca pierwszy element ciągu, second drugi, last ostatni.

T1: (= __ (first '(3 2 1)))
T2: (= __ (second [2 3 4]))
T3: (= __ (last (list 1 2 3)))

E1: 3
E2: (first '(3))
E3: (last '(3))
E4: (second (first (list '(1 3) 2)))

Problem 12: Sequences: rest


Zadanie polega na odgadnięciu wyniku działania funkcji rest. Funkcja rest zwraca wszystkie elementy ciągu poza pierwszym

T1: (= __ (rest [10 20 30 40]))

E1: [20 30 40]
E2: '(20 30 40)

Problem 13: Intro to functions


W Clojure istnieje wiele sposobów by zdefiniować funkcję:

1. Za pomocą funkcji fn. Dzięki niej tworzymy funkcje anonimowe. Nazwa za fn to nazwa funkcji. Nie daje ona nic oprócz tego, że łatwiej rozpoznać miejsce błędu w przypadku gdy program się posypie.

2. Inny sposób tworzenia funkcji anonimowych: za pomocą znaku number. np.: #(+ % 1). % - jest parametrem przekazanym do funkcji. Jeżeli do tej funkcji przekazywane jest więcej niż jeden argument, to można użyć znaku % z numerem. np. : %1 , %2 itd..

3. Funcja partial służy do uzupełniania parametrów funkcji, która jest jej pierwszym argumentem. W ten sposób można sobie skrócić kod, gdy wartość jednego z argumentów funkcji jest znana. W tym: T4 wypadku przekazywany jest dodatkowy argument do funkcji '+'.

Aby zapisać funkcję pod daną nazwą można użyć makra def do którego przekazujemy nazwę i funkcję anonimową, lub uproszczonego defn.

Tym razem trzeba zgadnąć wynik jaki ma pojawić się po przekazaniu parametru do funkcji anonimowej.

T1: (= __ ((fn add-five [x] (+ x 5)) 3))
T2: (= __ ((fn [x] (+ x 5)) 3))
T3: (= __ (#(+ % 5) 3))
T4: (= __ ((partial + 5) 3))

E1: 8

Problem 14: Hello World


Czas na Hello World. Można zapytać. czemu tak późno? Temu, że to specjalny Hello world. W tym miejscu powoli kończą się nasze zgadywanki, a zaczyna praca.
Zadanie polega na utworzeniu funkcji anonimowej, która weźmie za parametr podany ciąg znaków i doda do niego Hello z !. Jako, że z poprzedniego zadania wiemy już jak tworzyć funkcje anonimowe zadanie jest trywialne. ;) .

T1: (= (__ "Dave") "Hello, Dave!")
T2: (= (__ "Jenn") "Hello, Jenn!")
T3: (= (__ "Rhea") "Hello, Rhea!")

E1: #(str "Hello, " % "!")
E2: (fn [arg] (str "Hello," arg "!"))
E3: (fn hello-hi [arg] (str "Hello," arg "!"))
E4: #((partial str "Hello, ") % "!")

Problem 15: Double Down


Podwójny daun. Zadanie polega na zgadnięciu postaci funkcji, która w magiczny sposób podwoi wartość jej argumentu.

T1: (= (__ 2) 4)
T2: (= (__ 3) 6)
T3: (= (__ 11) 22)
T4: (= (__ 7) 14)

E1: #(* % 2)
E2: (fn [d] (* d 2))

Problem 16: Sequences: map


Kolejna zgadywanka. Uwaga, by nie mylić funkcji map z mapą (hash-map). Funkcja map służy do zwracania leniwej kolekcji (leniwej, bo wartość jej elementów liczona jest dopiero gdy są potrzebne - o tym później). Funkcja ta bierze za argument funkcję i minimum jedną niepustą kolekcję elementów (wektor, lista, mapa), na których kolumnami, po kolei wykonuje podaną funkcję. Przykładowo:
(map + [1 2 3]) daje: [1 2 3], bo (+ 1) daje 1
(map + [1 2 3] [4 5 6]) daje : [5 7 9], bo (+ 1 4) to 5, (+ 2 5) to 7...

Działanie jest wykonywane kolumnami. Jako, że funkcja + może wziąć dowolną liczbę parametrów można do map dawać dowolną ilość kolekcji. Jeżeli kolekcje mają różne długości. Map wykona działanie na ilości elementów równej długości najkrótszej kolekcji.
Może narysuję o co chodzi:

(map + '(1 2 3) '(4 5 6) '(1 1 1)) to:
         
         1 | 2 | 3
         4 | 5 | 6
         1 | 1 | 1
       + ---------
         6 | 8 |10

Wynik:
         '(6 8 10)

(map + '(1 2) '(4 5 6) '(1 1 1)) to:
         
         1 | 2
         4 | 5
         1 | 1
       + -----
         6 | 8

Wynik:
       '(6 8) - 2 elementy, bo najkrótsza lista miała długość 2

(map #(+ % 5) '(1 2 3) '(4)) to:
ArityException Wrong number of args (2) passed to: core$eval1442$fn
  clojure.lang.AFn.throwArity (AFn.java:437)

Dlaczego błąd? Bo w przeciwieństwie do funkcji + funkcja anonimowa
#(+ % 5) przyjmuje tylko jeden parametr, a wiemy, że map przekazuje
parametry kolumnami. W tym przypadku map wysłał do funkcji 1 i 4

Aby wyrażenie było prawidłowe należy uwzględnić drugi parametr
w funkcji anonimowej: #(+ %1 %2 5)
(map #(+ %1 %2 5) '(1 2 3) '(4)) to: (10), bo 1 + 4 + 5 = 10
- reguła o obcinaniu list wciąż obowiązuje.

I zadanie:

T1: (= __ (map #(+ % 5) '(1 2 3)))

E1: '(6 7 8)
E2: [6 7 8]

Brak komentarzy:

Prześlij komentarz