改进我的第一个Clojure程序 [英] Improving my first Clojure program

查看:114
本文介绍了改进我的第一个Clojure程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过几个周末探索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 ifs 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆