Skip to content

Commit 6817247

Browse files
committed
Lesson 15 has been added
1 parent 927d7c7 commit 6817247

File tree

6 files changed

+531
-0
lines changed

6 files changed

+531
-0
lines changed

otus-15/project.clj

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(defproject otus-15 "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.11.1"]]
7+
:main ^:skip-aot otus-15.atom-watcher-validator
8+
:target-path "target/%s"
9+
:profiles {:uberjar {:aot :all
10+
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
(ns otus-15.atom-watcher-validator
2+
(:gen-class))
3+
4+
;;
5+
;; Атомы
6+
;;
7+
8+
(def oleg
9+
(atom {:name "Oleg"
10+
:age 25
11+
:wears-glasses? false}))
12+
; => #'user/oleg
13+
14+
@oleg
15+
16+
(swap! oleg #(update % :age + 3))
17+
; => {:age 28, :wears-glasses? false, :name "Oleg"}
18+
19+
(swap! oleg update :age + 3)
20+
; => {:age 31, :wears-glasses? false, :name "Oleg"}
21+
22+
@oleg
23+
24+
(reset! oleg {:name "Nikita"})
25+
; => {:name "Nikita"}
26+
27+
(def resources-by-user
28+
(atom {1005 {:cpu 35
29+
:store 63466734
30+
:memory 10442856
31+
:pids #{6266, 5426, 6542}}}))
32+
33+
(swap! resources-by-user update-in [1005 :pids] conj 9999)
34+
35+
@resources-by-user
36+
(deref resources-by-user)
37+
38+
(comment
39+
;; Альтернативные варианты записи:
40+
41+
;; 1)
42+
(fn [atom-value]
43+
(update-in atom-value [1005 :pids] conj 9999))
44+
45+
;; 2)
46+
(fn [atom-value]
47+
(let [pids (get-in atom-value [1005 :pids])
48+
new-pids (conj pids 9999)
49+
new-atom-avalue (assoc-in atom-value [1005 :pids] new-pids)]
50+
new-atom-avalue))
51+
)
52+
53+
;; Семантика «сравнить и изменить» и повторы
54+
55+
(def accumulator
56+
(atom 0))
57+
58+
(defn +slow
59+
[a b timeout]
60+
(println (format "current: %s, delta: %s, timeout: %s" a b timeout))
61+
(Thread/sleep timeout)
62+
(+ a b))
63+
64+
(defn retry-example
65+
[]
66+
(future (swap! accumulator +slow 1 2000))
67+
(future (swap! accumulator +slow 2 5000)))
68+
69+
(retry-example)
70+
71+
@accumulator
72+
73+
(comment
74+
"current: 0, delta: 1, timeout: 2000"
75+
"current: 0, delta: 2, timeout: 5000"
76+
;; -> #<Future@257a94bc: :pending>
77+
"current: 1, delta: 2, timeout: 5000"
78+
)
79+
80+
;;
81+
;; Применение атомов для сохранения результата функции
82+
;;
83+
84+
;; Замыкание атома внутри анонимной функции.
85+
(defn memoize
86+
[f]
87+
(let [mem (atom {})]
88+
(fn [& args]
89+
(if-let [e (find @mem args)]
90+
(val e)
91+
(let [ret (apply f args)]
92+
(swap! mem assoc args ret)
93+
ret)))))
94+
95+
(def +mem (memoize +slow))
96+
97+
(time (+mem 1 2 2000))
98+
;; => Elapsed time: 2001.006041 msecs
99+
(time (+mem 1 2 2000))
100+
;; => Elapsed time: 0.124338 msecs
101+
102+
;;
103+
;; Функции-наблюдатели
104+
;;
105+
106+
;; На момент вызова наблюдателя значение в ref'е уже может быть изменено.
107+
;; Необходимо опираться на аргументы old и new.
108+
(defn echo-watch
109+
[key ref old new]
110+
(println key ref old "=>" new))
111+
112+
(add-watch accumulator :echo echo-watch)
113+
114+
;; В наблюдателя передаётся только конечный результат вызова reset!/swap!.
115+
;; Перезапуски будут проигноированы, т.к. их результат отбрасывается.
116+
(retry-example)
117+
118+
;; Наблюдатель вызвается даже если в результате вызовов
119+
;; reset!/swap! значение в атоме не было изменено.
120+
(reset! accumulator @accumulator)
121+
122+
(defn echo-watch*
123+
[key ref old new]
124+
(when (not= old new)
125+
(println key ref old "=>" new)))
126+
127+
;; Заменяю наблюдателя, зарегистрированного под ключом :echo.
128+
(add-watch accumulator :echo echo-watch*)
129+
130+
(reset! accumulator @accumulator)
131+
132+
;; Удаляю наблюдателя с ключом :echo.
133+
(remove-watch accumulator :echo)
134+
135+
(reset! accumulator (inc @accumulator))
136+
137+
;; Пример логирования при помощи наблюдателей.
138+
139+
(defn log->file
140+
[filename _ref old new]
141+
(when (not= old new)
142+
(spit filename (str new \newline) :append true)))
143+
144+
(def nikita
145+
(atom {:name "Nikita", :age 23}))
146+
147+
;; Наблюдатель, принимающий аргумент через ключ.
148+
(add-watch nikita "program.log" log->file)
149+
150+
(swap! nikita #(update % :age inc))
151+
(swap! nikita update :age inc)
152+
(swap! nikita identity)
153+
(swap! nikita assoc :wears-glasses? true)
154+
(swap! nikita update :age inc)
155+
156+
(def history
157+
(atom ()))
158+
159+
(defn log->atom
160+
[dest-atom key ref old new]
161+
nil)
162+
163+
;; Наблююатель - частично применённая функция.
164+
(add-watch nikita :record (partial log->atom history))
165+
166+
(swap! nikita update :age inc)
167+
(swap! nikita update :age inc)
168+
169+
@history
170+
171+
;;
172+
;; Функции-валидаторы
173+
;;
174+
175+
(def acc
176+
(atom 0 :validator nat-int?))
177+
;; => #'user/acc
178+
179+
(swap! acc dec)
180+
;; => IllegalStateException
181+
182+
;; Ссылка может иметь только один валидатор.
183+
;; Установка нового валидатора подменяет исходный.
184+
(set-validator! acc int?)
185+
;; => nil
186+
187+
(swap! acc dec)
188+
;; => -1
189+
190+
(set-validator! acc (fn [x]
191+
(or (int? x)
192+
(throw (IllegalArgumentException.)))))
193+
;; => nil
194+
195+
(reset! acc "string")
196+
;; => IllegalArgumentException
197+
198+
(set-validator! acc nil)
199+
;; => nil
200+
201+
(reset! acc "string")
202+
;; => "string"

otus-15/src/otus_15/future.clj

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(ns otus-15.future)
2+
3+
;;
4+
;; Механизм future.
5+
;;
6+
7+
(def long-calculation
8+
(future
9+
(Thread/sleep 5000)
10+
:done!))
11+
12+
;; Получение значения с таймаутом.
13+
(deref long-calculation 1000 :impatient!)
14+
15+
;; Получение значения.
16+
(deref long-calculation)
17+
18+
;; Получение значения путём применения синтаксического сахара.
19+
@long-calculation
20+
21+
;; Возвращает true, если значение было вычислено для future,
22+
;; promise, delay или ленивой последовательности.
23+
(realized? long-calculation)
24+
25+
;; Футуры запоминают свои значения, таким образом после их
26+
;; однократного вычисления, они не будут перезапускаться.

otus-15/src/otus_15/ref.clj

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
(ns otus-15.ref)
2+
3+
(defn character
4+
[name & {:as opts}]
5+
(ref (merge {:name name
6+
:items #{}
7+
:health 500}
8+
opts)))
9+
10+
(def smaug
11+
(character "Smaug"
12+
:health 500
13+
:strength 400
14+
:items (set (range 50))))
15+
16+
(def bilbo
17+
(character "Bilbo"
18+
:health 100
19+
:strength 100))
20+
21+
(def gandalf
22+
(character "Gandalf"
23+
:health 75
24+
:mana 750))
25+
26+
(defn loot
27+
[from to]
28+
(dosync
29+
(when-let [item (-> @from :items first)]
30+
(alter to update :items conj item)
31+
(alter from update :items disj item))))
32+
33+
(do
34+
(future (while (loot smaug bilbo)))
35+
(future (while (loot smaug gandalf))))
36+
37+
@smaug
38+
@bilbo
39+
@gandalf
40+
41+
[(-> @bilbo :items count) (-> @gandalf :items count)]
42+
(filter (:items @bilbo) (:items @gandalf))
43+
44+
(defn flawed-loot
45+
[from to]
46+
(dosync
47+
(when-let [item (-> @from :items first)]
48+
(commute to update :items conj item)
49+
(commute from update :items disj item))))
50+
51+
(do
52+
(future (while (flawed-loot smaug bilbo)))
53+
(future (while (flawed-loot smaug gandalf))))
54+
55+
[(-> @bilbo :items count) (-> @gandalf :items count)]
56+
(filter (:items @bilbo) (:items @gandalf))
57+
58+
(defn fixed-loot
59+
[from to]
60+
(dosync
61+
(when-let [item (-> @from :items first)]
62+
(commute to update :items conj item)
63+
(alter from update :items disj item))))
64+
65+
(do
66+
(future (while (fixed-loot smaug bilbo)))
67+
(future (while (fixed-loot smaug gandalf))))
68+
69+
[(-> @bilbo :items count) (-> @gandalf :items count)]
70+
(filter (:items @bilbo) (:items @gandalf))
71+
72+
(defn attack
73+
[aggressor target]
74+
(dosync
75+
(let [damage (* (rand 0.1)
76+
(:strength @aggressor))]
77+
(commute target update :health (fn [target-health]
78+
(max 0 (- target-health damage)))))))
79+
80+
(def alive? (comp pos? :health))
81+
82+
(defn play
83+
[character action target]
84+
(while (and (alive? @character)
85+
(alive? @target)
86+
(action character target))
87+
(Thread/sleep (rand-int 50))))
88+
89+
(do (future (play bilbo attack smaug))
90+
(future (play smaug attack bilbo)))
91+
92+
[(:health @smaug) (:health @bilbo)]
93+
94+
(def daylight (ref 1))
95+
96+
(defn attack-with-light
97+
[aggressor target]
98+
(dosync
99+
(let [damage (* (rand 0.1)
100+
(:strength @aggressor)
101+
@daylight)]
102+
(commute target update :health (fn [target-health]
103+
(max 0 (- target-health damage)))))))
104+
105+
(do (future (play bilbo attack-with-light smaug))
106+
(future (play smaug attack-with-light bilbo)))
107+
108+
[(:health @smaug) (:health @bilbo)]
109+
110+
(defn attack-with-light*
111+
[aggressor target]
112+
(dosync
113+
(let [damage (* (rand 0.1)
114+
(:strength @aggressor)
115+
(ensure daylight))]
116+
(commute target update :health (fn [target-health]
117+
(max 0 (- target-health damage)))))))

otus-15/src/otus_15/ref_task.clj

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(ns otus-15.ref-task)
2+
3+
(def bilbo
4+
(ref {:name "Bilbo", :health 100, :strength 100}))
5+
6+
(def smaug
7+
(ref {:name "Smaug", :health 500, :strength 400}))
8+
9+
(def gandalf
10+
(ref {:name "Gandalf", :health 75, :mana 750}))
11+
12+
(defn heal
13+
[healer target]
14+
(dosync (let [health-delta (* (rand 0.1) (:mana @healer))
15+
mana-delta (max 5 (/ health-delta 5))]
16+
(when (pos? health-delta)
17+
(alter healer update :mana - mana-delta)
18+
(commute target update :health + health-delta)))))
19+
20+
(do (future (while (heal gandalf bilbo)
21+
(Thread/sleep (rand-int 50))))
22+
(future (while (heal gandalf smaug)
23+
(Thread/sleep (rand-int 50)))))
24+
25+
[(:mana @gandalf) (:health @bilbo) (:health @smaug)]

0 commit comments

Comments
 (0)