功能 Groovy 短路?

Short-circuiting in functional Groovy?

"When you've found the treasure, stop digging!"

我想在 Groovy 中使用更多的函数式编程,并且认为重写以下方法将是很好的训练。它比看起来更难,因为 Groovy 似乎没有在其更多功能特性中构建短路。

这是完成这项工作的必要函数:

fullyQualifiedNames = ['a/b/c/d/e', 'f/g/h/i/j', 'f/g/h/d/e']
String shortestUniqueName(String nameToShorten) {
    def currentLevel = 1
    String shortName = ''
    def separator = '/'
    while (fullyQualifiedNames.findAll { fqName ->
        shortName = nameToShorten.tokenize(separator)[-currentLevel..-1].join(separator)
        fqName.endsWith(shortName)
    }.size() > 1) {

        ++currentLevel

    }

    return shortName
}

println shortestUniqueName('a/b/c/d/e')

Result: c/d/e

它扫描完全限定的文件名列表和returns最短的唯一形式。可能有数百个完全限定名称。

只要该方法找到一个只有一个匹配项的短名称,该短名称就是正确答案,迭代可以停止。无需扫描名称的其余部分或执行任何更昂贵的列表搜索。

但是在 Groovy 中转向更实用的流程,returnbreak 都不能使您退出迭代:

return 只是 returns 来自当前迭代,而不是来自整个 .each 所以它不会短路。

break 不允许在循环外,并且 .each {}.eachWithIndex {} 不被视为循环结构。

我不能使用 .find() 而不是 .findAll() 因为我的程序逻辑要求我扫描列表的所有元素,坚果只是停在第一个。

有很多理由不使用 try..catch 块,但我读过的最好的是 from here:

Exceptions are basically non-local goto statements with all the consequences of the latter. Using exceptions for flow control violates the principle of least astonishment, make programs hard to read (remember that programs are written for programmers first).

详细介绍了解决此问题的一些常用方法 here,包括基于 .each 新风格的解决方案。这是迄今为止我找到的最接近的解决方案,但我需要对我的用例使用 .eachWithIndex()(进行中。)

这是我自己对短路功能解决方案的拙劣尝试:

fullyQualifiedNames = ['a/b/c/d/e', 'f/g/h/i/j', 'f/g/h/d/e']
def shortestUniqueName(String nameToShorten) {
    def found = ''
    def final separator = '/'
    def nameComponents = nameToShorten.tokenize(separator).reverse()
    nameComponents.eachWithIndex { String _, int i ->
        if (!found) {
            def candidate = nameComponents[0..i].reverse().join(separator)
            def matches = fullyQualifiedNames.findAll { String fqName ->
                fqName.endsWith candidate
            }
            if (matches.size() == 1) {
                found = candidate
            }
        }
    }
    return found
}

println shortestUniqueName('a/b/c/d/e')

Result: c/d/e

如果 Groovy 中有我没有想到的更惯用的短路方法,请打倒我。谢谢!

可能有更简洁(也更易于阅读)的解决方案,但您可以这样做:

String shortestUniqueName(String nameToShorten) {
    // Split the name to shorten, and make a list of all sequential combinations of elements 
    nameToShorten.split('/').reverse().inject([]) { agg, l ->
        if(agg) agg + [agg[-1] + l] else agg << [l]
    }
    // Starting with the smallest element
    .find { elements ->
        fullyQualifiedNames.findAll { name ->
            name.endsWith(elements.reverse().join('/'))
        }.size() == 1
    }
    ?.reverse()
    ?.join('/')
    ?: ''
}