2010-05-08 59 views
11

在幾個週末探索Clojure之後,我想出了這個計劃。它允許您在窗口中移動一個小矩形。下面的代碼:改善我的第一個Clojure計劃

(import java.awt.Color) 
(import java.awt.Dimension) 
(import java.awt.event.KeyListener) 
(import javax.swing.JFrame) 
(import javax.swing.JPanel) 

(def x (ref 0)) 
(def y (ref 0)) 

(def panel 
    (proxy [JPanel KeyListener] [] 
    (getPreferredSize [] (Dimension. 100 100)) 
    (keyPressed [e] 
     (let [keyCode (.getKeyCode e)] 
     (if (== 37 keyCode) (dosync (alter x dec)) 
     (if (== 38 keyCode) (dosync (alter y dec)) 
     (if (== 39 keyCode) (dosync (alter x inc)) 
     (if (== 40 keyCode) (dosync (alter y inc)) 
          (println keyCode))))))) 
    (keyReleased [e]) 
    (keyTyped [e]))) 

(doto panel 
    (.setFocusable true) 
    (.addKeyListener panel)) 

(def frame (JFrame. "Test")) 
(doto frame 
    (.add panel) 
    (.pack) 
    (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) 
    (.setVisible true)) 

(defn drawRectangle [p] 
    (doto (.getGraphics p) 
    (.setColor (java.awt.Color/WHITE)) 
    (.fillRect 0 0 100 100) 
    (.setColor (java.awt.Color/BLUE)) 
    (.fillRect (* 10 (deref x)) (* 10 (deref y)) 10 10))) 

(loop [] 
    (drawRectangle panel) 
    (Thread/sleep 10) 
    (recur)) 

儘管是一個經驗豐富的C++程序員,我發現它非常具有挑戰性的在使用一種完全不同的風格比我用語言編寫甚至一個簡單的應用程序。

最重要的是,這段代碼可能很糟糕。我懷疑各種價值觀的全球性是件壞事。我也不清楚在這裏使用x和y值的引用是否合適。

歡迎提供改進此代碼的任何提示。

+2

我正在學習Clojure的了。感謝問題和工作代碼示例。 – mcotton 2010-05-08 04:00:52

+0

@mcotton,很高興你覺得它有幫助。也許這些筆記也很有用:http://www.reddit.com/r/programming/comments/c16rr/clojure_notes/ – StackedCrooked 2010-05-08 14:26:30

+0

我喜歡這個程序!當在leiningen下運行時,我偶爾會得到'線程異常AWT-EventQueue-0'java.lang.IllegalArgumentException:沒有匹配的子句:157',無論是通過lein run還是通過lein uberjar運行它。我不知道這個錯誤來自哪裏。 – 2013-03-24 14:36:26

回答

12

if s在keyPressed可以被替換爲單個case。另外,dosync可以向外移動以包裹case。實際上,alter也可以移出,這樣如果你決定將其更改爲commute,只有一個地方可以進行更改。其結果是:

(def panel 
    (proxy [JPanel KeyListener] [] 
    (getPreferredSize [] (Dimension. 100 100)) 
    (keyPressed [e] 
     (let [keyCode (.getKeyCode e)] 
     (dosync 
     (apply alter 
      (case keyCode 
      37 [x dec] 
      38 [y dec] 
      39 [x inc] 
      40 [y inc]))) 
     (println keyCode))) 
    (keyReleased [e]) 
    (keyTyped [e]))) 

你也可以重寫進口更簡潔:

(import [java.awt Color Dimension event.ActionListener]) 
(import [javax.swing JFrame JPanel]) 

- 你是否願意到是風格問題。

我會將drawRectangle重命名爲draw-rectangle(這是Clojure中函數名的慣用風格),更重要的是,將它重寫爲接受座標作爲顯式參數的純函數。然後你可以寫一個小的包裝來使用你的參考,如果你的設計確實會受益於參考。 (很難不知道你怎麼可能希望使用&對其進行修改等說吧)

不想while(loop [] ... (recur))(見(doc while)(clojure.contrib.repl-utils/source while))。

另外 - 這是很重要的 - 除了定義頂級以外不要放任何東西。這是因爲在編譯代碼時實際執行頂層窗體(嘗試在頂層加載一個帶有(println :foo)的庫)。該無限循環應該包裝在一個函數中; Clojure中「主」功能的標準名稱是-main; panelframe也是如此。這當然不適用於在REPL玩耍,但這是一個重要的知識。

順便提一句,(doto foo ...)返回foo,所以你可以寫(doto (proxy ...) (.setFocusable true) ...)

否則,我會說代碼是好的。通常你會想把它放在一個名字空間中;那麼所有的進口都將以ns的形式出現。

HTH

+0

謝謝,這很有幫助。 – StackedCrooked 2010-05-08 02:31:33

+0

我認爲你要麼定義你自己的'case'或者用'(condp = keyCode ...)'去。還是在標準的Clojure庫中有一個'case'? – 2010-05-13 16:11:26

+0

哦,顯然它已被添加到1.1後。感謝您的支持!當然,1.1的正確解決方案確實是使用'(condp = ...)'(實際上即使在1.2中,如果測試表達式之間存在散列衝突,請參閱http://www.assembla.com/spaces/clojure/門票/ 296)。 – 2010-05-13 16:38:28

3

雖然不完全Clojure的建議 - 考慮使用替代的KeyListener KeyAdapter。這樣你就不必爲keyReleased和keyTyped提供空的實現。當你不需要Listener接口的所有方法時,使用適配器類通常是一個很好的規則。

5

編輯,雖然趕時間寫下面的文章,但我忘了說你不能把常量放在例如java.awt.Color/WHITE。

除了Michal的評論:

  • 當您使用一個無限循環,用原子調理它(例如@switch)這樣你就可以阻止它(除非環路上運行你可以使用refs,所以它意味着x和y的值是協調一致的(在你的代碼中它們不是:每個更新都是獨立);因此,在drawRectangle中,您應該將讀取操作包裝在dosync中以確保您獲得一致的視圖 - 再次在您的實際代碼中,這並不重要,因爲x和y總是獨立更新。

心連心,

(無恥插頭:http://conj-labs.eu/