Улучшение моей первой программы 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.

Любые подсказки для улучшения этого кода приветствуются.

11
задан StackedCrooked 8 May 2010 в 00:37
поделиться

3 ответа

Те if в keyPressed можно заменить одним case . Кроме того, dosync можно переместить за пределы корпуса . Фактически, alter также можно переместить, чтобы, например, решите изменить его на коммутируют , есть только одно место, где можно внести изменения. Результат:

(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) и (clojure.contrib.repl-utils / source while) ).

Также - и это важно - не помещайте ничего, кроме определений, на верхний уровень . Это потому, что формы верхнего уровня фактически выполняются при компиляции кода (попробуйте загрузить библиотеку с (println: foo) на верхнем уровне). Этот бесконечный цикл должен быть заключен в функцию; стандартное имя для «главной» функции в Clojure - -main ; то же самое касается панели и кадра .Это, конечно, не применяется, когда вы играете в REPL, но об этом важно знать.

Между прочим, (doto foo ...) возвращает foo, поэтому вы можете просто написать (doto (proxy ...) (.setFocusable true) ...) .

В противном случае я бы сказал, что код в порядке. Обычно вы хотите поместить его в пространство имен; тогда весь импорт будет в форме нс .

HTH

12
ответ дан 3 December 2019 в 05:57
поделиться

Хотя это не совсем совет Clojure - подумайте об использовании KeyAdapter вместо KeyListener. Так вам не придется создавать пустые реализации для keyReleased и keyTyped. Вообще, это хорошее правило - использовать классы-адаптеры, если вам не нужны все методы интерфейса Listener.

3
ответ дан 3 December 2019 в 05:57
поделиться

Редактировать, при написании поста ниже в спешке я забыл сказать, что вы не должны помещать скобки вокруг констант, например java.awt.Color / WHITE.

В дополнение к комментариям Михала:

  • когда вы используете бесконечный цикл, задайте его атомом (например, @switch), чтобы вы могли его остановить (если только циклы не выполняются в потоке repl - так что запустите его в отдельном потоке с помощью «future»)

  • вы используете refs, поэтому это означает, что значения x и y предназначены для согласования (в вашем коде это не так: каждое обновление является независимым); следовательно, в drawRectangle вы должны обернуть чтение в dosync, чтобы убедиться, что вы получите согласованное представление - опять же, в вашем фактическом коде это не имеет значения, потому что x и y всегда обновляются независимо.

hth,

(бессовестная вилка: http://conj-labs.eu/ )

5
ответ дан 3 December 2019 в 05:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: