Code: Alles auswählen
def income():
for u in game.units:
if u.type == ut_city:
u.player.money += u.type.income
Hier meine (in Python nicht gezeigten) Objektdefinitionen:
Code: Alles auswählen
(defstruct game :players :units)
(defstruct player :no :money :units)
(defstruct unit :no :type :player)
(defstruct unittype :income)
(def ut-city (struct unittype 10))
(def *game* (struct game
[(struct player 1 5 [1])
(struct player 2 0 [2 3])]
[(struct unit 1 ut-city 1)
(struct unit 2 ut-city 2)
(struct unit 3 ut-city 2)]))
Dafür baue ich mir einige Zugriffsfunktionen:
Code: Alles auswählen
(defn all-players [] (*game* :players))
(defn all-units [] (*game* :units))
;; ich finde kein "detect" oder "find" in der echt miesen Dokumentation...
(defn find-by-no [no coll] (first (filter #(= (% :no) no coll)))
(defn player [no] (find-by-no no (all-players)))
(defn unit [no] (find-by-no no (all-units)))
(defn is-city [u] (= (:type u) ut-city))
(defn all-cities [] (filter is-city (all-units)))
Code: Alles auswählen
(defn income-per-players []
(reduce (fn [m c]
(let [p (player (:player c)) income (:income (:type c))]
(assoc m p (+ (or (m p) 0) income))))
{}
(all-cities)))
Nun muss ich ein neues Spiel bauen, welches eine neue Liste mit neuen Spielerobjekten hat, deren Geld das neue Einkommen enthält.
Code: Alles auswählen
(defn add-income [new-income]
(map (fn [p] (assoc p :money (+ (:money p) (or (new-income p) 0))))
(all-players)))
Code: Alles auswählen
(defn income []
(set! *game* (assoc *game* :players (add-income (income-per-players)))))
Hier sind noch zweite Varianten:
Code: Alles auswählen
def income():
for p in game.players:
p.money = p.money + sum(u.type.income for u in p.units if u.type == ut_city)
def income(game):
return update(game, {
'players',
map(lambda p: update(p, {
'money':
p.money + sum(u.type.income for u in p.units if u.type == ut_city)
}), game.players)
})
def update(obj, dct):
import copy
nobj = copy.copy(obj)
for k, v in dct.items():
setattr(nobj, k, v)
return nobj
Code: Alles auswählen
(defn money-of-player [p]
(reduce +
(:money p)
(for [u (map unit (:units p)) :when (is-city u)] (:income (:type u)))))
(defn players-with-updated-income []
(map (fn [p] (merge p {:money (money-of-player p)})) (all-players)))
(def update-players! [players] (set! *game* (assoc *game* :players players)))
(defn income [] (update-players! (players-with-updated-income)))
Clojure kennt übrigens eine Funktion `update-in`, mit der man (ineffizient) folgendes machen kann:
Code: Alles auswählen
(def game (ref (struct ...)))
(defn update-game-in [ks f v]
(dosync (ref-set game (update-in @game ks #(f % v)))))
(defn income []
(for [u (:units @game) :when (is-city u)]
(update-game-in [:players (:player u) :money] + (:income (:type u)))))
Ich speichere jetzt in `:player` nicht mehr die Nummer, sondern einen Index, aber ansonsten ist das jetzt fast genauso kompakt die der ursprüngliche Python-Code.
Stefan
PS: Leider ist die Entwicklung mit Clojure zur Zeit PITA^2, da einembei Fehlern nichtssagende Java-Exceptions um die Ohren fliegen und die API-Doku in Form einer alphabetischen Liste aller Funktionen und Macros absolut unzureichend ist.