2016-06-06 3 views
0

Meine Frage bezieht sich auf das Reactive Paket https://github.com/JuliaLang/Reactive.jlunintuitive Verhalten (für mich) in Reactive.jl

ich die tutorial gelesen und bin zu experimentieren, um den reaktiven Programmieransatz zu lernen. Ich versuche, den folgenden Code und es funktioniert wie erwartet:

using Reactive 

x = Signal(100) 
z = map(v -> v + 500, x; typ=Int64, init=500) 
dar = rand(90:110,10) 

for i in dar 
    push!(x, i) 
    println(value(z)) 
end 

Dies führt, wie erwartet, zu 10 Zufallszahlen betwwen 590 und 610 gedruckt werden:

500 
591 
609 
609 
605 
593 
602 
596 
590 
594 

So weit, so gut. Nun nehme ich an die Ausgänge des Signals z nach jedem Update sammeln möchten, sagen in einem Vektor c:

using Reactive 

x = Signal(100) 
z = map(v -> v + 500, x; typ=Int64, init=500) 
dar = rand(90:110,10) 
c = Vector() 

for i in dar 
    push!(x, i) 
    push!(c, value(z)) 
end 

Doch statt Vektor c mit zehn Zufallszahlen zwischen 590 und 610, c ist ein Vektor der umschließende Wert 500 zehnmal:

10-element Array{Any,1}: 
500 
500 
500 
500 
500 
500 
500 
500 
500 
500 

ich, wenn dieses Verhalten zu verstehen versuchen, durch etwas verursacht wird, ich Reactive Programmierung nicht verstehen; vielleicht kombiniert für Schleifen und Signale ist ein No-No? Ich würde mich über jeden Einblick freuen, was dieses Verhalten verursacht.

Als Referenz verwende ich Julia 0.4.5 in einem IJulia Notebook.

+0

Es ist keine Antwort, aber das Hinzufügen von 'yield()' zwischen 'push!' S ist eine minimale Änderung, die die Dinge funktionieren lässt. Die Aktualisierung von 'z's Wert geschieht in einer anderen Aufgabe. –

+0

Anstelle von 'yield()' verwende 'Reactive.run_till_now()' nach dem 'push!' Zum Signal. Dieser Tipp ist von https://github.com/JuliaLang/Reactive.jl/issues/99 –

+0

Das funktioniert, danke. Das scheint nicht so zu sein, aber das macht mehr Sinn. – pyrex

Antwort

2

Julias Reactive.jl Bibliothek ist entworfen, um Reactive programming in Julia zu ermöglichen. Die Crux ist, dass nur Signale reaktiv sind, und sie sind entweder unabhängig oder abhängig von anderen Signalen. Im Beispiel ist x ein unabhängiges Signal und die Standardaktualisierung für das Signal ruft push! auf. z ist abhängiges Signal auf x, weshalb es automatisch aktualisiert wird, wenn sich x ändert.

Nun sind dies die einzigen beiden Signale. Beachten Sie, dass c als Vector() definiert ist, was kein Signal, sondern ein normales Array in Julia ist. Jeder Code, der darauf ausgeführt wird, funktioniert nur einmal wie alle nicht reaktiven Sprachen. Deshalb

for i in dar 
    push!(x, i) 
    push!(c, value(z)) 
end 

wird nur c einmal vergeben, wenn der Code zum ersten Mal ausgeführt wird, wobei z noch den Standardwert 500 aufgrund init=500 im Code hält. Dies macht einen intuitiven Sinn. In der Tat wird, wenn c aufgrund geändert z, dann haben wir das zugrunde liegende Verhalten von Julia gemacht zu reaktiv, und dies ist flüchtig und so ist unerwünscht ...

Also, wie machen wir c Updates, wenn z tut? Der korrekte Weg ist, die reaktive Programmierung vollständig zu verwenden, und so sollte c ein abhängiges Signal von z sein. Da c den Zustand z beibehält, ist das korrekte Konstrukt in der reaktiven Programmierung foldp, was für "falten über vergangene Werte" steht.

Ein Code, der funktioniert, ist die folgende: -

using Reactive 

x = Signal(100) 
z = map(v -> v + 500, x) 
c = foldp((acc, value) -> push!(acc, value), Int[], z) 

for i in rand(90:110, 10) 
    push!(x, i) 
    yield() #Can also use Reactive.run_till_now() 
    println(value(z)) 
end 

@show value(c) 

Sie c erhalten die Array aller vorherigen Werte von z (mit Ausnahme der Anfangswert durch Wahl zu sein, wenn Sie also den Anfangswert wollen auch das kann leicht gemacht werden). Beachten Sie, dass die reaktive Programmierung eine ähnliche Codekomplexität beibehält, jedoch die reaktive Fähigkeit hinzufügt. Dies macht den Code eleganter und einfacher zu pflegen.

Mit yield() oder Reactive.run_till_now() ist wie im Kommentar empfohlen, so werde ich über die Erklärung skim. Aber meine Meinung ist, dass, wenn Sie das tun müssen, Sie wahrscheinlich reaktive Programmierung nicht richtig verwenden, oder Ihr Problem wird anderen Paradigma besser passen.

Dies ist, wie ich schreiben würde: -

using Reactive 

x = Signal(100) 

z = map(x) do v 
    result = v + 500 
    println(result) 
    return result 
end 

c = foldp(Int[], z) do acc, value 
    push!(acc, value) 
end 

for i in rand(90:110, 10) 
    push!(x, i) 
end 

@show value(c) 

Beachten Sie, dass der reaktive Teil der Beschreibung selbst ist. Jetzt z druckt sich selbst, wenn es aktualisiert wird, die beschreibende statt zwingend ist. Obwohl das Update asynchron ist, erfassen wir dennoch ein Update auf z. Der imperative Code des Schiebens zu x ist von selbst, Sachen modular haltend. Der Code ist hoch genug und leicht zu lesen, ohne Routinefunktionen wie yield() in einem solchen High-Level-Skript.

+0

Vielen Dank @ShaoWei für Ihre ausführliche und informative Antwort. Schätze, dass du dir die Zeit nimmst, es so gründlich zu erklären. – pyrex

+0

Sie sind herzlich willkommen @pyrex, Reactive.jl auf Julia ist ziemlich schön und es ist ein Vergnügen, darüber mit Ihnen zu teilen. –