2016-10-18 3 views
0

"Wenn du den Schatz gefunden hast, hör auf zu graben!"Kurzschluss in funktionalem Groovy?

Ich möchte mehr funktionale Programmierung in Groovy verwenden, und dachte, die folgende Methode wäre ein gutes Training. Es ist schwieriger als es aussieht, weil Groovy anscheinend nicht in seine funktionaleren Funktionen kurzschließt.

Hier ist eine zwingende Funktion der Arbeit zu tun:

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 

Es scannt eine Liste mit vollqualifizierten Dateinamen und gibt die kürzeste einzigartige Form. Es gibt möglicherweise Hunderte vollständig qualifizierte Namen.

Sobald die Methode einen kurzen Namen mit nur einer Übereinstimmung findet, ist dieser kurze Name die richtige Antwort und die Iteration kann beendet werden. Es ist nicht notwendig, den Rest des Namens zu scannen oder teure Listensuchen durchzuführen.

Aber Hinwendung zu einem Funktionsablauf in Groovy, weder return noch break können Sie aus der Iteration fallen:

return einfach aus der gegenwärtigen Iteration zurückgibt, nicht von den ganzen .each, damit es nicht kurz- Schaltung.

break ist außerhalb einer Schleife nicht zulässig, und .each {} und .eachWithIndex {} werden nicht als Schleifenkonstrukte betrachtet.

Ich kann nicht .find() anstelle von .findAll() verwenden, weil meine Programmlogik erfordert, dass ich alle Elemente der Liste scanne, Nuss halt einfach an der ersten.

Es gibt viele Gründe, nicht try..catch Blöcke zu verwenden, aber das Beste, was ich gelesen habe, ist from here:

Ausnahmen grundsätzlich nicht lokale GOTO-Anweisungen mit allen Folgen der letzteren sind. Die Verwendung von Ausnahmen für die Flusskontrolle verletzt das Prinzip der geringsten Verwunderung, machen Programme schwer zu lesen (Denken Sie daran, dass Programme für Programmierer zuerst geschrieben werden).

Einige der üblichen Wege, dieses Problem sind detaillierte here einschließlich einer Lösung auf einen neuen Geschmack von .each basiert. Dies ist in der Nähe von einer Lösung, die ich bisher gefunden habe, aber ich brauche .eachWithIndex() für meinen Anwendungsfall verwenden

mein eigener schlechter Versuch hier zu einem Kurzschluss funktionellen Lösung (in Bearbeitung.):

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 

Bitte schießen Sie mich nieder, wenn es in Groovy einen idiomatischen Weg zum Kurzschluss gibt, an den ich nicht gedacht habe. Vielen Dank!

+0

Mögliches Duplikat [Wie kann man die Rückkehr von einem starken Verschluss und seine Ausführung stoppen?] (http://stackoverflow.com/questions/765605/how-does-one-return-from-a-groovy-closure-and-stop-its-execution) –

Antwort

0

Es ist wahrscheinlich ein saubere suchen (und leichter zu lesen) Lösung, aber man kann diese Art der Sache tun:

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