gpt4 book ai didi

haskell - 将时间建模为惰性数字

转载 作者:行者123 更新时间:2023-12-03 22:35:56 24 4
gpt4 key购买 nike

我正在尝试在 Haskell 中编写交互式实时音频合成器,
我急需“懒惰的数字”来代表时间。

事情是这样的:我的程序基于“信号”的概念和这些信号
由“信号处理器”转换。但与其他类似项目不同的是
浮士德或查克,我想使用严格的纯函数,但有
显式访问时间。

这个想法是可以表达纯粹的“惰性流处理器”
Haksell 并且由于惰性评估,这将在交互式中起作用,
实时时尚。

例如,我可以将“midi 信号”表示为音符变化流
事件:

type Signal = [ (Time, Notes->Notes) ]

在非交互模式下一切都很好,但是当我想要真正
实时玩它,我遇到了一个很大的障碍:在任何一个时间点,
输出信号取决于下一个输入事件的时间。所以
我的合成引擎实际上会停止,直到下一个事件。

让我解释一下:当我的声卡要求我的输出信号样本时,
懒惰的评估器遍历我的信号处理器的依赖图和
最终要求输入(midi)信号的一部分。但是让我们说
输入信号在本地看起来像这样:
input :: Signal
input = [ ..., (1, noteOn 42), (2, noteOff 42), ... ]

当我需要在时间 1.5 计算输出(音频)信号时,我需要
像这样的东西:
notesAt :: Signal -> Time -> Notes
notesAt = notesAt' noNotes where
notesAt' n ((st,sf):ss) t
| st > t = n
| otherwise = notesAt' (sf n) ss t

...当我评估“notesAt input 1.5”时,它必须计算
返回前“2 > 1.5”。但是事件 (2, NoteOff 42) 不会发生
再等0.5秒!所以我的输出取决于输入事件
将在 future 发生并因此停止。

我称这种效应为“矛盾的因果关系”。

我已经考虑了很长一段时间如何处理这个问题,我有
得出的结论是,我需要的是某种形式的数字
将允许我懒惰地评估“a > b”。比方说:
bar :: LazyNumber
bar = 1 + bar

foo :: Bool
foo = bar > 100

...然后我希望“foo”评估为 True。

请注意,您可以为此使用 Peano 数字,它确实有效。

但为了提高效率,我想将我的数字表示为:
data LazyNumber = MoreThan Double | Exactly Double

...而且这需要是可变的才能工作,即使每个功能都在
LazyNumbers(例如“>”)将是纯的......

在这一点上,我有点迷路了。所以问题是:有没有可能
实现有效的惰性数字来表示交互式时间
实时应用?

编辑

有人指出,我正在做的事情有一个名字:Functional Reactive Programming。 Edward Amsden 的论文“A Survey of Functional Reactive Programming”是一个很好的介绍。这是一个摘录:

Most FRP implementations, including all signal function implementations to date, succumb to continuous re-evaluation of event non-occurrences due to a "pull-based" implementation where a system continuously resamples the FRP expression for output. The work on Reactive (Sections 3.1 and 4.4) purports to solve this problem for Classic FRP, but extending this work to signal functions has not yet been explored, and the simple operation of occurrence time comparison relies on a programmer-checked and arguably difficult to prove identity to retain referential transparency.



这似乎是问题的核心:我的“虚拟事件”方法和 DarkOtter 的提议属于“对事件不发生的持续重新评估”类别。

作为一个天真的程序员,我说“让我们使用惰性数字,让 foo/bar 示例工作”。/我挥手。同时,我会看看YampaSynth。

此外,在我看来,像我试图做的那样,让数字相对于线性时间变得“懒惰”与让(实数)数字在精度方面“懒惰”密切相关(参见 Exact Real Arithmetic)。我的意思是,我们希望在严格的纯上下文中使用可变对象(事件时间的下限与实数的间隔),满足某些法律以确保我们“保持引用透明度”。更多的挥手,对不起。

最佳答案

你可以做一些这样的事情来实现(大致)最大延迟,我认为它可能已经在一些 FRP 程序中完成。我认为这个想法与您的建议类似,类型如下:

data Improving n = Greater n (Improving n) | Exact n

您可以为此定义各种方便的实例,例如comonad,但您所说的关键是您必须有一些方法,当任何IO进程正在等待下一个midi事件发生时,它立即产生你的一对,与时间和事件的懒惰 promise 。该事件仍将仅在实际事件发生时才可用,但应该捏造时间,以便在某个最大延迟后它的一部分始终可用。即,它等待 100 毫秒,然后如果事件发生,则惰性 thunk 变为 (Greater 100ms (thunk)),然后下一个 thunk 以相同的方式运行。这使您可以根据需要懒惰地交错。

我已经看到在旧版本的 FRP 库中使用 MVars 和 unsafeDupablePerformIO 的组合完成了类似的操作。这个想法是,您有一个 MVar,您的 IO monad 等待线程将其插入以发出该值的信号,并且您放入的 thunk 使用 unsafeDupablePerformIO 从 MVar 读取(这应该是线程安全的,并且是幂等的,所以它应该是安全的做我认为)。

然后,如果等待线程认为它太长,您只需为下一位创建另一个 MVar 和伴随的 thunk,然后将您的 (Greater (100ms) (thunk)) 值插入旧的,这允许在惰性中进行评估部分继续。

这并不完美,但这应该意味着你只需要等待,比如 future 100 毫秒而不是 500 毫秒。

如果您不想弄乱时间表示,我想您总是可以将midi事件流设为(时间,Maybe事件)流,然后确保生成的任何东西至少每x插入一次事件小姐。

编辑:

我在这里做了一个简单的例子: https://gist.github.com/4359477

关于haskell - 将时间建模为惰性数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14003098/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com