замыкание зацикливание / рекурсия - PullRequest
2 голосов
/ 08 января 2012

Я пытаюсь выучить немного Clojure.Я сделал очень базовую модель детской игры "Горки и лестницы".Когда игрок получает счет больше или равный 100, игра заканчивается.Прямо сейчас это работает только для одного игрока.

начать игру с "(play player1)".

Я застрял в том, как бы я сделал из этого мультиплеер, используя рекрусию ибез оператора "foreach".
Как я могу изменить это для многопользовательской игры?

(defn roll []
 (+ 1 (rand-int 6)))

(def chutes { 10 5, 12 3, 55 38, 77 69})
(def ladders { 12 16, 10 25, 20 55, 77 91})

(defn apply_chutes [player]
 (if (contains? chutes @player)
  (do (reset! player (chutes @player))
      (println "down chute! " @player))))

(defn apply_ladders [player]
 (if (contains? ladders @player)
  (do (reset! player (ladders @player))
  (println "up ladder! " @player))))

(defn move [player]
 (do(swap! player + (roll))
  (println "p: " @player)
  (apply_chutes player)
  (apply_ladders player)
    player))
  (if 
("done")))

(defn play [player]
 (move player)
 (if (>= @player 100)
  (println "done")
  (play player)))

(def player1 (atom 0))

1 Ответ

3 голосов
/ 08 января 2012

Хорошо, я не буду предоставлять вам полный код, но лучше с высокоуровневым дизайном в отношении функционального программирования и, в частности, циклического выполнения / рекурсии в Clojure.

Начнем с очень важной концепции в функциональном программировании - неизменность . Функциональные программы стараются избегать любых изменений состояния системы. Вместо того, чтобы изменять состояние, программы должны создавать новое состояние. Например, если у вас есть вектор v1 = [1, 2, 3, 4] и вы хотите вставить 5 в его конец, вы не уничтожаете v1, а скорее производите v2 = [1, 2, 3, 4, 5] (v1 остается в памяти без изменений). См. этот вопрос для более подробной информации об неизменности в функциональном программировании.

Имея это, лучше всего создать специальную переменную state, которая будет содержать общее состояние игры.

Следующее, что нужно рассмотреть, это looping . Опять же, в функциональном программировании концепция цикла почти заменяется на рекурсию . Циклы и рекурсия очень часто похожи - оба позволяют повторять некоторый код много раз, прежде чем он завершается. В большинстве императивных языков программирования (например, Python, Java) рекурсия приводит к росту стека, но в функциональных языках очень популярна концепция хвостовой рекурсии . Если вы не знакомы с этой концепцией, я настоятельно рекомендую вам изучить ее. Пока я только говорю, что хвостовая рекурсия может происходить только в хвостовой позиции (последняя инструкция в потоке управления), и она не приводит к росту стека и, таким образом, может использоваться как конструкция цикла.

В Clojure хвостовая рекурсия организована с ключевым словом recur:

(defn play [state ...]
   ...
   (recur new-state ...))

Здесь мы определяем функцию play с параметром state и рекурсивно вызываем ее с ключевым словом recur в последней строке (мы можем также назвать ее как (play new-state ...), но в этом случае JVM не будет оптимизировать код быть хвостовой рекурсивной). new-state определяется где-то в теле функции и обозначает именно то, что это означает - новое состояние игры.

Наконец, вы хотите сделать свою игру многопользовательской, то есть менять текущего игрока после каждой итерации. С помощью цикла / рекурсии этого легко добиться, просто поменяв местами игроков:

(defn play [state current-player next-player]
   ;; do play with current-player and compute new-state
   (recur new-state next-player current-player))

Обратите внимание, что при повторном вызове игроки меняются местами, и, таким образом, следующий игрок становится текущим игроком, и наоборот при новом вызове play.

Имея это, вы сможете перевести ваш код в новую версию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...