“当你找到宝藏时,不要再挖了!”
我想在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中更具功能性的流程,return
和break
都不会让你退出迭代:
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中存在一种我没想过的更为惯用的短路方式,请拍下我。谢谢!
答案 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('/')
?: ''
}