7

一次制約とは

最初に、矢印に対する 1 次制約の意味を説明します。矢印の desugar の方法により、矢印 do 表記で矢印コマンドが予期される場合、ローカルにバインドされた名前を使用することはできません。

以下に例を示します。

proc x -> f -< x + 1desugar は to にarr (\x -> x + 1) >>> f、同様proc x -> g x -< ()に desugar toarr (\x -> ()) >>> g xになります。ここで、2 番目xは自由変数です。GHC ユーザーガイドはこれを説明し、矢印がモナドでもある場合、インスタンスを作成してこれを回避するためにArrowApply使用できると述べていますapp。のようなものが にproc x -> g x -<< ()なりarr (\x -> (g x, ())) >>> appます。

私の質問

Yampa は、accumHoldこのタイプの関数を定義します: a -> SF (Event (a -> a)) a. 矢印のこの一次制限により、次の関数を書くのに苦労しています。

accumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) a
accumHoldNoiseR r g = proc f -> do
  n <- noiseR r g -< ()
  accumHold n -< f

上記の定義nは、脱糖後にスコープ外であるため機能しません。

または、同様に、ペアの最初の部分がにSF渡される初期値であることを意味するこの関数accumHold

accumHold' :: SF (a,Event (a -> a)) -> a
accumHold' = ...

私が見逃しているコンビネーターやトリックはありますか? それとも、ArrowApplyインスタンスなしでこれらの定義を書くことはできませんか?

tl;dr: yampa でaccumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) aorを定義することは可能ですか?accumHold' :: SF (a,Event (a -> a)) -> a

注:ArrowApply forのインスタンスはありませんSF。私の理解では、定義することも意味がありません。詳細については、「アローを使用したプログラミング」を参照してください。

4

3 に答える 3

3

これは理論的な答えです。この質問に対する Roman Cheplyaka の回答を参照してください。これは、達成しようとしていることの実際的な詳細を扱っています。


範囲外である理由nは、そこで使用する範囲内にあるためには、モナドと同等bindまたは>>=モナドからのものがあるためです。モナドと同じくらい強力なものを作るのは、前の計算の結果を次の計算への関数入力として使用することです。

したがってn、ArrowApply インスタンスを作成できる正確なタイミングで、後続の矢印に関数引数として指定できます。

Chris Kuklewicz は、彼のコメントで範囲内にあることを正しく指摘しています。これも を使用し-<<ているため、ArrowApply インスタンスが必要です。napp

概要

ArrowApply を使用しない限り、そうではありません。これが ArrowApply の目的です。

于 2013-06-30T15:10:53.320 に答える
2

noiseRシグナル関数です。1 つの乱数だけでなく、乱数のストリームを生成します (そのためには、 randomRfrom を使用しますSystem.Random)。

一方、 の最初の引数accumHoldはただ 1 つの初期値です。

したがって、これは単なる制限ではなく、実際に型エラーをコミットすることを防ぎます。

あなたがやろうとしていることを正しく理解していれば、単に使用するだけrandomRでうまくいくはずです。そうでない場合は、 が必要な理由を明確にしてくださいnoiseR

于 2013-06-30T10:02:34.267 に答える
0

私がこれをどのように回避したかを他の人が理解できるように、私は自分の質問に答えます。

ゲームポンを実装しようとしていました。ラウンドごとにボールがランダムな速度でスタートするようにしました。accumHoldボールの速度を定義するために使用したかったのです。次のようなコードがありました。

ballPos = proc e -> mdo -- note the recursive do
  {- some clipping calculations using (x,y) -}
  ...
  vx <- accumHold 100 -< e `tag` collisionResponse paddleCollision
  vy <- accumHold 100 -< e `tag` collisionResponse ceilingFloorCollision
  (x,y) <- integral -< (vx,vy)
  returnA -< (x,y)

100 をランダムな値 (おそらく から) に置き換えたかったのnoiseRです。

代わりにこれを解決した方法は、方向に蓄積することです。ここではcollisionResponse、符号を反転させるだけです (最終的には、壁/パドルに対する速度の角度を使用する必要があります)。

ballPos = proc (initV, e) -> mdo
  {- some clipping calculations using (x,y) -}
  ...
  (iVx,iVy) <- hold (0,0) -< initV
  vx <- accumHold 1 -< e `tag` collisionResponse paddleCollision
  vy <- accumHold 1 -< e `tag` collisionResponse ceilingFloorCollision
  (x,y) <- integral -< (iVx*vx,iVy*vy)
  returnA -< (x,y)

学んだ教訓:

多くの場合、蓄積したい値/状態を、それがどのように変化するかを説明する動作と、動作を入力として受け取る現在の値を説明する「大きさ」に分けることができます。私の場合、初速度の大きさを分離し、それを信号関数への入力として渡し、accumHold衝突のボールへの影響 (動作) を計算するために使用します。したがって、初速度が何であれ、壁にぶつかるとボールが「反射」します。そして、それはまさにaccumHoldが蓄積しているものです。

于 2013-06-30T17:06:52.743 に答える