我正在使用 F# 规范中的最终工作流的修改版本在 Xbox 上进行开发。Xbox 上的 .net 框架似乎不支持尾调用。因此,我必须在编译时禁用尾调用优化。
尽管乍一看这种限制会阻止在计算表达式中使用任何形式的循环,但我最初认为“步进”会避免这个问题:计算表达式中的递归函数 f 不会直接调用自身,而是它返回包含调用 f 的 lambda 的最终值。
实验表明我对 while 循环的看法是正确的(在计算表达式中使用它们不会导致堆栈溢出),但对递归函数却不是。
为了澄清,这有效:
// Wait until "start" or "A" is pressed on one of the gamepads.
// Set "player" when that happens.
let player : PlayerIndex option ref = ref None
while (!player).IsNone do
for p in all_players do
let state = GamePad.GetState(p)
if state.IsConnected
&& (state.Buttons.Start = ButtonState.Pressed
|| state.Buttons.A = ButtonState.Pressed) then
player := Some p
do! sys.WaitNextFrame()
这会导致堆栈溢出:
// Wait until "start" is pressed on the controlling gamepad.
let rec wait() = task {
input.Update()
if not (input.IsStartPressed()) then
do! sys.WaitNextFrame()
do! wait()
}
在第二种情况下,堆栈跟踪显示对“bind@17-1”的一长串调用,其代码如下所示。堆栈跟踪中出现的行号是第 17 行。
let rec bind k e =
match e with
| Completed r ->
Running(fun () -> k r)
| Running f ->
Running(fun () -> f() |> bind k) // line 17
| Blocked(dt, f) ->
Blocked(dt, fun () -> f() |> bind k)
| BlockedNextFrame f ->
BlockedNextFrame(fun () -> f() |> bind k)
| Yield f ->
Yield(fun () -> f() |> bind k)
我哪里错了?我关于“可步进递归”是无害的 wrt 堆栈溢出的推理不正确吗?我的绑定实现有问题吗?
哦,我的头!递归的继续传递让我头疼......
编辑:“可步进递归是无害的 wrt 堆栈溢出”有一个名字,我刚刚了解到。它被称为蹦床。