用另一个信号的值过滤一个信号

Filter a signal by the value of another signal

我想为鼠标位置创建过滤 Signal。它应该在按下鼠标按钮或鼠标从 "not down" 过渡到 "down" 时更新。

我想出了这个功能。它有效,但使用三个匿名函数似乎不对。有惯用的方法吗?

mouseDownPosition: Signal (Int, Int)
mouseDownPosition = 
  Signal.map2 (\(x, y) isDown -> (x, y, isDown)) Mouse.position Mouse.isDown
  |> Signal.filter (\(x, y, isDown) -> isDown) (0, 0, False)
  |> Signal.map (\(x, y, isDown) -> (x, y))

在发布问题时,我想出了如何避免匿名函数。不过,我觉得有一个更优雅的解决方案。

mouseDownPosition: Signal (Int, Int)
mouseDownPosition = 
  Signal.map2 (,) Mouse.position Mouse.isDown
  |> Signal.filter snd ((0, 0), False)
  |> Signal.map fst

我更喜欢@joews 的解决方案,但只是为了替代,这是另一个版本:

mouseDownPosition: Signal (Int, Int)
mouseDownPosition = 
  Signal.map2 (\p d -> if d then Just p else Nothing) Mouse.position Mouse.isDown
  |> Signal.filterMap identity (0,0)

库函数

有一个名为 signal-extra (full disclosure: I'm the author of that library), which has a function for just this: keepWhen: Signal Bool -> a -> Signal a -> Signal a 的信号实用程序库。

[...] filter that keeps events from the Signal a as long as the Signal Bool is true.

实施

如果你想知道,implementation 和你自己写的几乎一样,但有细微的差别!

keepWhen : Signal Bool -> a -> Signal a -> Signal a
keepWhen boolSig a aSig =
  zip boolSig aSig
  |> sampleOn aSig
  |> keepIf fst (True, a)
  |> map snd

zip只是一个Signal.map2 (,)来组合两个信号。 keepIfSignal.filter 的旧名称(恕我直言,更清晰)。所以这基本上是一样的。但是把两个信号压缩在一起之后,就有了一个sampleOn。就在那里,所以过滤的结果是一个信号,它只在原始值信号 aSig 更新时更新,而在 boolSig 更新时不更新。

替代语义

还有一个名为 sampleWhen: Signal Bool -> a -> Signal a -> Signal a 的过滤器,其描述为:

A combination of Signal.sampleOn and keepWhen. When the first signal becomes True, the most recent value of the second signal will be propagated.

此备选方案与您编写的代码相符,但与您的描述不符"Its value should change only when a mouse button is down"。

现在由您决定您真正想要的是哪种行为:)