2014-11-03 11 views
7

Ich habe einen Socket-Server, der auf einer Schleife zu akzeptieren Kunden ausgeführt werden muss, so dass ich herausgefunden, dass in funcional Programmierung, eine rekursive Schleife verwendet wird:F # Endlosschleifen in F #

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop()= 
     async { 
      let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token)) 
      printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString()) 
      Async.Start <| awaitMessages ws 
      do! loop()} 
    loop() 

Und dieser Code aufgerufen wird, by doing:

Angesichts der Tatsache, dass die App kontinuierlich ausgeführt wird, sollte ich stattdessen einen iterativen Ansatz verwenden? Erstellt der rec Ansatz geschachtelte Ausführungsstapel?

Auch ich möchte etwas, nachdem die Schleifenenden tun, wie:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop()= 
     async { 
      let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token)) 
      printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString()) 
      Async.Start <| awaitMessages ws 
      do! loop()} 
    loop() 
    printf "The loop ended" // <-- this line 

Aber dann kann es nicht, weil die awaitConnections Rückgabetyp kompilieren. Wie könnte ich das tun? Mache ich das richtig?

Antwort

17

Sie sind sicher auf dem richtigen Weg! Hier ist ein einfaches Beispiel, das zeigt, was Sie tun müssen:

// Loop that keeps running forever until an exception happens 
let rec loop() = async { 
    do! Async.Sleep(1000) 
    printfn "Working" 
    return! loop() } 

// Call the loop in a try .. finally block to run some cleanup code at the end 
let main() = async { 
    try 
    do! loop() 
    finally 
    printfn "That's it!" } 

// Start the work in the background and create a cancellation token 
let cts = new System.Threading.CancellationTokenSource() 
Async.Start(main(), cts.Token) 
// To cancel it, just call: cts.Cancel() 

Einige wichtige Punkte:

  • Sie können nicht wirklich Code nach einer Endlos-Schleife beendet laufen (es ist unendlich!), Aber sie kann try .. finally verwenden, um einige Code auszuführen, wenn der Block

  • Hinweis abgebrochen, dass return! für rekursive Looping mit besser ist - mit do! Speicherlecks erzeugt.

  • Sie können die Berechnung mit dem Abbruch-Token abbrechen - übergeben Sie das Token einfach beim Start.

+3

Danke! Wo finde ich weitere Informationen zu 'return!' vs. 'do!'? – vtortola

+1

Das ist eine geladene Frage, aber ich werde es versuchen. Berechnungsausdrücke wie Async sind syntaktischer Zucker für eine Reihe von Funktionsaufrufen, und das Rückgabeelement ist das Ergebnis der Funktionen. 'do!' definiert einen Ausdruck, der 'unit' zurückgibt, was (in diesem Fall) im Grunde ein neues' Async <'a> '-Objekt erzeugt, das nur zwischen einem rekursiven Aufruf und dem nächsten auftritt,' return! 'vermeidet dies. Zugegebenermaßen bedarf es einiger Überlegungen darüber, wie Async-Objekte hinter den Kulissen funktionieren, um alles möglich zu machen. –

+0

Danke, sehr hilfreich. – vtortola