C'è un modo per tornare immediatamente da una funzione in uno o più cicli annidati?Ritorno da una funzione all'interno di uno o più cicli annidati?
Ecco qualche esempio di codice che illustra il problema:
; Grid data structure
; -------------------
(defstruct grid :width :height)
(defn create-grid [w h initial-value]
(struct-map grid
:width w
:height h
:data (ref (vec (repeat (* w h) initial-value)))))
(defn create-grid-with-data [w h gdata]
(struct-map grid
:width w
:height h
:data (ref gdata)))
(defn get-grid [g x y]
(let [gdata (g :data)
idx (+ x (* (g :width) y)) ]
(gdata idx)))
(defn set-grid [g x y value]
(let [data (deref (g :data))
idx (+ x (* (g :width) y)) ]
(dosync (alter (g :data) (fn [_] (assoc data idx value))))))
(defn get-grid-rows [g]
(partition (g :width) (deref (g :data))))
; Beginning of test app
; ---------------------
; The Tetris playing field
(def current-field (create-grid 20 10 0))
; A tetris block (the L-Shape)
(def current-block {
:grid (struct-map grid :width 3 :height 3 :data [ 0 1 0
0 1 0
0 1 1 ])
; upper-left corner of the block position in the playing field
:x (ref 0)
:y (ref 0)
})
; check-position-valid checks if the current position
; of a block is a valid position in a playing field
(defn check-position-valid [field block]
(dotimes [ x ((block :grid) :width) ]
(dotimes [ y ((block :grid) :height) ]
(if
(let [ g (block :grid)
block-value (get-grid g x y)
field-x (+ x (deref (block :x)))
field-y (+ y (deref (block :y))) ]
(if (not (zero? block-value))
(if-not
(and (>= field-x 0)
(< field-x (field :width))
(< field-y (field :height))
(zero? (get-grid field field-x field-y)))
false ; invalid position, function should now return false
true ; ok, continue loop
)))
true
false))))
(println (check-position-valid current-field current-block))
Forse mi sto avvicinando il problema troppo in modo imperativo.
Aggiornamento
Ok, ho trovato una soluzione:
; check-position-valid checks if the current position
; of a block is a valid position in a playing field
(defn check-position-valid [field block]
(let [stop-condition (ref false)]
(loop [ x 0 ]
(when (and (not (deref stop-condition))
(< x ((block :grid) :width)))
(println "x" x)
(loop [ y 0 ]
(when (and (not (deref stop-condition))
(< y ((block :grid) :height)))
(println "y" y)
(let [ g (block :grid)
block-value (get-grid g x y)
field-x (+ x (deref (block :x)))
field-y (+ y (deref (block :y))) ]
(if (not (zero? block-value))
(if-not
(and (>= field-x 0)
(< field-x (field :width))
(< field-y (field :height))
(zero? (get-grid field field-x field-y)))
(do
(println "stop is true")
(dosync (alter stop-condition (fn [_] true)))))))
(recur (inc y))))
(recur (inc x))))
(not (deref stop-condition))))
(println (check-position-valid current-field current-block))
Esso utilizza un riferimento mutabile come una bandiera di arresto, rompendo lo stile funzionale di programmazione. Ma sono felice di avere una soluzione. Sentiti libero di condividere un modo migliore.
Aggiornamento
Per chi fosse interessato, ho finito una prima versione versione del mio Clojure Tetris gioco. Sentitevi liberi di provarlo :)
Per cicli nidificati, si intende una struttura ricorsiva (ricorsione effettiva o una struttura ricorsiva)? O intendi un processore di sequenze come "for"? – Greg
@Greg Harman: ho aggiornato il mio post con un esempio di codice. – StackedCrooked
Solo un suggerimento: dovresti introdurre delle astrazioni in questo codice (cioè separare le parti usando fns e macro dove appropriato) se non ci sono colli di bottiglia nelle prestazioni qui. Il codice così com'è sembra abbastanza complesso e quindi più difficile da mantenere. – jjpe