功能Groovy中的短路?

时间:2016-10-18 23:22:48

标签: groovy functional-programming short-circuiting

“当你找到宝藏时,不要再挖了!”

我想在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

它扫描完全限定文件名列表并返回最短的唯一表单。可能有数百个完全合格的名称。

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

但是转向Groovy中更具功能性的流程,returnbreak都不会让你退出迭代:

return只是从当前迭代返回,而不是从整个.each返回,因此它不会短路。

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

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

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

  

异常基本上都是非本地goto语句   后者的后果。使用异常进行流量控制   违反了最不惊讶的原则,使程序难以阅读   (请记住,程序首先是为程序员编写的。)

围绕此问题的一些常用方法详细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中存在一种我没想过的更为惯用的短路方式,请拍下我。谢谢!

1 个答案:

答案 0 :(得分:0)

这可能是一个更清洁(更容易阅读)的解决方案,但你可以做这样的事情:

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('/')
    ?: ''
}