改进我的第一个Clojure程序 [英] Improving my first Clojure program
问题描述
经过几个周末探索Clojure后,我想出了这个程序。它允许您在窗口中移动一个小矩形。这里是代码:
After a few weekends exploring Clojure I came up with this program. It allows you to move a little rectangle in a window. Here's the code:
(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 ++程序员,我发现写一个简单的应用程序非常具有挑战性,使用一种与我过去完全不同的风格的语言。
Despite being an experienced C++ programmer I found it very challenging to write even a simple application in a language that uses a radically different style than what I'm used to.
最重要的是,这段代码可能很糟糕。我怀疑各种价值观的全球性是一件坏事。这也是不清楚的,如果适当的使用引用这里的x和y值。
On top of that, this code probably sucks. I suspect the globalness of the various values is a bad thing. It's also not clear to me if it's appropriate to use references here for the x and y values.
欢迎任何提高这个代码的提示。
Any hints for improving this code are welcome.
推荐答案
如果
可以用单个
案例替换
。此外,可以将 dosync
移到外部以包装案例
。事实上, alter
也可以移出,所以如果你决定将其更改为 commute
,只有一个地方可以进行更改。结果:
Those if
s in keyPressed
can be replaced with a single case
. Also, the dosync
can be moved outside to wrap the case
. In fact, alter
can be moved out too, so that if you e.g. decide to change it to commute
, there's just the one place to make the change. The result:
(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])))
您还可以更简洁地重写导入:
You could also rewrite the imports more concisely:
(import [java.awt Color Dimension event.ActionListener])
(import [javax.swing JFrame JPanel])
- 你是否想要
我会将 drawRectangle
重命名为 draw-rectangle
(这是Clojure中函数名的惯用风格),更重要的是,将它重写为一个接受坐标作为显式参数的纯函数。然后你可以写一个小包装使用你的参考,如果的确你的设计将受益于使用Refs。 (很难说,不知道如何使用和修改它们等)
I would rename drawRectangle
to draw-rectangle
(that's the idiomatic style for function names in Clojure) and, more to the point, rewrite it as a pure function accepting the coordinates as explicit arguments. Then you could write a small wrapper around that to use your Refs, if indeed your design would benefit from the use of Refs. (Hard to say without knowing how you might want to use & modify them etc.)
首选而
(loop [] ...(recur))
(参见(doc while)
(clojure.contrib.repl-utils / source while))。
Prefer while
to (loop [] ... (recur))
(see (doc while)
and (clojure.contrib.repl-utils/source while)
).
除了顶层定义之外,任何东西都可以放置。。这是因为顶层表单实际上是在编译代码时执行的(尝试在顶层加载一个(println:foo)
的库)。那个无限循环应该被包装在一个函数中; Clojure中main函数的标准名称为 -main
;同样适用于面板
和框架
。
Also -- and this is important -- don't put anything except definitions at top level. That's because top-level forms are actually executed when the code is compiled (try loading a library with a (println :foo)
at top level). That infinite loop should be wrapped inside a function; the standard name for a "main" function in Clojure is -main
; same goes for panel
and frame
. This doesn't apply when playing around at the REPL, of course, but it's an important gotcha to know about.
顺便说一句,(doto foo) ...)
返回foo,所以你可以写(doto(proxy ...)(.setFocusable true)...)
。
Incidentally, (doto foo ...)
returns foo, so you can just write (doto (proxy ...) (.setFocusable true) ...)
.
否则,我会说代码很好。通常你想把它放在一个命名空间;那么所有的导入都将进入 ns
表单。
Otherwise, I'd say the code is fine. Normally you'd want to put it in a namespace; then all the imports would go in the ns
form.
HTH
这篇关于改进我的第一个Clojure程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!