改进我的第一个Clojure计划

Sta*_*ked 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))
Run Code Online (Sandbox Code Playgroud)

尽管我是一位经验丰富的C++程序员,但我发现用一种语言编写一个简单的应用程序是非常具有挑战性的,这种语言使用的风格与我以前完​​全不同.

最重要的是,这段代码可能很糟糕.我怀疑各种价值观的全球性是一件坏事.我也不清楚在这里使用x和y值的引用是否合适.

欢迎任何改进此代码的提示.

Mic*_*zyk 12

这些ifS IN 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])))
Run Code Online (Sandbox Code Playgroud)

您还可以更简洁地重写导入:

(import [java.awt Color Dimension event.ActionListener])
(import [javax.swing JFrame JPanel])
Run Code Online (Sandbox Code Playgroud)

- 你是否想要的是风格问题.

我将重命名drawRectangledraw-rectangle(这是Clojure中函数名称的惯用样式),更重要的是,将其重写为接受坐标作为显式参数的纯函数.那么你可以在它周围写一个小包装来使用你的Refs,如果你的设计确实会受益于Refs的使用.(很难说你不知道如何使用和修改它们等)

身高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


cgr*_*and 5

编辑,同时匆忙写下面的帖子我忘了说你不能把常量放在常量上,例如java.awt.Color/WHITE.

除了Michał的评论:

  • 当你使用一个无限循环时,用一个原子来调节它(例如@switch),这样你就能够阻止它(除非循环在repl线程上运行 - 所以它在一个带有"future"的分离线程中)

  • 你使用refs,所以它意味着要协调x和y的值(在你的代码中它们不是:每次更新都是独立的); 因此,在drawRectangle中,您应该将读取包装在dosync中以确保获得一致的视图 - 再次在您的实际代码中它无关紧要,因为x和y总是独立更新.

心连心,

(无耻的插件:http://conj-labs.eu/)