经过几个周末探索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
这些if
S 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)
- 你是否想要的是风格问题.
我将重命名drawRectangle
为draw-rectangle
(这是Clojure中函数名称的惯用样式),更重要的是,将其重写为接受坐标作为显式参数的纯函数.那么你可以在它周围写一个小包装来使用你的Refs,如果你的设计确实会受益于Refs的使用.(很难说你不知道如何使用和修改它们等)
身高while
来(loop [] ... (recur))
(见(doc while)
和(clojure.contrib.repl-utils/source while)
).
此外 - 这一点很重要 - 除了顶级定义之外,不要放任何东西.这是因为在编译代码时实际执行了顶级表单(尝试加载(println :foo)
顶级库).那个无限循环应该包含在一个函数中; Clojure中"主要"功能的标准名称是-main
; 这同样适用于panel
和frame
.当然,这在REPL玩游戏时不适用,但这是一个重要的问题.
顺便说一句,(doto foo ...)
返回foo,所以你可以写(doto (proxy ...) (.setFocusable true) ...)
.
否则,我会说代码没问题.通常你想把它放在命名空间里; 那么所有的进口都会以ns
形式出现.
HTH
编辑,同时匆忙写下面的帖子我忘了说你不能把常量放在常量上,例如java.awt.Color/WHITE.
除了Michał的评论:
当你使用一个无限循环时,用一个原子来调节它(例如@switch),这样你就能够阻止它(除非循环在repl线程上运行 - 所以它在一个带有"future"的分离线程中)
你使用refs,所以它意味着要协调x和y的值(在你的代码中它们不是:每次更新都是独立的); 因此,在drawRectangle中,您应该将读取包装在dosync中以确保获得一致的视图 - 再次在您的实际代码中它无关紧要,因为x和y总是独立更新.
心连心,
(无耻的插件:http://conj-labs.eu/)
归档时间: |
|
查看次数: |
1239 次 |
最近记录: |