我在Yampa中编写了一个基本信号函数,如下:
sf :: SF Int Int
sf = arr $ \x -> trace "1 * 2 = 2" (x * 2)
该函数将其输入加倍,并向控制台打印“1 * 2 = 2”以表明它已被调用。然后我用下面的代码测试了 sf:
embed sf (deltaEncode 1 (repeat 1))
如您所见,仅提供值 1 作为 sf 的输入。我希望 Yampa 具有某种形式的记忆功能,以防止使用相同的输入重复调用 sf,因为传递给
arr
的函数应该是纯函数。然而,似乎没有这样的支持,因为“1 * 2 = 2”被重复打印。
为了提供更多背景信息,我有一些使用 React 进行响应式编程的经验,并且我目前正在探索 FRP 的概念。 Yampa 是我选择的第一个 FRP 库,我希望在 Yampa 中找到类似于 React 的
memo
的东西来有效地处理重新计算。我在 Hoogle 中搜索了 a -> b -> SF a b
,找到了 arrPrim
。但它也没有达到我的预期。
在扬帕有办法实现这一目标吗?如果没有,Yampa 如何高效执行信号函数?
此外,我提供的示例不涉及时变系统,因为它只是忽略了时间。我最初认为 Yampa 对于仅离散事件系统也很有用,如果它至少提供类似
memo
的东西。然而,如果这样的东西不存在,那么我相信 Yampa 可能只对时变系统有用。这个理解准确吗?
由于我仍处于学习FRP的早期阶段,我担心我可能误解了一些概念。如果您能澄清任何误解或推荐其他更符合我期望的 FRP 库,我将不胜感激。
在 Haskell 中,没有默认的记忆函数。 Haskell 的惰性工作原理是值默认是不可变的,因此除非您使用不同的变量,否则不会重新计算。在这种情况下,函数被记忆,但其结果未被记忆。这是因为在幕后,您正在输入列表上调用此函数:
$ ghci
Prelude> let xs = take 5 $ repeat 1
Prelude> import Debug.Trace
Prelude Debug.Trace> let f x = trace "hi" $ x * 2
Prelude Debug.Trace> f <$> xs
[hi
2,hi
2,hi
2,hi
2,hi
2]
由于每个输入都是不同的,因此没有记忆。但是,您可以非常直接地记住您自己的函数。只需使用状态信号函数来跟踪最后的输入,使用
sscanPrim
:
-- test.hs
import FRP.Yampa
import Debug.Trace
memoized :: Eq a => a -> (a -> b) -> SF a b
memoized x0 f = sscanPrim memo x0 (f x0)
where
memo old new | old == new = Nothing
| otherwise = Just (new, f new)
sf :: SF Int Int
sf = memoized 1 $ \x -> trace "1 * 2 = 2" (x * 2)
--
$ ghci test.hs
*Main> take 10 $ embed sf (deltaEncode 1 (repeat 1))
[1 * 2 = 2
2,2,2,2,2,2,2,2,2,2]