Reactive.jl中的不直观行为(对我而言)

时间:2016-06-06 13:28:10

标签: julia reactive-programming

我的问题与反应包https://github.com/JuliaLang/Reactive.jl

有关

我已阅读tutorial并正在尝试,了解反应式编程方法。我尝试以下代码,它按预期工作:

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

正如预期的那样,这导致590和610之间的10个随机数被打印出来:

500
591
609
609
605
593
602
596
590
594

到目前为止,这么好。现在,假设我想在每次更新后收集信号z的输出,比如在矢量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

然而,在590和610之间有10个随机数的Vector c,c是一个包含500次值的Vector:

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

我试图了解这种行为是否是由我对Reactive编程无法理解的事情引起的;也许结合 for loops Signals 是不是没有?如果您对导致此行为的原因有所了解,我将不胜感激。

作为参考,我在IJulia笔记本中使用Julia 0.4.5。

1 个答案:

答案 0 :(得分:2)

Julia的Reactive.jl库旨在允许Julia中的Reactive programming。关键是只有信号是反应性的,它们要么独立,要么依赖于其他信号。在示例中,x是一个独立信号,信号的默认更新将调用push!z上的x是相关信号,这就是x更改时自动更新的原因。

现在,这是唯一的两个信号,请注意c被定义为Vector(),这不是信号,而是Julia中的正常数组。因此,在其上运行的任何代码只执行一次,就像所有非反应性语言一样。因此

for i in dar
    push!(x, i)
    push!(c, value(z))
end
在代码首次运行时,

只会分配到c一次,因此代码中z仍会保留500的默认值init=500。这具有直观意义。事实上,如果由c导致z发生变化,那么我们已经将Julia的基本行为变为反应性,这是不稳定的,因此是不可取的......

那么,只要c,我们如何更新z?正确的方法是一直使用反应式编程,因此c应该是z的相关信号。由于c维持z的状态,因此反应式编程中的正确结构为foldp,代表“折叠过去的值”。

有效的代码如下: -

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)

您将c成为z之前所有值的数组(除了初始值之外,所以如果您想要初始值也可以轻松完成)。请注意,反应式编程保留了类似的代码复杂性,但增加了反应性功能。这使代码更优雅,更易于维护。

评论中建议使用yield()Reactive.run_till_now(),因此我将略过解释。但我的意见是,如果你需要这样做,那么你可能没有正确使用被动编程,或者你的问题会更好地适应其他范例。

这就是我写的: -

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)

请注意,被动部分是自我描述的。现在z会在更新时自行打印,这是描述性而非强制性的。因此,即使更新是异步的,我们仍会捕获z的更新。推送到x的命令性代码本身就是模块化的。代码是高级别且易于阅读,没有低级例程在这样的高级脚本中传递yield()等函数。