2010-05-14 49 views
5

Có cách nào để trả về ngay lập tức từ một hàm khi ở một hoặc nhiều vòng lồng nhau không?Trả về từ một hàm trong khi bên trong một hoặc nhiều vòng lồng nhau?

Dưới đây là một số mẫu mã minh họa vấn đề:

; 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)) 

Có lẽ tôi đang tiếp cận vấn đề quá nhiều một cách bắt buộc.

Cập nhật
Ok, tôi tìm thấy một giải pháp:

; 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)) 

Nó sử dụng một tài liệu tham khảo có thể thay đổi như một lá cờ dừng, phá vỡ phong cách chức năng của chương trình. Nhưng tôi rất vui khi có một giải pháp. Hãy chia sẻ một cách tốt hơn.

Cập nhật
Đối với những người quan tâm, tôi đã hoàn thành một phiên bản phiên bản đầu tiên của Clojure Tetris trò chơi của tôi. Hãy thử một lần thử :)

+0

Bằng vòng lặp lồng nhau, bạn có nghĩa là cấu trúc đệ quy (đệ quy thực sự hoặc cấu trúc lặp lại)? Hoặc bạn có nghĩa là một bộ xử lý chuỗi như "cho"? – Greg

+0

@Greg Harman: Tôi đã cập nhật bài đăng của mình bằng mẫu mã. – StackedCrooked

+0

Chỉ cần một mẹo: bạn nên giới thiệu trừu tượng trong mã này (ví dụ: tách các phần bằng cách sử dụng fns và macro nếu thích hợp) nếu không có nút cổ chai hiệu suất tại đây. Mã có vẻ khá phức tạp và do đó khó bảo trì hơn. – jjpe

Trả lời

4

chưa được kiểm tra:

(defn position-valid? [field block] 
    (let [g (block :grid)] 
    (every? true? (for [x (range 0 (inc (g :width))) 
         y (range 0 (inc (g :height))) 
         :let [block-value (get-grid g x y) 
           field-x  (+ x @(block :x)) 
           field-y  (+ y @(block :y))]] 
        (and (not (zero? block-value)) 
         (>= field-x 0) 
         (< field-x (field :width)) 
         (< field-y (field :height)) 
         (zero? (get-grid field field-x field-y))))))) 

for là lười biếng, vì vậy every? sẽ chỉ đi cho đến khi nó đạt đến giá trị không thực sự đầu tiên.

+0

Nếu khối-giá trị bằng không thì sự lặp lại có thể mang lại hiệu quả ngay lập tức và tiếp tục vòng lặp.Đối với phần còn lại, nó hoàn hảo. Cảm ơn! – StackedCrooked

2

Trong cấu trúc vòng lặp, bạn kiểm tra xem bạn có cần giữ vòng lặp hay không và lặp lại nếu bạn thực hiện hoặc trả về giá trị nếu bạn không . Trong một vòng lặp while, bạn chỉ cần làm cho vị từ bằng false. Không có sự phá vỡ và tiếp tục trong Clojure, bởi vì nó không có ý nghĩa trong Clojure.

Tôi nghĩ bạn đang tìm kiếm loop và không phải dotimes.

+0

Cảm ơn, tôi đã cập nhật bài đăng của mình bằng mẫu bằng cách sử dụng vòng lặp/lặp lại. Nó hoạt động, nhưng nó là một chút xấu xí bởi vì nó sử dụng một tham chiếu có thể thay đổi được như cờ dừng. Vui lòng đề xuất cải tiến. – StackedCrooked

0

Tôi nghĩ bạn có thể thay thế các vòng lồng nhau dotimes bằng hàm bậc cao thành ngữ đi bộ sưu tập dữ liệu và trả về giá trị boolean. Ví dụ: tôi nghĩ rằng some có thể trợ giúp.

1

Bạn đang đi đúng hướng bằng cách thay thế dấu chấm bằng vòng lặp/lần lặp lại. Bây giờ, để thoát khỏi điều đó có thể thay đổi dừng cờ:

  1. Thêm một biến thứ hai đại diện cho cờ dừng để vòng của bạn, giống như

    (loop [x 0 stop false] ... 
    
  2. Đừng một nếu/sau đó để xem nếu dừng cờ là đúng như hoạt động đầu tiên trong vòng lặp.

    (if stop (println "I'm all done) (... 
    
  3. sâu trong mã lồng nhau của bạn, nơi bạn có if-không kiểm tra, có cả chi nhánh gọi tái diễn với các giá trị thích hợp đặt ra cho sai. Để diễn giải:

    (if (stop-condition-is-true) (recur y true) (recur (inc y) false)) 
    
2

Vì trong một câu hỏi khác của OP, tôi đã đề xuất một cấu trúc dữ liệu khác cho lưới phát - cụ thể là vec tơ vectơ - tôi bị cám dỗ để cho thấy cách tôi giải quyết vấn đề này với biểu diễn đó.Vì mục đích của vấn đề này, có vẻ như dễ nhất với tôi khi sử dụng 01 để thể hiện trạng thái ô lưới. Điều chỉnh mã cho trường hợp của một cấu trúc ô lưới phức tạp hơn (có thể một bản đồ giữ số hoặc một Boolean ở đâu đó bên trong) sẽ không có vấn đề gì.

Đây là chức năng được thảo luận:

(defn check-position-valid [field-grid block] 
    (let [grid-rect (subgrid field-grid 
          @(block :x) 
          (-> block :grid :width) 
          @(block :y) 
          (-> block :grid :height)) 
     block-rect (-> block :grid :data)] 
    (and grid-rect 
     (not-any? pos? 
        (mapcat #(map (comp dec +) %1 %2) 
          grid-rect 
          block-rect))))) 

tôi loại bỏ các bản đồ grid struct; thay vào đó, tất cả các lưới là các vectơ đơn giản của vectơ. Lưu ý rằng việc giữ các khóa rõ ràng :width:height có thể không nhất thiết phải giúp ích nhiều về hiệu suất, vì vectơ Clojure giữ số lượng thành viên của chúng (cũng như nhiều bộ sưu tập Clojure khác). Không có lý do cụ thể để không có chúng, mặc dù, tôi chỉ tìm thấy nó đơn giản để làm mà không có. Điều này ảnh hưởng đến thuật ngữ của tôi dưới đây: từ 'lưới' luôn đề cập đến vectơ của vectơ.

Sau đây tạo lưới mà các chức năng khác hoạt động; còn được hưởng các chức năng tiền thưởng in:

(defn create-grid 
    ([w h] (create-grid w h 0)) 
    ([w h initial-value] 
    (let [data (vec (map vec (repeat h (repeat w initial-value))))] 
     data))) 

(defn print-grid [g] 
    (doseq [row g] 
    (apply println row))) 

Chìa khóa để các phiên bản trên của check-position-valid là chức năng này, mang đến cho như một subgrid của lưới điện đưa ra:

(defn subgrid 
    "x & y are top left coords, x+ & y+ are spans" 
    [g x x+ y y+] 
    (if (and (<= (+ x x+) (count g)) 
      (<= (+ y y+) (count (first g)))) 
    (vec 
    (map #(subvec % x (+ x x+)) 
      (subvec g y (+ y y+)))))) 

subvec được quảng cáo bởi docstring của nó như là một hoạt động O (1) (thời gian không đổi) rất nhanh, vì vậy điều này cũng khá nhanh. Ở trên, nó được sử dụng để trích xuất một cửa sổ vào lưới đã cho, mà chính nó là một lưới (và có thể được in với print-grid). check-position-valid mất một cửa sổ như vậy vào lưới và kiểm tra nó song song với lưới của khối để xác định xem khối có ở vị trí hợp lệ hay không.

Người ta cho rằng giá trị lý luận hoàn toàn vô nghĩa (âm x, x+, y, y+) sẽ không xảy ra, tuy nhiên trong trường hợp cửa sổ sẽ "lè" của lưới ở bên phải hoặc ở phía dưới, nil được trả về thay vì chỉ số của subvec ngoài giới hạn ngoại lệ.

Cuối cùng, một định nghĩa của current-block có thể sử dụng với các phần trên:

(def current-block 
    {:grid [[0 1 0] 
      [0 1 0] 
      [0 1 1]]) 
     :x (ref 0) 
     :y (ref 0)}) 

Và một số chức năng tiện ích (mà tất cả các lưới cửa sổ mới)

(defn get-grid [g x y] 
    (get-in g [y x])) 

(defn set-grid [g x y v] 
    (assoc-in g [y x] v)) 

(defn swap-grid [g x y f & args] 
    (apply update-in g [y x] f args)) 

(defn get-grid-row [g y] 
    (get g y)) 

(defn set-grid-row [g y v] 
    (assoc g y (vec (repeat (count (g 0)) v)))) 

(defn get-grid-col [g x] 
    (vec (map #(% x) g))) 

(defn set-grid-col [g x v] 
    (vec (map #(assoc-in % [x] v) g))) 

Sau bốn có thể được sử dụng để xây dựng một kiểm tra lưới nhanh chóng như vậy (các 2 s và 3 s không có ý nghĩa trong việc kết nối với mã ở trên vì nó hiện đang được viết, nhưng chúng phục vụ để minh họa điều gì xảy ra):

user> (print-grid (set-grid-row (set-grid-col (create-grid 6 10) 1 2) 0 3)) 
3 3 3 3 3 3 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
0 2 0 0 0 0 
nil 
+0

Cảm ơn, điều này có vẻ như một tập hợp rất hữu ích của các chức năng tiện ích chung cho thao tác lưới. Một vấn đề mà tôi nghĩ rằng vẫn còn với chức năng kiểm tra vị trí hợp lệ là khối lưới có thể dính vào bên trái, bên phải hoặc dưới và điều này không nhất thiết có nghĩa là vị trí của nó không hợp lệ. Ví dụ, khối L được xác định ở trên có một cột bên trái chứa đầy số 0. Vì vậy -1 là giá trị hợp lệ cho vị trí x của nó. Có lẽ một thực hiện học tập tốt đẹp của kiểm tra vị trí hợp lệ sẽ không thể. Cảm ơn bài đăng rất giáo dục của bạn! – StackedCrooked

+0

Bạn được chào đón. :-) Re: khối gắn bó ra, tôi bị cám dỗ để khám phá một cách khác nhau để xử lý luân chuyển; nó có thể kết thúc đơn giản hơn về khái niệm và sẽ loại bỏ vấn đề này như là một tác dụng phụ. Bạn chắc chắn phải với những hàng trống/cols tại chỗ - mà tôi hoàn toàn quên - ở trên sẽ phải được sửa đổi để xử lý tất cả các trường hợp đúng cách. Một cách tiếp cận có thể là kiểm tra các phần của khối có thể dính ra trước tiên - chúng cần phải là tất cả bằng không, tất nhiên - sau đó xác minh phần còn lại như trên. –

Các vấn đề liên quan