1

我想使用 Groovy 闭包来处理来自 SQL 表的数据。对于每个新行,计算将取决于先前计算的内容。但是,新行可能会在应用程序的进一步运行中变得可用,所以我希望能够重新加载闭包,并使用它在应用程序的上一次运行中最后一次执行闭包时的中间状态进行初始化。

例如,一个打算计算 3 行移动平均值的闭包可以这样实现:

def prev2Val = null
def prevVal = null
def prevId = null

Closure c = { row ->
    println([ prev2Val, prevVal, prevId])

    def latestVal = row['val']

    if (prev2Val != null) {
        def movMean = (prev2Val + prevVal + latestVal) / 3
        sql.execute("INSERT INTO output(id, val) VALUES (?, ?)", [prevId, movMean])
    }

    sql.execute("UPDATE test_data SET processed=TRUE WHERE id=?", [row['id']])

    prev2Val = prevVal
    prevVal = latestVal
    prevId = row['id']
}

test_data有 3 列:(id自动递增的主键)valueprocessed. 根据前两个值计算移动平均值并将其插入到output表中,与id前一行的值相对。已处理的行用 标记processed=TRUE

如果所有数据从一开始就可用,则可以这样调用:

sql.eachRow("SELECT id, val FROM test_data WHERE processed=FALSE ORDER BY id", c)

当应用程序运行后新行可用时,问题就出现了。这可以通过每次处理一小批来模拟(例如LIMIT 5在前面的语句中使用)。

我希望能够在执行结束时转储闭包的完整状态eachRow(例如,将中间数据保存在数据库中的某个位置)并在我重新运行整个应用程序时再次重新初始化它(通过加载那些来自数据库的中间变量)。

在这个特定示例中,我可以通过存储和的值来手动执行此操作prev2Val,但我正在寻找一个通用解决方案,其中不需要确切知道使用了哪些变量。prevValprevId

也许类似于c.getState()which 会返回[ prev2Val: 1, prevVal: 2, prevId: 6](例如),以及[ prev2Val: 1, prevVal: 2, prevId: 6]下次执行应用程序时我可以在哪里使用 c.setState() (如果存储了一个状态)。

我还需要sql从列表中排除。看来这可以使用c.@sql=null.

我意识到这在一般情况下不太可能起作用,但我正在寻找适合大多数情况的足够通用的东西。如本 Groovy 问题中所述,我已尝试dehydrate序列化和关闭,但我不确定如何在单个操作中保存和存储所有字段。rehydrate@

这可能吗?假设闭包使用的变量列表不一定事先知道,是否有更好的方法来记住执行之间的状态?

4

1 回答 1

3

不确定这是否会长期有效,您最好返回一个包含要传递给闭包的值的列表以获取下一组数据,但您可以询问闭包的绑定。

鉴于:

def closure = { row ->
  a = 1
  b = 2
  c = 4
}

如果你执行它:

closure( 1 )

然后,您可以编写如下函数:

def extractVarsFromClosure( Closure cl ) {
  cl.binding.variables.findAll { 
    !it.key.startsWith( '_' ) && it.key != 'args'
  }
}

执行时:

println extractVarsFromClosure( closure )

印刷:

['a':1, 'b':2, 'c':4]

但是,在本地绑定(没有 a )中定义的任何“自由”变量def也将在闭包绑定中,因此:

fish = 42
println extractVarsFromClosure( closure )

将打印:

['a':1, 'b':2, 'c':4, 'fish':42]

def fish = 42
println extractVarsFromClosure( closure )

不会打印值fish

于 2012-05-09T09:40:55.293 回答