【问题标题】:Lazy GString evaluation in Groovy closuresGroovy 闭包中的惰性 GString 评估
【发布时间】:2017-04-07 11:44:57
【问题描述】:

我试图理解为什么在下面的 sn-p 中,如果 GString 是在闭包内创建的,它会被很好地评估,但如果我尝试在外部创建字符串并尝试在闭包内对其进行评估,则会抛出异常:

map1 = ['foo': 1, 'bar': 2]
map2 = ['foo': 3, 'bar': 4]

dynamicallyGeneratedString = "key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}"

map1.each { key1, value1 ->
    map2.each { key2, value2 ->
        println "key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}" // works as expected
        // println dynamicallyGeneratedString // throws MissingPropertyException
    }
}

两种情况下的期望输出都是:

key1: foo, val1: 1, key2: foo, val2: 3
key1: foo, val1: 1, key2: bar, val2: 4
key1: bar, val1: 2, key2: foo, val2: 3
key1: bar, val1: 2, key2: bar, val2: 4

我的目标是根据其他一些条件动态生成一个字符串,然后在遍历地图时懒惰地评估其内容。

这是一种有效的方法吗?

【问题讨论】:

  • 不确定是否可行...您可以使用模板引擎吗?为什么字符串的定义需要远离它的用法?
  • 只是因为我使用了一些 if 和 else 来组装字符串,并且我不知何故认为只创建一次会更容易,而不是测试每次迭代的条件。但也许我不应该考虑在这一步进行优化?!
  • 听起来像是模板的案例?

标签: groovy closures lazy-evaluation gstring


【解决方案1】:

除了使用@Vampire 建议的模板之外,我还可以想到两种解决任务的替代方法。

  • 在闭包内重新分配变量:

    map1 = ['foo': 1, 'bar': 2]
    map2 = ['foo': 3, 'bar': 4]
    
    def k1, v1, k2, v2
    dynamicString = "key1: ${->k1}, val1: ${->v1}, key2: ${->k2}, val2: ${->v2}"
    
    map1.each { key1, value1 ->
        map2.each { key2, value2 ->
            k1 = key1
            v1 = value1
            k2 = key2
            v2 = value2
            println dynamicString
        }
    }
    
  • 功能评估:

    map1 = ['foo': 1, 'bar': 2]
    map2 = ['foo': 3, 'bar': 4]
    
    def myfunc(key1, value1, key2, value2) {
        dynamicallyGeneratedString = "key1: ${key1}, val1: ${value1}, key2: ${key2}, val2: ${value2}"
    }
    
    map1.each { key1, value1 ->
        map2.each { key2, value2 ->
            println myfunc(key1, value1, key2, value2)
        }
    }
    

我想这只是口味问题...(或者我缺少任何性能考虑因素?)

【讨论】:

    【解决方案2】:

    问题是,当您创建 GString 时,它会存储对变量的引用。然后,当您尝试对其进行评估时,这些引用指向任何内容,您会得到异常。

    如果你真的想这样做,我认为你必须使用像 with 这样的模板引擎

    println new groovy.text.GStringTemplateEngine().createTemplate(dynamicallyGeneratedString).make(key1: key1, value1: value1, key2: key2, value2: value2)
    

    【讨论】:

      【解决方案3】:

      聚会有点晚了,但它来了。

      我编写了以下类,我用它来动态构建 SQL 查询而不牺牲安全性,因为 fill 的结果是一个 GStringImpl 实例并且 groovy.sql.Sql 正确地转换了它进入参数化的数据库查询。

      我不确定 Closure 的 call 方法是否是线程安全的(因为我正在设置 delegate 属性)所以我添加了 synchronized 到填充方法。

      class GTemplate {
      
          def compiledTemplate
      
          GTemplate(String templateSource) {
              compiledTemplate = new GroovyShell().evaluate('{-> """' + escape(templateSource) + '""" }')
          }
      
          def static GTemplate compile(String templateSource) {
              return new GTemplate(templateSource)
          }
      
          def synchronized fill(def args) {
              compiledTemplate.delegate = args
              return compiledTemplate.call()
          }
      
          def synchronized fill(Map<?,?> args) {
              compiledTemplate.delegate = args
              return compiledTemplate.call()
          }
      
          private static String escape(String str) {
              StringBuilder buf = new StringBuilder()
              for(char c : str) {
                  if ((c == '"') || (c == '\\'))
                      buf.append('\\')
                  buf.append(c)
              }
              return buf.toString()
          }
      }
      
      map1 = ['foo': 1, 'bar': 2]
      map2 = ['foo': 3, 'bar': 4]
      
      dynamicallyGeneratedString = GTemplate.compile('key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}')
      
      map1.each { key1, value1 ->
          map2.each { key2, value2 ->
              println dynamicallyGeneratedString.fill(key1: key1, value1: value1, key2: key2, value2: value2)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-17
        • 2015-05-29
        • 1970-01-01
        • 2014-02-08
        • 1970-01-01
        • 2013-03-11
        • 2017-01-18
        相关资源
        最近更新 更多