所有这些switch
函数都是将信号函数更改为像另一个信号函数一样工作的方法。我个人觉得 Yampa 图有点难以解析,但是各种开关的类型签名很好地说明了如何理解它们。一旦你理解了类型签名,图表就会变得更加清晰。switch
本身就是最基本的:
switch :: SF a (b, Event c) -> (c -> SF a b) -> SF a b
如果我们查看类型,它会准确地告诉我们它做了什么:它需要一个 SFsf
和一个 SF 生成器sfg
。sf
产生 type 的值(b, Event c)
,而信号函数发生器的输入恰好也是 type 的值c
。因此,每当sf
' 事件发生时,SF 都会切换到 SF 生成器的结果。在事件发生之前,生成的 SF 将返回原始 SF 的值。
这个想法也用在rswitch
andkswitch
变体中,但有点不同。
rswitch
:这被称为“外部开关”,这意味着 SF 将在不对其输入或输出进行任何分析的情况下进行切换。让我们看一下类型签名:
rswitch :: SF a b -> SF (a, Event (SF a b)) b
它需要一个单一的 SF,作为 type 的输入值和 type 的a
输出值b
。rswitch
创建一个新的 SF,它也产生输出b
,但需要额外的 type 输入Event (SF a b)
。请注意,事件值的类型与输入类型匹配。这意味着每当事件发生时,这个 SF 都会切换到那个事件值。但是,SF 的类型仍然存在SF (a, Event (SF a b)) b
。这意味着 SF 可以自由地接收带有新 SF 的附加事件,这将影响整个 SF 的行为。一种用途可能是游戏中的 AI 行为:
moveFollowTarget :: SF TargetPosition Velocity
moveShootTarget :: SF TargetPosition Velocity
moveIdle :: SF TargetPosition Velocity
aiMovement :: SF (TargetPosition, Event (SF TargetPosition Velocity)) Velocity
aiMovement = rswitch moveIdle -- Initially idle...
aiMovementManager :: SF a (Event (SF TargetPosition Velocity))
aiMovementManager = ... whatever ...
在这里,aiMovementManager
只要 AI 的运动行为需要改变,就会触发一个事件,事件的值将是运动应该改变到的 SF。
kswitch
:这被称为intrinsic switch
,因为分析 SF 的内容以确定正确的开关应该是什么。让我们回顾一下类型签名
kswitch :: SF a b -> SF (a, b) (Event c) -> (SF a b -> c -> SF a b) -> SF a b
在这里,kswitch
接受三个参数,sf
,analyzer
和mapping
。sf
只是一个标准 SF,输入为 type a
,输出为 type b
。sf
是信号最初的行为方式。analyzer
是一个 SF,它接受输入和输出,sf
并且可能会或可能不会触发某种类型的事件,其值为 type c
。如果它没有触发事件,那么什么也不会发生,SF 继续表现得像sf
. 如果它确实触发了一个事件,那么两者sf
和事件值都会被传递给mapping
它来确定要切换到的新 SF。kswitch
在根据输出改变系统行为方式时很有用。
一个有用的例子是TCP Congestion Avoidance算法。在这里,我们看看我们是否丢失了网络数据包,并提高或降低了我们请求数据的速度。