Czy istnieje sposób na natychmiastowy powrót z funkcji, gdy znajduje się w co najmniej jednej zagnieżdżonej pętli?Powracanie z funkcji w jednej lub kilku zagnieżdżonych pętlach?
Oto niektóre przykładowy kod ilustrujący problem:
; 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))
Może jestem zbliża problemu zbyt dużo w imperatywem sposób.
Aktualizacja
Ok, znalazłem rozwiązanie:
; 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))
Używa zmienny odniesienie jako flaga zatrzymania, łamiąc funkcjonalnym stylu programowania. Ale cieszę się, że mam rozwiązanie. Zapraszam do dzielenia się lepszym sposobem.
Aktualizacja
Dla zainteresowanych Skończyłem pierwszą wersję wersję mojego Clojure Tetris gry. Zapraszam do wypróbowania :)
Przez zagnieżdżone pętle rozumie się strukturę rekursywną (rzeczywistą rekursję lub strukturę powtarzającą pętlę)? Czy masz na myśli procesor sekwencyjny typu "dla"? – Greg
@Greg Harman: Zaktualizowałem swój post z próbką kodu. – StackedCrooked
Wskazówka: w tym kodzie należy wprowadzić abstrakcje (tzn. Rozdzielić fragmenty za pomocą fns i makr tam, gdzie to właściwe), jeśli nie ma tu wąskiego gardła wydajności. Kod taki jak ten wygląda dość złożony i dlatego trudniejszy do utrzymania. – jjpe