Ruby 寻找“的例子”;“真正的”;延续的使用

Ruby 寻找“的例子”;“真正的”;延续的使用,ruby,language-agnostic,scheme,smalltalk,continuations,Ruby,Language Agnostic,Scheme,Smalltalk,Continuations,我试图理解延续性的概念,我从以下几个例子中找到了一些类似的小教学例子: 我理解这个小函数的作用,但我看不到它有任何明显的应用。虽然我不希望很快在我的代码中都使用continuations,但我希望知道一些合适的情况 因此,我正在寻找更明确有用的代码示例,以了解continuations可以为程序员提供什么 干杯 只要程序流不是线性的,甚至不是预先确定的,就可以在“现实生活”示例中使用延续。常见的情况是。一些web服务器和web框架使用Continuations来存储会话信息。为每个会话创建一个c

我试图理解延续性的概念,我从以下几个例子中找到了一些类似的小教学例子:

我理解这个小函数的作用,但我看不到它有任何明显的应用。虽然我不希望很快在我的代码中都使用continuations,但我希望知道一些合适的情况

因此,我正在寻找更明确有用的代码示例,以了解continuations可以为程序员提供什么


干杯

只要程序流不是线性的,甚至不是预先确定的,就可以在“现实生活”示例中使用延续。常见的情况是。

一些web服务器和web框架使用Continuations来存储会话信息。为每个会话创建一个continuation对象,然后由会话中的每个请求使用

海边:

    • @Pat

      海边

      是的,这是一个很好的例子。我快速浏览了它的代码,发现了这样一条信息,说明了在组件之间以一种似乎全状态的方式通过Web传递控制

      WAComponent >> call: aComponent
          "Pass control from the receiver to aComponent. The receiver will be
          temporarily replaced with aComponent. Code can return from here later
          on by sending #answer: to aComponent."
      
          ^ AnswerContinuation currentDo: [ :cc |
              self show: aComponent onAnswer: cc.
              WARenderNotification raiseSignal ]
      

      太好了

      在Algo&Data II中,我们一直使用这些函数从(长)函数中“退出”或“返回”

      例如,用于遍历树的BFS算法的实现方式如下:

      (define (BFS graph root-discovered node-discovered edge-discovered edge-bumped . nodes)
        (define visited (make-vector (graph.order graph) #f))
        (define q (queue.new))
        (define exit ())
        (define (BFS-tree node)
          (if (node-discovered node)
            (exit node))
          (graph.map-edges
           graph
           node
           (lambda (node2)
             (cond ((not (vector-ref visited node2))
                    (when (edge-discovered node node2)
                      (vector-set! visited node2 #t)
                      (queue.enqueue! q node2)))
                   (else
                    (edge-bumped node node2)))))
          (if (not (queue.empty? q))
            (BFS-tree (queue.serve! q))))
      
        (call-with-current-continuation
         (lambda (my-future)
           (set! exit my-future)
           (cond ((null? nodes)
                  (graph.map-nodes
                   graph
                   (lambda (node)
                     (when (not (vector-ref visited node))
                       (vector-set! visited node #t)
                       (root-discovered node)
                       (BFS-tree node)))))
                 (else
                  (let loop-nodes
                    ((node-list (car nodes)))
                    (vector-set! visited (car node-list) #t)
                    (root-discovered (car node-list))
                    (BFS-tree (car node-list))
                    (if (not (null? (cdr node-list)))
                      (loop-nodes (cdr node-list)))))))))
      
      如您所见,当发现的节点函数返回true时,算法将退出:

          (if (node-discovered node)
            (exit node))
      
      该函数还将给出一个“返回值”:“节点”

      函数退出的原因在于以下语句:

      (call-with-current-continuation
             (lambda (my-future)
               (set! exit my-future)
      
      当我们使用exit时,它将返回到执行前的状态,清空调用堆栈并返回您给它的值

      因此,基本上,调用cc(此处)用于跳出递归函数,而不是等待整个递归自行结束(这在执行大量计算工作时可能会非常昂贵)

      另一个较小的示例使用call cc执行相同的操作:

      (define (connected? g node1 node2)
        (define visited (make-vector (graph.order g) #f))
        (define return ())
        (define (connected-rec x y)
          (if (eq? x y)
            (return #t))
          (vector-set! visited x #t)
          (graph.map-edges g
                           x
                           (lambda (t)
                             (if (not (vector-ref visited t))
                               (connected-rec t y)))))
        (call-with-current-continuation
         (lambda (future)
           (set! return future)
           (connected-rec node1 node2)
           (return #f))))
      

      我在from中使用continuations实现了
      amb
      操作符

      以下是
      amb
      操作员的工作:

      # amb will (appear to) choose values
      # for x and y that prevent future
      # trouble.
      x = amb 1, 2, 3
      y = amb 4, 5, 6
      
      # Ooops! If x*y isn't 8, amb would
      # get angry.  You wouldn't like
      # amb when it's angry.
      amb if x*y != 8
      
      # Sure enough, x is 2 and y is 4.
      puts x, y 
      
      以下是《华盛顿邮报》的实施:

      # A list of places we can "rewind" to
      # if we encounter amb with no
      # arguments.
      $backtrack_points = []
      
      # Rewind to our most recent backtrack
      # point.
      def backtrack
        if $backtrack_points.empty?
          raise "Can't backtrack"
        else
          $backtrack_points.pop.call
        end
      end
      
      # Recursive implementation of the
      # amb operator.
      def amb *choices
        # Fail if we have no arguments.
        backtrack if choices.empty?
        callcc {|cc|
          # cc contains the "current
          # continuation".  When called,
          # it will make the program
          # rewind to the end of this block.
          $backtrack_points.push cc
      
          # Return our first argument.
          return choices[0]
        }
      
        # We only get here if we backtrack
        # using the stored value of cc,
        # above.  We call amb recursively
        # with the arguments we didn't use.
        amb *choices[1...choices.length]
      end
      
      # Backtracking beyond a call to cut
      # is strictly forbidden.
      def cut
        $backtrack_points = []
      end
      

      我喜欢amb

      我构建了自己的单元测试软件。在执行测试之前,我在执行测试之前存储延续,然后在失败时,我(可选)告诉scheme解释器进入调试模式,并重新调用延续。通过这种方式,我可以很容易地逐步完成有问题的代码


      如果您的continuations是可序列化的,那么您还可以在应用程序失败时存储它们,然后重新调用它们以获取有关变量值、堆栈跟踪等的详细信息。

      continuations是服务器编程(包括web应用程序前端)中每个请求线程的一个很好的替代方案

      在这个模型中,不是每次请求进来时都启动一个新的(沉重的)线程,而是在函数中开始一些工作,您将一个延续传递到网络响应处理程序。当响应返回时,您执行延续。使用此方案,您只需几个线程即可处理大量请求

      这使得控制流比使用阻塞线程更复杂,但在高负载下,它更高效(至少在今天的硬件上)。

      如何?有一系列函数(都以
      异步
      结尾)API函数执行一个异步请求,获取结果,然后将结果传递给回调函数(作为“下一步要做的事情”)。听起来很像

      这是一个非常简单的例子

      map.getZoomAsync(function(zoom) {
          alert("Current zoom level is " + zoom); // this is the continuation
      });  
      alert("This might happen before or after you see the zoom level message");
      

      由于这是Javascript,因此堆栈将随着每次调用而增长,成为一个延续,最终您将把控制线程返回到浏览器。尽管如此,我认为这是一个很好的抽象。

      延续可以用于实现异常,一个调试器。

      如果您必须调用异步操作,并挂起exe在得到结果之前,您通常会轮询结果或将其余代码放入回调中,在完成时由异步操作执行。使用continuations,您不需要执行效率低下的轮询选项,也不需要将所有代码打包,以便在回调中的异步事件后运行-您只需将代码的当前状态作为回调进行传递,一旦异步操作完成,代码就会被有效地“唤醒”。

      amb操作符就是一个很好的示例,它允许类似prolog的声明性编程

      正如我们所说,我正在用Scheme编写一个音乐创作软件(我是一个几乎不懂音乐背后的理论的音乐家,我只是在分析我自己的作品,看看它背后的数学原理是如何运作的。)

      使用amb操作符,我可以只填充旋律必须满足的约束,并让Scheme计算出结果


      Continuations可能被放入Scheme是因为语言哲学,Scheme是一个框架,通过在Scheme中定义库,您可以了解其他语言中的任何编程范式。Continuations用于构建您自己的抽象控制结构,如“return”、“break”或启用声明式编程Scheme更具“通用性”,并要求程序员也能指定此类结构。

      是的,Seaside是一个很好的例子,我在一个6个月的项目中使用了它!不过我正在寻找代码示例。
      map.getZoomAsync(function(zoom) {
          alert("Current zoom level is " + zoom); // this is the continuation
      });  
      alert("This might happen before or after you see the zoom level message");