Tuesday, November 27, 2012

Baby steps in Clojure. Правдивый ли random в Java?

Недавно Санек скинул интересную статью про нерандомные рандомы в php http://boallen.com/random-numbers.html.
Вот решил побаловаться и проверить рандомный ли рандом в java :)

Сделаю тоже самое, что сделали ребята на пхп, только на Clojure, то есть сгенерирую (x * y) вариантов рандома (0 или 1), и, если выпадет 1, то нарисую точку на экране с координатой (x, y).

Для начала определю функцию, которая будет считать рандомы.

(defn rands [max-x max-y]
  (for [x (range max-x) y (range max-y)
        :let [rnd (cond (< (rand 1) 0.5) 0 :else 1)]
        :when (= rnd 1)
        ]
    [x y rnd]))


Что здесь:
- Входные параметры max-x и max-y
- Перебераем все сочетания (0..max-x : 0..max-y) и отбераем только те значения, рандом которых вернет 1. Выражение (cond (< (rand 1) 0.5) 0 :else 1) должно вернуть нам 1 или 0 с вероятностью 50%
- Возвращаем последовательность из векторов, состоящих из x, y, rnd. Где x, y - переменные, по которым итерируемся, rnd - рандомное значение (уже отфильтрованное ограничением :when)

Проверяю. Запускаю в REPL:

Вроде похоже на правду - возвращает значения x и y только для рандома, равному 1.

Теперь выведем на экран (по примеру, взятому из книжки Joy of Clojure). Запускаю в REPLe команды:
(def frame (java.awt.Frame.))
(.setSize frame 512 512)
(.setVisible frame true)
Что здесь:
- Определяем переменную frame, которой присвоится значение нового экземпляра java.awt.Frame. На Java это выглядело бы как Frame frame = new java.awt.Frame();
- Устанавливаем размер frame 512 x 51. Java: frame.setSize(512, 512);
- Делаем фрейм видимым. Java: frame.setVisible(true);

Крутизна в том, что не надо перекомпиливать приложение! Результат выполнения команд виден сразу же.

Вот как это выглядит из Идеевской консоли REPL. Я использую плагин La Closure для IDEA.


Появилось пустое awt-шное окошко, в котором и будем рисовать:


Теперь осталось совсем малость - нарисовать. Опять же в REPLe запускаю такие команды:

(def gfx (.getGraphics frame))
(doseq [[x y rnd] (rands 512 512)]
  (.fillRect gfx x y 1 1))
Что здесь:
-  Определили gfx, который равен graphics у frame. Java: Graphics gfx = frame.getGraphics();
- Проитерировались по списку, который нам вернет вызов функции (rands 512 512) 
-  Помним, что rands возвращает вектор из переменных [x, y и значение рандома rnd]. Значения x,y и рандома присвоятся в локальные переменные x,y,rnd соответственно. Такая форма в Clojure называется  деструктуризация (destructuring).
-  Для каждого элемента списка заполним единичный rectangle в координате x, y

Вот что получилось на экране:


Важно помнить, что изображение в awt-шном окошке "одноразовое". То есть оно исчезнет как только мы перекроем awt-шный фрейм другим окном или же свернем/развернем его.

Так что псевдорандом в Java похож на рандом. А Clojure + REPL - прикольная вещь!

Весь код целиком ниже. Тоже самое на Java наверняка потребовало бы чуть больше кода, но повторять это на Java уже не хочется :)

(defn rands [max-x max-y]
  (for [x (range max-x) y (range max-y)
        :let [rnd (cond (< (rand 1) 0.5) 0 :else 1)]
        :when (= rnd 1)
        ]
    [x y rnd]))

(def frame (java.awt.Frame.))
(.setSize frame 512 512)
(.setVisible frame true)
(def gfx (.getGraphics frame))
(doseq [[x y rnd] (rands 512 512)]
  (.fillRect gfx x y 1 1))

No comments:

Post a Comment