文字列 - ruby 配列




継続の「本当の」使用の例を探す (8)

@パット

シーサイド

はい、 シーサイドは素晴らしい例です。 私はそのコードをすばやくブラウズし、このメッセージが、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 ]

とても素敵!

私は継続の概念を把握しようとしており、 Wikipediaの記事からこのようないくつかの小さな教授の例が見つかりました:

(define the-continuation #f)

(define (test)
  (let ((i 0))
    ; call/cc calls its first function argument, passing 
    ; a continuation variable representing this point in
    ; the program as the argument to that function. 
    ;
    ; In this case, the function argument assigns that
    ; continuation to the variable the-continuation. 
    ;
    (call/cc (lambda (k) (set! the-continuation k)))
    ;
    ; The next time the-continuation is called, we start here.
    (set! i (+ i 1))
    i))

私はこの小さな機能が何をしているのか理解していますが、私はそれを明らかにすることはできません。 まもなく私のコード全体で継続を使用することを期待していませんが、私はそれらが適切である場合がいくつかあります。

だから私は、プログラマとして私に提供できる継続のコードサンプルをもっと明白に探しています。

乾杯!


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)))))))))

ご覧のように、node-discovered関数がtrueを返すとアルゴリズムは終了します:

    (if (node-discovered node)
      (exit node))

この関数は "戻り値"も返します: 'node'

関数がなぜ終了するのかは、次のステートメントのためです。

(call-with-current-continuation
       (lambda (my-future)
         (set! exit my-future)

exitを使用すると、実行前の状態に戻り、コールスタックを空にして、与えた値を返します。

ですから、基本的にcall-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))))

継続は、セッション情報を保存するためにいくつかのWebサーバーとWebフレームワークで使用されます。 継続オブジェクトが各セッションに対して作成され、セッション内の各要求によって使用されます。

このアプローチについての記事がここにあります。



私自身の単体テストソフトウェアを作った。 テストを実行する前に、テストを実行する前に継続を保存し、失敗した場合は、(オプションとして)スキームインタープリタにデバッグモードに移行し、継続を再度呼び出すよう指示します。 このようにして問題のあるコードを実際に簡単に進めることができます。

継続性がシリアライズ可能な場合は、アプリケーションの失敗を保存してから再呼び出しして、変数値、スタックトレースなどの詳細情報を取得することもできます。


継続は、サーバープログラミング(Webアプリケーションのフロントエンドを含む)の要求ごとのスレッドに代わる良い方法です。

このモデルでは、リクエストが来るたびに新しい(重い)スレッドを起動するのではなく、関数内でいくつかの作業を開始するだけです。 次に、I / O(データベースからの読み取り)をブロックする準備ができたら、ネットワーキング応答ハンドラに継続を渡します。 応答が戻ったら、継続を実行します。 このスキームでは、わずかなスレッドで多くの要求を処理できます。

これにより、ブロッキングスレッドを使用するよりも制御フローが複雑になりますが、負荷が高い場合は、少なくとも今日のハードウェアではより効率的です。


継続は、例外を実装するために使用することができます、デバッガ。


非同期アクションを起動し、結果を得るまで実行を中断する必要がある場合、通常は結果をポーリングするか、コードの残りをコールバックに入れて完了時に非同期アクションによって実行されます。 継続性を持たせることで、非効率的なポーリングのオプションを行う必要はなく、コールバックでasynchイベントの後に実行されるすべてのコードをまとめておく必要はありません。コードの現在の状態をコールバックとして渡すだけです - コードは、非同期アクションが完了するとすぐに効果的に "目を覚ます"。





continuations