我只是没有得到延续!

Ode*_*ded 32 continuations functional-programming callcc

他们是什么,他们有什么好处?

我没有CS学位,我的背景是VB6 - > ASP - > ASP.NET/C#.任何人都能以清晰简洁的方式解释它吗?

Joh*_*kin 42

想象一下,如果程序中的每一行都是一个单独的函数.每个接受作为参数的下一行/要执行的函数.

使用此模型,您可以在任何行"暂停"执行并稍后继续执行.您还可以执行创造性操作,例如暂时跳过执行堆栈以检索值,或将当前执行状态保存到数据库以便稍后检索.

  • 我也喜欢它,但哪一部分恰好是延续? (4认同)

Dus*_*tin 11

你可能比你想象的更好地理解它们.

例外是"仅向上"延续的一个例子.它们允许堆栈内部的代码调用异常处理程序来指示问题.

Python示例:

try:
    broken_function()
except SomeException:
    # jump to here
    pass

def broken_function():
    raise SomeException() # go back up the stack
    # stuff that won't be evaluated
Run Code Online (Sandbox Code Playgroud)

生成器是"向下"延续的例子.它们允许代码重新进入循环,例如,创建新值.

Python示例:

def sequence_generator(i=1):
    while True:
        yield i  # "return" this value, and come back here for the next
        i = i + 1

g = sequence_generator()
while True:
    print g.next()
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,这些都必须特别添加到语言中,而在具有延续的语言中,程序员可以创建这些不可用的东西.


Kyl*_*nin 9

提醒一下,这个例子并不简洁,也不是特别清楚.这是对continuation的强大应用的演示.作为VB/ASP/C#程序员,您可能不熟悉系统堆栈或保存状态的概念,因此这个答案的目标是演示而不是解释.

Continuations非常通用,是一种保存执行状态并在以后恢复的方法.以下是使用Scheme中的continuation的协作多线程环境的一个小例子:

(假设操作在此处未定义的全局队列上按预期排队和出列工作)

(define (fork)
  (display "forking\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue (lambda ()
                (cc #f)))
     (cc #t))))

(define (context-switch)
  (display "context switching\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue
      (lambda ()
        (cc 'nothing)))
     ((dequeue)))))

(define (end-process)
  (display "ending process\n")
  (let ((proc (dequeue)))
    (if (eq? proc 'queue-empty)
        (display "all processes terminated\n")
        (proc))))
Run Code Online (Sandbox Code Playgroud)

这提供了一个函数可以使用的三个动词 - fork,context-switch和end-process.fork操作分叉线程并在一个实例中返回#t而在另一个实例中返回#f.上下文切换操作在线程之间切换,而end-process终止线程.

以下是它们的使用示例:

(define (test-cs)
  (display "entering test\n")
  (cond
    ((fork) (cond
              ((fork) (display "process 1\n")
                      (context-switch)
                      (display "process 1 again\n"))
              (else (display "process 2\n")
                    (end-process)
                    (display "you shouldn't see this (2)"))))
    (else (cond ((fork) (display "process 3\n")
                        (display "process 3 again\n")
                        (context-switch))
                (else (display "process 4\n")))))
  (context-switch)
  (display "ending process\n")
  (end-process)
  (display "process ended (should only see this once)\n"))
Run Code Online (Sandbox Code Playgroud)

输出应该是

entering test
forking
forking
process 1
context switching
forking
process 3
process 3 again
context switching
process 2
ending process
process 1 again
context switching
process 4
context switching
context switching
ending process
ending process
ending process
ending process
ending process
ending process
all processes terminated
process ended (should only see this once)
Run Code Online (Sandbox Code Playgroud)

那些在课堂上学习分叉和穿线的人经常会给出类似的例子.这篇文章的目的是证明通过延续,你可以在一个线程中通过手动保存和恢复其状态 - 它的延续 - 来获得类似的结果.

PS - 我想我在On Lisp中记得类似的东西,所以如果你想看专业代码,你应该检查一下这本书.


Pau*_*son 8

考虑延续的一种方法是作为处理器堆栈.当你"使用current-continuation c调用"时,它调用你的函数"c",传递给"c"的参数是你当前的堆栈,上面包含你所有的自动变量(表示为另一个函数,称之为"k") ").与此同时,处理器开始创建一个新的堆栈.当你调用"k"时,它会在原始堆栈上执行"从子程序返回"(RTS)指令,跳回原来的"call-with-current-continuation"(从现在起"call-cc")的上下文on)并允许您的程序像以前一样继续.如果您将参数传递给"k",那么这将成为"call-cc"的返回值.

从原始堆栈的角度来看,"call-cc"看起来像是一个普通的函数调用.从"c"的角度来看,您的原始堆栈看起来像一个永不返回的函数.

有一个关于数学家的老笑话,他通过爬进笼子,锁定它,并宣称自己在笼子外面,而其他一切(包括狮子)在里面捕获了一只笼子里的狮子.延续有点像笼子,"c"有点像数学家.你的主程序认为"c"在里面,而"c"认为你的主程序在"k"里面.

您可以使用continuation创建任意控制流结构.例如,您可以创建一个线程库."yield"使用"call-cc"将当前的continuation放在队列中,然后跳转到队列头部的那个.信号量也有自己的暂停连续队列,并通过将其从信号量队列中取出并将其放入主队列来重新安排线程.