0

私はHtDPの第4章でBSL言語を使用して作業しています。

私が取り組んでいた問題は次のとおりです。

演習136:メインを実行し、スペースバーを押して(ショットを発射)、しばらく待つと、ショットがキャンバスから消えます。ただし、ワールドキャンバスをシャットダウンすると、この目に見えないショットがまだ含まれているワールドになります。

代替のtock関数を設計します。これは、ショットをクロックティックごとに1ピクセル移動するだけでなく、座標がキャンバスの上に配置されているショットを削除します。ヒント:再帰的なcond句の補助関数の設計を検討することをお勧めします。

私が思いついた解決策は以下のとおりです(スポイラーで)。しかし、私は何か冗長なことをしているように感じます。基本的に、補助関数の私の適用は完全に正しくありません。

(define (main w0)
  (big-bang w0
            (on-tick ticking)
            (on-key fire-key)
            (to-draw to-render)))

(define HEIGHT 100)
(define WIDTH 80)
(define TURRET-X-POS (/ WIDTH 2))
(define BKGRND (empty-scene WIDTH HEIGHT))
(define SHOT-IMG (triangle 4 "solid" "red"))

(define (to-render w0)
  (cond
    [(empty? w0) BKGRND]
    [else (place-image SHOT-IMG TURRET-X-POS (first w0) (to-render (rest w0)))]))

(define (fire-key w0 ke)
  (cond
    [(key=? ke " ") (cons HEIGHT w0)]
    [else w0]))

(define (ticking w0)
  (cond
   [(empty? w0) empty]
   [(empty? (only-inbound-shots w0)) empty]
   [else (cons (sub1    (first (only-inbound-shots w0))) 
               (ticking (rest  (only-inbound-shots w0))))]))

(define (only-inbound-shots w0)
  (cond      
    [(< (first w0) -4) (rest w0)]
    [else w0]))

更新:(
これは以前よりもはるかにクリーンです)

(define HEIGHT 100) ;height of scene
(define WIDTH 80)   ;width of scene
(define TURRET-X-POS (/ WIDTH 2)) ;position of turret, ie. shot's x-coordinate
(define BKGRND (empty-scene WIDTH HEIGHT)) ; scene itself
(define SHOT-IMG (triangle 4 "solid" "red")) ;image representing the shot
(define Y-BOUNDARY -4) ;y-coordinate where shot is no longer visible in scene

;List-of-numbers -> List-of-numbers
;renders all shots fired
(define (to-render w0)
  (cond
    [(empty? w0) BKGRND]
    [else (place-image SHOT-IMG TURRET-X-POS (first w0) 
                       (to-render (rest w0)))]))

;List-of-numbers, key event -> List-of-numbers
;only allows the space bar to fire a shot
;one space bar event produces one shot
(define (fire-key w0 ke)
  (cond
    [(key=? ke " ") (cons HEIGHT w0)]
    [else w0]))

;List-of-numbers -> List-of-numbers
;during each clock tick, the y-coordinate each of the shot 
;                                      in List-of-numbers is updated
;each y-coordinate decreases by -1
(define (ticking w0)
  (cond
    [(empty? w0) w0]
    [else (only-inbound-shots (update-shots w0) Y-BOUNDARY)]))

;List-of-numbers -> List-of-numbers
;does the actual updating of the shots in List-of-numbers
;each shot's value is decreased by -1
(define (update-shots w0)
  (cond
    [(empty? w0) w0]
    [else (cons (sub1 (first w0)) (update-shots (rest w0)))]))

;List-of-numbers -> List-of-numbers
;checks to see if the first shot in the List-of-numbers has gone past the Y-BOUNDARY
;if so then remove shot from the List-of-numbers and return the rest of the List
;otherwise return the List without change
(define (only-inbound-shots w0 y-boundary)
  (cond
    [(empty? w0) w0]
    [(< (first w0) y-boundary) (rest w0)]
    [else w0]))

;List-of-numbers -> List-of-numbers
;creates the world of shots
;seed value is empty, additional values created by space bar
(define (main w0)
  (big-bang w0
            (on-tick ticking)
            (on-key fire-key)
            (to-draw to-render)))

追加されたテスト:
私はまだテストに取り組んでいます。

(define test-shots
  (cons -6 (cons -5 (cons 10 empty))))

(define test-shots-2
  (cons -6 (cons 2 (cons 7 empty))))

(define test-shots-3
  (cons 4 (cons 9 (cons 10 empty))))

(check-expect (to-render test-shots) 
  (place-image SHOT-IMG TURRET-X-POS -6
    (place-image SHOT-IMG TURRET-X-POS -5
      (place-image SHOT-IMG TURRET-X-POS 10
        BKGRND))))


(check-expect (to-render test-shots-2) 
  (place-image SHOT-IMG TURRET-X-POS -6
    (place-image SHOT-IMG TURRET-X-POS 2
      (place-image SHOT-IMG TURRET-X-POS 7
        BKGRND))))

ワールド関数が追加されたテスト:

(define HEIGHT 1) ; makes test a little faster

(check-expect
  (fire-key 
    (ticking 
      (ticking 
        (ticking 
          (ticking 
            (fire-key 
              (ticking 
                (ticking 
                  (ticking 
                    (ticking (fire-key empty " "))))) 
            " "))))) 
    " ")
  (cons -3 (cons 1 empty))
4

2 に答える 2

7
  • 欠落している契約、目的ステートメント、およびデータ定義に関する通常のコメントがここに適用されます。個々の機能のテストと同様に; world.ss / universe.ssが本当に優れたライブラリである大きな理由は、概念的に入出力を実行している関数をテストできるようにするためです。

  • 私はあなたのデータ定義がコードから何であるかについて多くを推測していますが、(1。)あなたはその責任を読者に負わせるべきではありません、そして(2.)それは私の推論の間違いにつながる可能性があります。

  • ticking;の定義のテンプレートから大幅に逸脱しているように見えます。それは私が考えることができるどんなテンプレートのようにも見えません。同様のコメントが適用されますonly-inbound-shots

  • ticking複数のサブルーチンに分割してから、それらを作成することをお勧めします。

    これが意味する例:数値のリストの平均を取る関数を作成する場合、それを行う簡単な方法は、2つの新しい関数を作成することです。最初の関数は数値の合計を生成し、 2番目はリストの長さを生成します。これらは、デザインレシピを介して書くのは簡単です。次に、次のようaverageになります。

    ;; average : [Listof Number] -> Number
    ;; produces average value of input (x_1 x_2 ... x_n
    (define (average l)
      (/ (sum-of-list l) (length-of-list l)))
    

    averageしかし、のテンプレートに従った単一の定義でそれを実行しようとすると[Listof Number]、正しい答えを得るのにいくつかの問題が発生します。(アキュムレータを1つか2つ使用しないと適切に実行できないと思います。)

    非常に単純なサブルーチンをファクタリングし、最後にそれらを構成して目的の効果を得るということは、分割してから構成することを意味しますticking。(入力を破壊しない場合、関数の合成は完全に有効な設計プロセスです。HtDPセクション3.1を参照してください。)

  • しかし、もっと重要なことは、個々の機能についていくつかのテストを行うことだと思います。特にonly-inbound-shots:この関数については、それ自体で考えてみることをお勧めします。

    • 誰がそれを呼ぶのかわからないふりをして、彼らがその契約に従うだけだと思ってください(たとえば、あなたがここにいると定義したものは何でも、彼らは世界を通過するだけです)。
    • そして、彼らが提供する可能性のある法的インプットに対して正しい答えを出すようにしてください。
    • 上記の他のコードで自分でどのように使用するかを考えないでください。すべてを同時に頭の中に入れておきたくないからです。ここで一般化する方が実際には簡単で、考えられる入力に対して何をすべきかを考えます。only-inbound-shots

テストの問題について考えるための具体的な食べ物を提供するために、テストで処理しようとする可能性のある入力を説明するいくつかの架空の写真を次に示します。

1つの上の2つのボール、、3つのボール2つの低い_3つのボール、2つの高さ


2013年2月28日更新:

各関数の個別の単体テストを作成することをお勧めしますが、エンドツーエンドのテストも重要です。この場合、現在レンダリングされているゲームでは、ショットがシーンの外側にあるかどうかはわかりません(place-imageたとえばoverlay、レンダリングから自動的にトリミングされるため)。

したがって、実行中にゲームをデバッグする場合は、そのような情報を取得すると便利です。ゲームの上に表示されるテキストのドロップダウンビットのように言います(フレームレートなどを表示するためにビデオゲームでよく見られます)。したがって、ゲームの実行中にその情報を取得するための1つの戦略があります。既存のレンダリング関数の上に階層化されている代替のレンダリング関数を交換しますが、世界のw0議論に関する他の情報を出力します。

(この場合、他の情報を抽出することを想像できますが、その長さを確認すると便利な場合があります。)

;; List-of-numbers -> Image
;; Renders w0 via to-render, with a printout of shot count in top left corner.
(define (to-render-with-count w0)
  (place-image/align (text (number->string (length w0)) 30 'blue)
                     0 0 "left" "top"
                     (to-render w0)))

次に、呼び出しをフックしto-render-with-countます。big-bangキーストロークとクロックティックが混在しているときに何が起こるかを確認できるように、クロックティックレートを遅くすることも役立つ場合があります。そのため、(on-tick句で)その変更も行いました。

(define (main w0)
  (big-bang w0
            (on-tick ticking 0.1)
            (on-key fire-key)
            (to-draw to-render-with-count)))

今、私はインタラクティブに興味深いトレンドに気付くことができます。このような状況を生み出すトレンド:

148個のボールがどこにありますか?

画面に148個のボールが表示されているのに、4個しか表示されていないのはどうしてですか?それはどのような世界で起こるのでしょうか?(によって作成されたウィンドウを閉じるbig-bangと、現在のワールドがインタラクションウィンドウに戻るため、その場でどのようなワールドが発生するかが正確にわかります。)

于 2013-02-27T09:55:01.397 に答える
0

元の質問はすでに多くのことが行われているので、ここに最終的な答えを置きます。

(HEIGHT 200を定義);シーンの高さ
 (WIDTH 80を定義);シーンの幅
 (TURRET-X-POS(/ WIDTH 2)を定義);タレットの位置、すなわち。ショットのx座標
 (BKGRND(空のシーンのWIDTH HEIGHT)を定義します); シーン自体
 (SHOT-IMG(三角形4 "実線""赤")を定義);ショットを表す画像
 (Y境界-4を定義);ショットがシーンに表示されなくなったy座標
 
 ;番号のリスト->番号のリスト
 ;発射されたすべてのショットをレンダリングします
 (定義(w0をレンダリングする)
   (条件
     [(空?w0)BKGRND]
     [else(place-image SHOT-IMG TURRET-X-POS(first w0)(to-render(rest w0)))]))
 
 ;番号のリスト、キーイベント->番号のリスト
 ;スペースバーがショットを発射することのみを許可します
 ;1つのスペースバーイベントで1つのショットが生成されます
 (定義(fire-key w0 ke)
   (条件
     [(key =?ke "")(cons HEIGHT w0)]
     [else w0]))
 
 ;番号のリスト->番号のリスト
 ;クロックティックごとに世界の状態を更新します
 (定義(w0をチェック)
   (条件
     [(空?w0)w0]
     [else(remove-outbound-shots(update-shots w0)Y-BOUNDARY)]))
 
 ;番号のリスト->番号のリスト
 ;すべてのショットを更新します
 (定義(update-shots w0)
   (条件
     [(空?w0)w0]
     [else(cons(sub1(first w0))(update-shots(rest w0)))]))
 
 ;番号のリスト->番号のリスト
 ;リストからy境界を超えるすべてのショットを削除します
 (define(remove-outbound-shots w0 y-boundary)
   (条件
     [(空?w0)w0]
[(<(最初のw0)y-boundary)(remove-outbound-shots(rest w0)y-boundary)] [else(cons(first w0)(remove-outbound-shots(rest w0)y-boundary))])) ;番号のリスト->番号のリスト ;ショットの世界を作成します ;シード値は空です。スペースバーによって追加の値が作成されます (定義(メインw0) (ビッグバンw0 (ダニのカチカチ音をたてる) (オンキーファイアキー) (描画してレンダリングする)))

テスト:

(define test-shots-1
  (cons 1 (cons 4 (cons 10 (cons -6 (cons -5  (cons 1 (cons 4 (cons 10 (cons 10 (cons -6 (cons -9 empty))))))))))))


(define test-shots-4
  (cons 10 (cons -6 (cons -5  (cons 1 (cons 4 (cons 10 empty)))))))

(check-expect (remove-outbound-shots test-shots-4 -4) (list 10 1 4 10))
(check-expect (remove-outbound-shots test-shots-1 -4) (list 1 4 10 1 4 10 10))
于 2013-03-03T19:36:08.987 に答える