Question

Je suis en train de saisir le concept de continuations et j'ai trouvé plusieurs petits enseignement exemples comme celui-ci à partir de la Article de 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))

Je comprends ce que cette petite fonction, mais je ne vois pas évident application.Alors que je ne m'attends pas à utiliser les continuations de tous les plus de mon code de sitôt, je souhaite que je connaissais un peu le cas où ils peuvent être appropriées.

Donc, je suis à la recherche pour plus explicitement utile exemples de code de ce que les poursuites ne peuvent me proposer en tant que programmeur.

Cheers!

Était-ce utile?

La solution

Dans Algo & Données II, nous avons utilisé ces tout le temps de "sortir" ou "retour" à partir d'un (long) fonction

par exemple, le BFS algorthm de traverser les arbres avec a été mis en œuvre comme ceci:

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

Comme vous pouvez le voir l'algorithme de sortie lorsque le nœud-découvert la fonction renvoie la valeur true:

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

la fonction permettra également de donner une "valeur de retour":"nœud"

pourquoi la fonction se termine, c'est parce que de cette déclaration:

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

lorsque nous utilisons la sortie, il va revenir à l'état d'avant l'exécution, vider la pile des appels et retour de la valeur que vous lui avez donné.

Donc, fondamentalement, appelez-cc est utilisé (ici) pour sauter d'une fonction récursive, au lieu d'attendre pour l'ensemble de la récursivité à la fin en elle-même (qui peut être très coûteux de faire beaucoup de travail de calcul)

un autre petit exemple de faire la même chose avec la 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))))

Autres conseils

@Pat

Bord de mer

Oui, Bord de mer est un excellent exemple.J'ai parcouru son code rapidement et a trouvé ce message illustrant de contrôle de transmission entre les composants dans une apparence de statefull chemin à travers le 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 ]

Tellement agréable!

J'ai construit ma propre unité de test de logiciels.Avant d'exécuter le test, je stocke la poursuite avant d'exécuter le test, puis en cas d'échec, j' (en option) raconter l'interpréteur scheme de tomber dans le mode de débogage, et de ré-invoquer la suite.De cette façon, je peux étape à travers la problématique de code très facilement.

Si votre continuations sont sérialisables, vous pouvez également les stocker sur défaillance de l'application, et puis de le ré-invoquer pour obtenir des informations détaillées sur les valeurs de la variable, les traces de pile, etc.

Les suites sont utilisés par certains serveurs web et les frameworks web pour stocker les informations de session.Une poursuite de l'objet est créée pour chaque session et ensuite utilisé par chaque requête au sein de la session.

Il y a un article au sujet de cette méthode ici.

Je suis tombé sur une mise en œuvre de la amb opérateur ce post à partir de http://www.randomhacks.net, à l'aide de suites.

Voici ce que l' amb opeerator n':

# 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 

Et voici le post de la mise en œuvre:

# 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

J'aime amb!

Poursuites ne peuvent être utilisés dans la "vie réelle" des exemples à chaque fois que le flux de programme n'est pas linéaire, ou même pas pré-déterminé.Une situation familière est les applications web.

Les suites sont une bonne alternative à fil-par-requête au serveur programmation (y compris les applications web frontaux.

Dans ce modèle, au lieu de lancer un nouveau (gros) thread à chaque fois qu'une requête arrive, vous venez de commencer à travailler dans une fonction.Ensuite, lorsque vous êtes prêt à bloc sur les I/O (c'est à direlecture à partir de la base de données), vous passez un prolongement dans la mise en réseau de la réponse du gestionnaire.Lorsque la réponse est de retour, vous exécutez la suite.Avec ce régime, vous pouvez traiter de nombreuses demandes avec juste quelques fils.

Cela rend le contrôle des flux plus complexe que l'utilisation de blocage des filets, mais sous une lourde charge, il est plus efficace (au moins sur le matériel d'aujourd'hui).

L'amb opérateur est un bon exemple qui permet prolog-comme la programmation déclarative.

Comme nous parlons je suis le codage d'un morceau de musique logiciel de composition dans le Schéma (je suis un musicien avec d'un côté pas de connaissances de la théorie derrière la musique, et je suis juste l'analyse de mes propres pièces pour voir comment les mathématiques derrière cela fonctionne.)

À l'aide de l'amb opérateur, je peux vous suffit de remplir les contraintes qui une mélodie doit satisfaire et laissez Schéma de la figure le résultat.

Les suites sont probablement mis dans le Système en raison de la langue de la philosophie, est un cadre permettant de réaliser sur toute paradigme de programmation trouvé dans d'autres langues par la définition de bibliothèques dans le Régime lui-même.Les suites sont pour faire vos propres abstraite des structures de contrôle comme le 'retour', 'pause' ou pour activer la programmation déclarative.Le schéma est plus "généraliser" et implique que de telles constructions doivent pouvoir être spécifié par le programmeur trop.

Comment au sujet de la Google Mapplets API?Il y a un tas de fonctions (tout se terminant dans Async) à qui vous transmettez un rappel.La fonction de l'API fait une demande asynchrone, il obtient un résultat, puis passe que suite à votre rappel (comme la "prochaine chose à faire").Cela ressemble beaucoup continuation passing style pour moi.

Cette exemple montre un cas très simple.

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

Comme c'est le Javascript il n'y a pas la queue d'appel d'optimisation, de sorte que la pile va grandir avec chaque appel en continuation, et vous finirez par revenir au thread de contrôle pour le navigateur.Tout de même, je pense que c'est une belle abstraction.

Si vous avez d'invoquer une action asynchrone, et de suspendre l'exécution jusqu'à ce que vous obtenez le résultat, vous le feriez normalement soit du sondage pour le résultat, ou de placer le reste de votre code dans une fonction de callback à exécuter par l'action asynchrone à la fin.Avec les continuations vous n'avez pas besoin de faire de la mauvaise option pour les bureaux de vote, et vous n'avez pas besoin d'emballer tous vos code à exécuter après l'asynch événement dans un rappel - vous venez de passer de l'état actuel du code de votre rappel - et le code est effectivement "réveillé" dès que le asynch terminée.

Poursuites ne peuvent être utilisés pour mettre en œuvre des exceptions, un débogueur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top