在 groovy 中查找闭包的自由变量列表/映射

Finding list/map of free variable(s) of a closure in groovy

提问人:stdout 提问时间:10/16/2018 更新时间:10/17/2018 访问量:831

问:

这是我简单时髦的脚本;

def fourtify(String str) {

    def clsr = {
         str*4
    }

    return clsr
}

def c = fourtify("aa")
println("binding variables: ${c.getBinding().getVariables()}")
...

我在这里要做的就是能够使用闭包实例访问自由变量“”,以更好地理解闭包在幕后的工作方式。也许就像 Python 的方法一样。strlocals()

有没有办法做到这一点?

Groovy 属性 闭包 free-variable

评论


答:

1赞 Szymon Stepniak 10/17/2018 #1

您定义的闭包不会在 object 中存储任何内容 - 它只是返回作为变量传递的 String,重复 4 次。bindingstr

此对象存储定义的所有变量,但未指定其类型或使用关键字。它是通过Groovy元编程功能(以及更具体的方法)完成的。因此,当您定义一个变量时,例如:bindingdefgetPropertysetPropertys

def clsr = {
     s = str*4
     return s
}

然后,此闭包将创建一个绑定,其中包含从表达式中计算的键和值。此绑定对象只不过是通过 和 方法访问的映射。因此,当 Groovy 执行时,它会调用,因为没有定义变量/属性。如果我们做一个稍微简单的改变,比如:sstr * 4getPropertysetPropertys = str * 4setProperty('s', str * 4)s

def clsr = {
     def s = str*4 // or String s = str * 4
     return s
}

则不会创建绑定,因为不会执行方法。ssetProperty

对你的例子的另一条评论。如果要查看绑定对象中的任何内容,则需要调用返回的闭包。在上面的示例中,您已经显示了闭包被返回,但它永远不会被调用。如果这样做:

def c = fourtify("aa")
c.call()
println("binding variables: ${c.getBinding().getVariables()}")

然后调用您的闭包,绑定对象将包含绑定(如果有任何集)。现在,如果您将示例修改为如下所示:

def fourtify(String str) {

    def clsr = {
        def n = 4 // it does not get stored as binding
        s = str * n
        return s
    }

    return clsr
}

def c = fourtify("aa")
c.call()
println("binding variables: ${c.getBinding().getVariables()}")

您将看到以下输出作为回报:

binding variables: [args:[], s:aaaaaaaa]

希望对你有所帮助。

评论

0赞 stdout 10/17/2018
我明白你的意思,但我仍然不明白为什么我在任何地方都看不到“free”变量“str”——即使在我删除了 String 类型之后?我的观点是,在编译脚本时,变量“str”应该存储在某个地方,这样当我在此过程中调用闭包时,它可以通过查看例如所有者或委托或其他方式来解析该“str”名称。希望它更有意义。
1赞 daggett 10/17/2018 #2

在您的示例中是方法/函数的参数strfortify

但是,也许以下示例会让您更好地理解 Closure

def c={ String s,int x-> return s*x }

println( c.getClass().getSuperclass() )     // groovy.lang.Closure
println( c.getMaximumNumberOfParameters() ) // 2
println( c.getParameterTypes() )            // [class java.lang.String, int]

Python 的函数更好地匹配 groovy.lang.Script.getBinding()locals()

下面是一个带有脚本的简单示例:

Script scr = new GroovyShell().parse(''' 
    println this.getBinding().getVariables()  // print "s" and "x"
    z = s*(x+1)                               // declare a new script-level var "z"
    println this.getBinding().getVariables()  // print "s", "x", and "z"
    return s*x 
''')
scr.setBinding( new Binding([
        "s":"ab",
        "x":4
    ]) )
println scr.run() // abababab
println scr.getBinding().getVariables() // print "s", "x", and "z"

评论

0赞 stdout 10/17/2018
这就是我一开始的想法,这就是为什么我尝试在闭包实例上使用 getBinding() 方法获取变量(包括“免费”变量)的原因。此外,我更感兴趣的是获取“自由”变量,一旦编译脚本,闭包就会关闭,而不是检索有关参数的信息。
0赞 daggett 10/17/2018
@zgulser,没有用于闭包的“自由”变量。有了,您可以访问声明此闭包的对象,并使用 Groovy Metaclass : 或 Java Native 请求声明的类字段。但是,您无法访问在方法/函数中声明的局部变量。delegatedelegate.metaClass.properties.collect{it.name}delegate.getClass().getDeclaredFields()
0赞 stdout 10/17/2018
我不认为这完全正确——groovy-lang.org/closures.html。我知道 Groovy 中的委托概念,但就我而言,我认为“str”不能是任何委托的一部分,因为它是函数的局部变量。
1赞 daggett 10/17/2018
这就是我想说的。在您的示例中无法获得它。除了您可能想使用 AstBuilder 来分析代码......str