7

我所说的一阶约束是什么意思

首先,我将解释我所说的对箭头的一阶约束的含义:由于箭头脱糖的方式,您不能在箭头 do-notation 中需要箭头命令的地方使用本地绑定名称。

下面是一个例子来说明:

proc x -> f -< x + 1desugar toarr (\x -> x + 1) >>> f和类似地desugar proc x -> g x -< ()to arr (\x -> ()) >>> g x,其中第二个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)) a或定义?accumHold' :: SF (a,Event (a -> a)) -> a

注意:没有ArrowApplyfor 的实例SF。我的理解是,定义一个也没有意义。有关详细信息,请参阅“使用箭头编程”

4

3 回答 3

3

这是一个理论上的答案。看看Roman Cheplyaka对这个问题的回答,它更多地涉及你想要实现的实际细节。


原因n超出范围是因为要在范围内使用它,您将拥有相当于bind>>=来自 monads。它是使用先前计算的结果作为下一个计算的功能输入,这使得某些东西像单子一样强大。

因此,n当您可以创建 ArrowApply 实例时,您可以将其作为函数参数提供给后续箭头。

Chris Kuklewicz 在他的评论中正确地指出了这-<<n纳入范围 - 它也使用app,因此您需要一个 ArrowApply 实例。

概括

除非您使用 ArrowApply,否则不会。这就是 ArrowApply 的用途。

于 2013-06-30T15:10:53.320 回答
2

noiseR是信号函数;它产生一个随机数,而不仅仅是一个随机数(为此,您只需使用randomRfrom System.Random)。

另一方面, 的第一个参数accumHold只是一个初始值。

所以这不仅仅是一些限制——它实际上可以防止你提交类型错误。

如果我正确理解您要做什么,那么只需使用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)

我想用随机值(大概来自noiseR)替换 100s。

我如何解决这个问题是在方向上累积,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 回答