Frage

Ich habe gerade in Scala Programmierung , und ich habe gesucht in die Veränderungen zwischen Scala 2.7 und 2.8. Derjenige, der das wichtigste zu sein scheint, ist die Fortsetzungen Plugin, aber ich verstehe nicht, was es nützlich ist für oder wie es funktioniert. Ich habe die für asynchrones I / O, es ist gut zu sehen, aber ich habe nicht in der Lage gewesen, um herauszufinden, warum. Einige der beliebtesten Ressourcen zu diesem Thema sind diese:

Und diese Frage auf Stack-Überlauf:

Leider keine dieser Referenzen versuchen zu definieren, was Fortsetzungen sind oder was die Verschiebung / werden Reset-Funktionen tun soll, und ich habe keine Hinweise gefunden, die zu tun. Ich habe nicht in der Lage gewesen zu erraten, wie alle Beispiele in der verknüpften Artikel Arbeit (oder was sie tun), so eine Art und Weise, mir zu helfen sein könnte line-by-line zu gehen, durch eine diese Proben. Selbst diese einfachen aus dem dritten Artikel:

reset {
    ...
    shift { k: (Int=>Int) =>  // The continuation k will be the '_ + 1' below.
        k(7)
    } + 1
}
// Result: 8

Warum ist das Ergebnis 8? Das würde wahrscheinlich helfen Sie mir, um loszulegen.

War es hilfreich?

Lösung

Blog nicht erklären, was reset und shift tun, so dass Sie das noch einmal lesen möchten.

Eine weitere gute Quelle, die ich auch in meinem Blog zeigen, ist der Wikipedia-Eintrag auf Fortsetzung vorbei Stil . Dass man bei weitem der am deutlichsten zu diesem Thema, obwohl es nicht Scala Syntax verwenden, und die Fortsetzung explizit übergeben.

Das Papier, auf begrenzte Fortsetzungen, die ich in meinem Blog verlinken, aber scheint gebrochen geworden zu sein, gibt viele Beispiele der Nutzung.

Aber ich denke, das beste Beispiel für das Konzept von begrenzten Fortsetzungen ist Scala Swarm. Darin die Bibliothek stoppt die Ausführung des Codes an einem Punkt, und die verbleibende Berechnung der Fortsetzung wird. Die Bibliothek dann etwas tut -. In diesem Fall die Berechnung auf einen anderen Host übertragen, und gibt das Ergebnis (der Wert der Variablen auf die zugegriffen wurde) auf die Berechnung, die gestoppt wurde

Nun, die Sie nicht verstehen, auch das einfache Beispiel auf der Scala Seite, so nicht lesen Sie mein Blog. Darin bin ich nur die sich mit diesen Grundlagen zu erklären, warum das Ergebnis 8.

Andere Tipps

fand ich die bestehenden Erklärungen als weniger wirksam zu erklären, das Konzept als ich hoffen würde. Ich hoffe, das eine klar ist (und richtig sind.) Ich nicht Fortsetzungen noch verwendet haben.

Wenn eine Fortsetzung Funktion cf heißt:

  1. springt Ausführung über den Rest des shift Block und beginnt wieder am Ende der es
    • die Parameter auf cf vergangen ist, was der shift Block „auswertet“ als die Ausführung fortgesetzt. dies kann für jeden Anruf zu cf unterschiedlich sein
  2. Die Ausführung wird fortgesetzt, bis das Ende des reset Block (oder bis ein Anruf reset wenn kein Block)
    • das Ergebnis des reset Block (oder den Parameter reset (), wenn kein Block) ist das, was cf kehrt
  3. Die Ausführung wird fortgesetzt, nachdem cf bis zum Ende des shift Block
  4. Die Ausführung springt bis zum Ende des reset Block (oder einen Anruf zurücksetzen?)

So in diesem Beispiel folgt den Buchstaben von A bis Z

reset {
  // A
  shift { cf: (Int=>Int) =>
    // B
    val eleven = cf(10)
    // E
    println(eleven)
    val oneHundredOne = cf(100)
    // H
    println(oneHundredOne)
    oneHundredOne
  }
  // C execution continues here with the 10 as the context
  // F execution continues here with 100
  + 1
  // D 10.+(1) has been executed - 11 is returned from cf which gets assigned to eleven
  // G 100.+(1) has been executed and 101 is returned and assigned to oneHundredOne
}
// I

Diese Ausgabe:

11
101

Da das kanonische Beispiel aus dem Forschungsarbeit Scala begrenzt Fortsetzungen, leicht modifiziert, so dass die Funktion eingegeben shift den f Name gegeben wird und somit nicht mehr anonym.

def f(k: Int => Int): Int = k(k(k(7)))
reset(
  shift(f) + 1   // replace from here down with `f(k)` and move to `k`
) * 2

Die Scala Plugin Transformationen dieses Beispiel derart, dass die Berechnung (innerhalb des Eingabearguments der reset) von jedem shift zum Aufruf von reset Ausgang ist ersetzt mit der Funktion (zB f) -Eingang zu shift.

Die Berechnung ersetzt ist verschoben (d.h. bewegt) in eine Funktion k. Die Funktion f Eingänge der Funktion k, wo k enthält die ersetzt Berechnung, k Eingänge x: Int, und die Berechnung in k ersetzt mit shift(f) x.

f(k) * 2
def k(x: Int): Int = x + 1

, die die gleiche Wirkung hat wie:

k(k(k(7))) * 2
def k(x: Int): Int = x + 1

Hinweis des Typ Int den Eingangsparameter x (das heißt, die Art der Unterschrift k) durch die Art Signatur des Eingangsparameters f gegeben wurde.

Ein weiterer entlehnt Beispiel mit der konzeptionell äquivalenten Abstraktion, dh read ist die Funktion Eingang shift:

def read(callback: Byte => Unit): Unit = myCallback = callback
reset {
  val byte = "byte"

  val byte1 = shift(read)   // replace from here with `read(callback)` and move to `callback`
  println(byte + "1 = " + byte1)
  val byte2 = shift(read)   // replace from here with `read(callback)` and move to `callback`
  println(byte + "2 = " + byte2)
}

Ich glaube, das von auf den logischen äquivalent übersetzt werden würde:

val byte = "byte"

read(callback)
def callback(x: Byte): Unit {
  val byte1 = x
  println(byte + "1 = " + byte1)
  read(callback2)
  def callback2(x: Byte): Unit {
    val byte2 = x
    println(byte + "2 = " + byte1)
  }
}

Ich hoffe, das die kohärente gemeinsame Abstraktion erläutert, die durch vorherige Vorlage dieser beiden Beispiele etwas verschleiert wurde. So wurde zum Beispiel die kanonische erste Beispiel in der Forschungspapier als eine anonyme Funktion, statt meiner namens f, so wurde es sofort nicht für einige Leser klar, dass es abstrakt analog den read im zweites Beispiel entlehnt.

So begrenzt Fortsetzungen erzeugen die Illusion einer Inversion of Control von „rufen Sie mich von außerhalb reset“ bis „Ich rufe Sie in reset“.

Beachten Sie den Rückgabetyp f, aber k ist nicht erforderlich, das gleiches wie der Rückgabetyp reset sein, dh f hat die Freiheit, jeden Rückgabetyp für k solange f kehren vom gleichen Typ wie reset zu erklären . Das Gleiche gilt für read und capture (siehe auch ENV unten).


Delimited Fortsetzungen nicht implizit invertieren die Kontrolle staatlicher, z.B. read und callback sind keine reinen Funktionen. So kann der Anrufer nicht referentiell transparent Ausdrücke erstellen und somit nicht deklarativen hat (auch bekannt als transparent) Kontrolle über die beabsichtigten Imperativ Semantik .

Wir können explizit reine Funktionen mit begrenzten Fortsetzungen erreichen.

def aread(env: ENV): Tuple2[Byte,ENV] {
  def read(callback: Tuple2[Byte,ENV] => ENV): ENV = env.myCallback(callback)
  shift(read)
}
def pure(val env: ENV): ENV {
  reset {
    val (byte1, env) = aread(env)
    val env = env.println("byte1 = " + byte1)
    val (byte2, env) = aread(env)
    val env = env.println("byte2 = " + byte2)
  }
}

Ich glaube, das von auf den logischen äquivalent übersetzt werden würde:

def read(callback: Tuple2[Byte,ENV] => ENV, env: ENV): ENV =
  env.myCallback(callback)
def pure(val env: ENV): ENV {
  read(callback,env)
  def callback(x: Tuple2[Byte,ENV]): ENV {
    val (byte1, env) = x
    val env = env.println("byte1 = " + byte1)
    read(callback2,env)
    def callback2(x: Tuple2[Byte,ENV]): ENV {
      val (byte2, env) = x
      val env = env.println("byte2 = " + byte2)
    }
  }
}

Dies ist immer laut, wegen der expliziten Umgebung.

Tangential Anmerkung, ist Scala nicht Haskells globale Typinferenz hat und damit so weit ich weiß nicht, implizite Aufhebung zu einem Zustand Monade unit (als eine mögliche Strategie, um die explizite Umgebung für das Verstecken) unterstützen könnte, weil Haskells global (Hindley- Milner) Typinferenz hängt von nicht Diamant mehrere virtuelle Vererbung unterstützen.

Fortsetzung Erfassung der Zustand einer Berechnung, die später aufgerufen werden.

Denken Sie an der Berechnung zwischen dem Schalt Ausdruck zu verlassen und dem Reset-Ausdruck als eine Funktion zu verlassen. Innerhalb der Verschiebung Ausdruck dieser Funktion k genannt wird, ist es die Fortsetzung. Sie können es um passieren, rufen Sie es später noch mehr als einmal.

Ich denke, der Wert durch den Reset-Ausdruck zurückgegeben wird, ist der Wert des Ausdrucks in der Verschiebung Ausdruck nach dem =>, aber darüber bin ich nicht ganz sicher.

So mit Fortsetzungen können Sie ein eher willkürlich und nicht-lokales Stück Code in einer Funktion einpacken. Dies kann dazu verwendet werden, Nicht-Standard-Steuerungsablauf zu implementieren, wie coroutining oder Rückzieher.

sollten also Fortsetzungen auf Systemebene verwendet werden. sie durch Ihren Anwendungscode Beregnung wäre ein sicheres Rezept für Alpträume, viel schlimmer als der schlimmste Spaghetti-Code ginge mit je sein könnte.

Disclaimer:. ich nicht in der Tiefe haben das Verständnis von Fortsetzungen in Scala, ich kann es nur gefolgert aus der Betrachtung der Beispiele und Fortsetzungen von Schema zu wissen,

Aus meiner Sicht war die beste Erklärung, die hier gegeben: http : //jim-mcbeath.blogspot.ru/2010/08/delimited-continuations.html

Ein Beispiel:

  

Um zu sehen, die Steuerung ein wenig deutlich mehr fließen, können Sie dies ausführen können   Code-Schnipsel:

reset {
    println("A")
    shift { k1: (Unit=>Unit) =>
        println("B")
        k1()
        println("C")
    }
    println("D")
    shift { k2: (Unit=>Unit) =>
        println("E")
        k2()
        println("F")
    }
    println("G")
}
  

Hier ist die Ausgabe der obige Code erzeugt:

A
B
D
E
G
F
C

Eine andere (jüngere - Mai 2016) Artikel über die Scala Fortsetzungen ist:
Time Travel in Scala: CPS in Scala (Scalas Fortsetzung) “durch Shivansh Srivastava (shiv4nsh) .
Es bezieht sich auch auf Jim McBeath 's Artikel in Dmitry Bespalov erwähnt 's Antwort .

Aber vorher, es beschreibt Fortsetzungen wie folgt:

  

Eine Fortsetzung ist eine abstrakte Darstellung des Steuerzustand eines Computerprogramms .
  Also, was es eigentlich bedeutet, dass es sich um eine Datenstruktur, die den Rechenprozess zu einem bestimmten Punkt stellt in der Ausführung des Prozesses; die erzeugte Datenstruktur kann durch die Programmiersprache zugegriffen werden, statt in der Laufzeitumgebung verborgen ist.

     

Um es zu erklären, weiter wir eine der klassischen Beispiel haben kann,

     

Sprich du ist in der Küche vor dem Kühlschrank, darüber nachzudenken, ein Sandwich. Sie nehmen eine Fortsetzung genau dort und halten Sie es in Ihrer Tasche.
  Dann erhalten Sie etwas Truthahn und Brot aus dem Kühlschrank und machen Sie sich ein Sandwich, das jetzt auf der Theke sitzt.
  Sie berufen sich auf die Fortsetzung in der Tasche, und Sie finden sich vor dem Kühlschrank stehen wieder, darüber nachzudenken, ein Sandwich. Aber zum Glück gibt es ein Sandwich auf der Theke, und alle Materialien verwendet, um sie weg sind zu machen. So man es essen. : -)

     

In dieser Beschreibung der sandwich Teil der Programmdaten (zB ein Objekt auf dem Heap), und anstatt eine „make sandwich“ Routine aufrufen und dann zurückkehrte, rief die Person ein „ make sandwich with current continuation“Routine, die das Sandwich erzeugt und fährt dann fort, wo die Ausführung unterbrochen wird.

Das wird gesagt, wie in April 2014 für Scala 2.11.0-RC1

  

Wir suchen Maintainer zu übernehmen, die folgenden Module: scala-Schaukel , < a href = "https://github.com/scala/scala-continuations" rel = "nofollow noreferrer"> scala-Fortsetzungen .
   2.12 schließt sie nicht, wenn keine neuen Maintainer gefunden .
  Wir werden wahrscheinlich halten Sie die anderen Module (scala-xml, scala-Parser-Kombinatoren) beibehalten wird, aber Hilfe ist immer noch sehr geschätzt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top