Clojure: Агенты
Теперь поговорим про агентов. Что их отличает от атомов? Дело в том, что любое изменение состояния атома является синхронным, в то время как состояние агента меняется асинхронно. В остальном агенты похожи на атомы, в них можно добавлять валидации, настраивать обработку ошибок.
Рассмотрим несколько примеров:
(def my-agent (agent 0))
@my-agent ; => 0
(send my-agent inc)
#object[clojure.lang.Agent 0x59c25f30 {:status :ready, :val 1}]
@my-agent ; => 1
(send my-agent dec)
#object[clojure.lang.Agent 0x59c25f30 {:status :ready, :val 1}]
@my-agent ; => 0Как видно из в последнем примере, при попытке уменьшить значение агента на единицу, вернулось состояние агента с неизмененным значением, затем мы извлекли состояние агента вручную, однако в нем уже хранился 0, почему? Как упоминалось выше, изменения к состоянию агента применяются асинхронно, важно помнить эту особенность, при работе с ними. Если же нужно получить значение агента после обновления, то можно использовать функции await и await-for.
Рассмотрим еще пару примеров с обработкой ошибок в агентах:
(def broken-agent (agent 0))
(send broken-agent (fn [_] (throw
(Exception. "Houston we have a problem!"))))
#object[clojure.lang.Agent 0xd76bd8c {:status :ready, :val 0}]
(send broken-agent inc)
Execution error at user/eval2175$fn (REPL:1).
Houston we have a problem!
; Теперь настроим агента так, чтобы при возникновении ошибок процесс изменения не прерывался
(def not-broken-agent (agent 0 :error-mode :continue))
(send not-broken-agent (fn [_] (throw
(Exception. "Houston we have one more problem!!!"))))
#object[clojure.lang.Agent 0x44b27f64 {:status :ready, :val 0}]
(send not-broken-agent inc)
#object[clojure.lang.Agent 0x44b27f64 {:status :ready, :val 0}]
; ошибки не возникло!
@not-broken-agent ; => 1Задание
Реализуйте функцию transit, которая ведет себя так же, как в упражнении с атомами, только с помощью агентов. Функция принимает два агента. Агенты представляют счета в банках и число денег, которое нужно перевести с первого на второй аккаунт, в результате выполнения функции, верните счета в виде вектора (помните, изменения в агентах применяются асинхронно!).
(transit (agent 100) (agent 20) 20)
; => [80 40]
(transit (agent 50) (agent 30) 50)
; => [0 80]Полезное
Clojure: Агенты
Теперь поговорим про агентов. Что их отличает от атомов? Дело в том, что любое изменение состояния атома является синхронным, в то время как состояние агента меняется асинхронно. В остальном агенты похожи на атомы, в них можно добавлять валидации, настраивать обработку ошибок.
Рассмотрим несколько примеров:
(def my-agent (agent 0))
@my-agent ; => 0
(send my-agent inc)
#object[clojure.lang.Agent 0x59c25f30 {:status :ready, :val 1}]
@my-agent ; => 1
(send my-agent dec)
#object[clojure.lang.Agent 0x59c25f30 {:status :ready, :val 1}]
@my-agent ; => 0Как видно из в последнем примере, при попытке уменьшить значение агента на единицу, вернулось состояние агента с неизмененным значением, затем мы извлекли состояние агента вручную, однако в нем уже хранился 0, почему? Как упоминалось выше, изменения к состоянию агента применяются асинхронно, важно помнить эту особенность, при работе с ними. Если же нужно получить значение агента после обновления, то можно использовать функции await и await-for.
Рассмотрим еще пару примеров с обработкой ошибок в агентах:
(def broken-agent (agent 0))
(send broken-agent (fn [_] (throw
(Exception. "Houston we have a problem!"))))
#object[clojure.lang.Agent 0xd76bd8c {:status :ready, :val 0}]
(send broken-agent inc)
Execution error at user/eval2175$fn (REPL:1).
Houston we have a problem!
; Теперь настроим агента так, чтобы при возникновении ошибок процесс изменения не прерывался
(def not-broken-agent (agent 0 :error-mode :continue))
(send not-broken-agent (fn [_] (throw
(Exception. "Houston we have one more problem!!!"))))
#object[clojure.lang.Agent 0x44b27f64 {:status :ready, :val 0}]
(send not-broken-agent inc)
#object[clojure.lang.Agent 0x44b27f64 {:status :ready, :val 0}]
; ошибки не возникло!
@not-broken-agent ; => 1Задание
Реализуйте функцию transit, которая ведет себя так же, как в упражнении с атомами, только с помощью агентов. Функция принимает два агента. Агенты представляют счета в банках и число денег, которое нужно перевести с первого на второй аккаунт, в результате выполнения функции, верните счета в виде вектора (помните, изменения в агентах применяются асинхронно!).
(transit (agent 100) (agent 20) 20)
; => [80 40]
(transit (agent 50) (agent 30) 50)
; => [0 80]Полезное
Ваше упражнение проверяется по этим тестам
(ns agents-test
(:require [test-helper :refer [assert-solution]]
[index :refer [transit]]))
(assert-solution
[[(agent 100) (agent 50) 20] [(agent 10) (agent 100) 10] [(agent 50) (agent 30) 50]]
[[80 70] [0 110] [0 80]]
transit)Решение учителя откроется через:
20:00
