开发者

Clojure Agent question - using send-off

I have a couple of questions about the following code:

(import 
 '(java.awt Color Graphics Dimension)
 '(java.awt.image BufferedImage)
 '(javax.swing JPanel JFrame))

(def width 900)
(def height 600) 

(defn render
 [g]
 (let [img (new BufferedImage width height 
                 (. BufferedImage TYPE_INT_ARGB))
       bg (. img (getGraphics))]
   (doto bg
      (.setColor (. Color white))
      (.fillRect 0 0 (. img (getWidth)) (. img (getHeight)))
      (.setColor (. Color red))
      (.drawOval 200 200 (rand-int 100) (rand-int 50)))
   (. g (drawImage img 0 0 nil))
   (. bg (dispose))
   ))

(def panel (doto (proxy [JPanel] []
                        (paint [g] (render g)))
             (.setPreferredSize (new Dimension 
                                     width 
                                     height))))

(def frame (doto (new JFrame) (.add panel) .pack .show))

(def animator (agent nil))


(defn animation 
  [x]
  (send-off *agent* #'animation)
  (. panel (repaint))
  (. Thread (sleep 100)))

(send-off animator animation)
  1. In the animation function - why is #' use开发者_运维知识库d before animation in send-off?
  2. Why does send-off at the start of animation function work? Shouldn't it just go the start of animation function again and never execute the repaint and sleep methods?
  3. Is there any disadvantage, as compared to the original, in writing the animation function as:

    (defn animation 
      [x]
      (. panel (repaint))
      (. Thread (sleep 100))
      (send-off *agent* animation))
    


In the animation function - why is #' used before animation in send-off?

To demonstrate Clojure's dynamic nature.

The form #'animation is a Var, one of Clojure's mutable reference types. The defn macro creates a Var. For convenience, invoking a Var which refers to a function is the same as invoking the function itself. But a Var, unlike a function, can change! We could redefine #'animation at the Clojure REPL and immediately see the effects.

Using (send-off *agent* #'animation) forces Clojure to look up of the current value of the #'animation Var every time. If the code had used (send-off *agent* animation) instead, then Clojure would look up the value only once, and it would not be possible to change the animation function without stopping the loop.


1. This is a little unclear to me as well but seems to be a design decision by Rich. If you notice:

user=> (defn x [y] (+ y 2))
#'user/x
user=> ((var x) 3)
5

If a var is in the function/macro location, it will eventually resolve to the function or macro.

2. One important thing to understand here is the agent model. Agents can be thought of as a worker that operates on a single mutable cell. There is a queue of work (a queue of functions) for that agent to do. send-off and send add work to that queue. Since send-off is only adding work to the queue, it immediately returns. Since the agent only executes the functions serially, the first animation call must finish before executing the next one. Therefore, you achieve basically the same thing regardless of putting send-off first or last.

3. There should be no noticeable difference between the two.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜