2011-05-11 62 views
2

我有一對夫婦的有關以下代碼的問題:Clojure的代理問題 - 使用歡送

(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. 在動畫功能 - 爲什麼在歡送動畫之前使用#'
  2. 爲什麼send-off在動畫函數開始工作?它不應該只是再次開始動畫功能,並且從不執​​行重繪和睡眠方法?
  3. 是否有任何缺點,相比原來的,以書面形式動畫功能:

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

回答

8

在動畫函數中 - 爲什麼在動畫發送之前使用了#'?

展現Clojure的動態本質。

表格#'animation是一個Var,Clojure的可變引用類型之一。 defn宏創建一個Var。爲方便起見,調用引用函數的Var與調用函數本身相同。但是與功能不同,Var可以改變!我們可以在Clojure REPL中重新定義#'animation並立即查看效果。

使用(send-off *agent* #'animation)迫使Clojure每次查找當前值的#'animation Var。如果代碼使用了(send-off *agent* animation),那麼Clojure只會查找該值一次,並且不可能在不停止循環的情況下更改動畫功能。

+1

感謝您的解釋。實際上是否需要在這裏使用#'動畫Var?如果不是,爲什麼要麻煩?如果是的話,爲什麼呢?當一個函數不純/需要隱藏狀態/有副作用時需要嗎? – limist 2012-09-28 19:37:37

3

1.這是一個有點不清楚我很好,但似乎是由設計決定豐富。如果您注意到:

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

如果一個var位於函數/宏位置,它最終會解析爲該函數或宏。

2.要理解的重要一點是代理模型。代理人可以被認爲是一個工作在一個可變單元上的工作人員。該代理有一個工作隊列(一個函數隊列)。發送並將添加工作發送到該隊列。由於發送只是將工作添加到隊列中,因此它立即返回。由於代理只能連續執行函數,所以在執行下一個動畫之前,必須完成第一個動畫調用。因此,無論首先發送還是最後發送,您都可以實現基本相同的目標。

3.兩者之間應該沒有明顯的差異。