Skip to content

Commit 5c8e3e0

Browse files
committed
add lesson №11
1 parent 4fb2870 commit 5c8e3e0

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

otus-11/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## Clojure Developer. Урок 11

otus-11/project.clj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(defproject otus-11 "0.1.0-SNAPSHOT"
2+
:description "FIXME: write description"
3+
:url "http://example.com/FIXME"
4+
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
5+
:url "https://www.eclipse.org/legal/epl-2.0/"}
6+
:dependencies [[org.clojure/clojure "1.10.3"]]
7+
8+
:repl-options {:init-ns otus-11.core})

otus-11/src/otus_11/core.clj

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
(ns otus-11.core)
2+
3+
;; * Sequence abstraction
4+
5+
(first nil)
6+
(first [1 2 3])
7+
(first '(1 2 3))
8+
(first {:a 1 :b 2})
9+
10+
(next [1 2 nil 3])
11+
(rest (rest [1 2 nil 3]))
12+
13+
;; * Ленивость
14+
15+
(defn geom [start mul stop]
16+
(lazy-seq
17+
(when (< start stop)
18+
(println (str "=> " start))
19+
(cons start
20+
(geom (* start mul) mul stop)))))
21+
22+
;; ленивые последовательности, выраженные рекурсивно,
23+
;; не растят стек:
24+
25+
(comment
26+
(letfn [(ones [] (lazy-seq (cons 1 (ones))))]
27+
(reduce + (take 1000000 (ones))))
28+
;; => 1000000
29+
)
30+
31+
;; * Бесконечные последовательности
32+
33+
(defn geom-inf [start mul]
34+
(lazy-seq
35+
(cons start
36+
(geom-inf (* start mul) mul))))
37+
38+
(def three-nums
39+
(lazy-cat
40+
'(1 2)
41+
(do (println "calculating third num")
42+
[3])))
43+
44+
(comment
45+
(print (take 2 three-nums)) ;; не напечатает сообщение
46+
(print three-nums) ;; напечатает
47+
(print three-nums) ;; не напечатает из-за кеширования
48+
)
49+
50+
;; * Трансдьюсеры
51+
;; ** reducers
52+
53+
(defn conj-even
54+
;; инициализация аккумулятора
55+
([] [])
56+
;; финализация аккумулятора
57+
([acc] acc)
58+
;; одиночный "шаг", получающий новое значение аккумулятора
59+
;; из старого значения аккумулятора и значения текущего элемента
60+
;; сворачиваемой последовательности
61+
([acc v]
62+
(if (even? v)
63+
(conj acc v)
64+
acc)))
65+
66+
(comment
67+
(reduce conj-even [] (range 10))
68+
;; => [0 2 4 6 8]
69+
)
70+
71+
;; ** transduce
72+
73+
(comment
74+
;; transduce не требует указания начального значения аккумулятора,
75+
;; вместо этого вызывает (conj-even) перед началом сворачивания
76+
;; и (conj-even acc) в конце сворачивания
77+
(transduce identity conj-even (range 10))
78+
;; => [0 2 4 6 8]
79+
)
80+
81+
(transduce (filter even?) conj (range 10))
82+
83+
(defn conj-tr
84+
"Работает как conj, но выводит свои аргументы при вызове."
85+
([]
86+
(println "(conj-tr) ;; init")
87+
[])
88+
([acc]
89+
(println (str "(conj-tr " acc ") ;; finalize"))
90+
acc)
91+
([acc v]
92+
(println (str "(conj-tr " acc " " v ") ;; step"))
93+
(conj acc v)))
94+
95+
(comment
96+
(transduce identity conj-tr (range 5))
97+
;; (conj-tr) ;; init
98+
;; (conj-tr [] 0) ;; step
99+
;; (conj-tr [0] 1) ;; step
100+
;; (conj-tr [0 1] 2) ;; step
101+
;; (conj-tr [0 1 2] 3) ;; step
102+
;; (conj-tr [0 1 2 3] 4) ;; step
103+
;; (conj-tr [0 1 2 3 4]) ;; finalize
104+
)
105+
106+
(defn avg
107+
"Сворачивает последовательность чисел в среднее значение."
108+
([] [0 0])
109+
([[s c]] (float (/ s c)))
110+
([[s c] v] [(+ s v) (inc c)]))
111+
112+
(comment
113+
(transduce identity avg [2 5])
114+
;; => 4.5
115+
)
116+
117+
(defn xmap [f]
118+
"Упрощённый аналог map, работающий только как трасдьюсер."
119+
(fn [reducer]
120+
(fn
121+
([]
122+
(reducer))
123+
([acc]
124+
(reducer acc))
125+
([acc v]
126+
(reducer acc (f v))))))
127+
128+
(defn xfilter [f]
129+
"Упрощённый аналог filter, работающий только как трасдьюсер."
130+
(fn [reducer]
131+
(fn
132+
([] (reducer))
133+
([acc] (reducer acc))
134+
([acc v]
135+
(if (f v)
136+
(reducer acc v)
137+
(reducer acc))
138+
))))
139+
140+
(comment
141+
(= (transduce
142+
(comp (xfilter odd?)
143+
(xmap str))
144+
conj
145+
(range 10))
146+
147+
(transduce
148+
;; это уже встроенные map и filter, работающие в режиме
149+
;; трансдьюсеров
150+
(comp (filter odd?)
151+
(map str))
152+
conj
153+
(range 10)))
154+
;; => true
155+
)
156+
157+
;; ** отладка
158+
159+
(defn trace [prefix]
160+
(fn [reducer]
161+
(fn [& args]
162+
(println (str prefix ": " args))
163+
(apply reducer args))))
164+
165+
(comment
166+
(transduce
167+
(comp (filter odd?)
168+
(map str)
169+
(trace "trace"))
170+
conj
171+
(range 10))
172+
;; trace: ([] "1")
173+
;; trace: (["1"] "3")
174+
;; trace: (["1" "3"] "5")
175+
;; trace: (["1" "3" "5"] "7")
176+
;; trace: (["1" "3" "5" "7"] "9")
177+
;; trace: (["1" "3" "5" "7" "9"])
178+
;; заметьте, что инициализация аккумулятора с помощью трансдьюсеров
179+
;; не производилась — не было вызова без аргументов
180+
181+
(transduce
182+
(comp (filter odd?)
183+
(map str))
184+
((trace "trace") conj)
185+
(range 10))
186+
;; trace: ;; <- вот он, вызов без аргументов
187+
;; trace: ([] "1")
188+
;; trace: (["1"] "3")
189+
;; trace: (["1" "3"] "5")
190+
;; trace: (["1" "3" "5"] "7")
191+
;; trace: (["1" "3" "5" "7"] "9")
192+
;; trace: (["1" "3" "5" "7" "9"])
193+
;; редьюсер был вызван без аргументов для инициализации аккумулятора
194+
)
195+
196+
;; ** into
197+
198+
(comment
199+
(into '() (map inc) (range 5))
200+
;; => (5 4 3 2 1)
201+
(into [] (map inc) (range 10))
202+
;; => [1 2 3 4 5]
203+
)
204+
205+
;; ** sequence
206+
207+
(comment
208+
(let [touch (fn [rf]
209+
(fn
210+
([acc] (rf acc))
211+
([acc v]
212+
(println "step")
213+
(rf acc v))))
214+
s (take 3 (sequence touch (range 1000)))]
215+
(println (count s))
216+
(println "--")
217+
(println (count s))
218+
)
219+
;; step
220+
;; step
221+
;; ...
222+
;; step ;; итого 32 раза
223+
;; => [3 3]
224+
;; при вычислении второго count последовательность заново не вычислялась,
225+
;; так что некоторая ленивость пристуствует.
226+
;; Но и вычислены были 32 элемента, а не 3 и не 1000 — sequence
227+
;; обрабатывает элементы порциями!
228+
)

0 commit comments

Comments
 (0)