14

在阅读了Eric Lippert 的回答后,我的印象是awaitcall/cc几乎是同一枚硬币的两个方面,最多有句法上的差异。然而,在尝试call/cc在 C# 5 中实际实现时,我遇到了一个问题:要么我误解了 call/cc(这很有可能),要么 await 只是让人想起call/cc。

考虑这样的伪代码:

function main:
    foo();
    print "Done"

function foo:
    var result = call/cc(bar);
    print "Result: " + result;

function bar(continuation):
    print "Before"
    continuation("stuff");
    print "After"

如果我对 call/cc 的理解是正确的,那么应该打印:

Before
Result: stuff
Done

至关重要的是,当调用 continuation 时,程序状态会与调用历史记录一起恢复,从而foo返回到main并且永远不会返回到bar

但是,如果await在 C# 中实现,则调用延续不会恢复此调用历史记录。foo返回bar,并且没有办法(我可以看到)await可用于使正确的通话记录成为延续的一部分。

请解释一下:我完全误解了 的操作call/cc,还是与 的操作await不太一样call/cc


既然我知道了答案,我不得不说有充分的理由认为它们非常相似。考虑一下上面的程序在伪 C#-5 中的样子:

function main:
    foo();
    print "Done"

async function foo:
    var result = await(bar);
    print "Result: " + result;

async function bar():
    print "Before"
    return "stuff";
    print "After"

因此,虽然 C# 5 风格从不给我们传递值的延续对象,但总体而言,相似性是相当惊人的。除了这次很明显“After”永远不会被调用,这与 true-call/cc 示例不同,这是喜欢 C# 并赞扬其设计的另一个原因!

4

1 回答 1

22

await确实不太一样call/cc

您正在考虑的那种完全基本call/cc的方法确实必须保存和恢复整个调用堆栈。但这await只是一个编译时转换。它做了类似的事情,但不使用真正的调用堆栈。

想象一下,你有一个包含 await 表达式的异步函数:

async Task<int> GetInt()
{
    var intermediate = await DoSomething();
    return calculation(intermediate);
}

await 现在想象你通过自身调用的函数包含一个await表达式:

async Task<int> DoSomething()
{
    var important = await DoSomethingImportant();
    return un(important);
}

现在想想当DoSomethingImportant()完成时会发生什么,结果是可用的。控制返回DoSomething()。然后DoSomething()完成,然后会发生什么?控制返回GetInt()该行为与在GetInt()调用堆栈上的行为完全相同。但事实并非如此。您必须await每次要以这种方式模拟的呼叫中使用。因此,调用堆栈被提升到在等待程序中实现的元调用堆栈。

顺便说一句,同样的情况也适用于yield return

IEnumerable<int> GetInts()
{
    foreach (var str in GetStrings())
        yield return computation(str);
}

IEnumerable<string> GetStrings()
{
    foreach (var stuff in GetStuffs())
        yield return computation(stuff);
}

现在,如果我调用GetInts(),我得到的是一个封装了当前执行状态的对象GetInts()(这样调用MoveNext()它可以恢复它停止的操作)。该对象本身包含迭代器GetStrings()调用MoveNext()它。因此,真正的调用堆栈被对象层次结构所取代,这些对象层次结构每次通过对下一个内部对象的一系列调用来重新创建正确的调用堆栈。MoveNext()

于 2012-03-22T16:48:39.463 回答