Подтвердить что ты не робот

Как вернуться из закрытия groovy и остановить его выполнение?

Я хотел бы вернуться из закрытия, как если бы вы использовали оператор break в цикле.

Например:

largeListOfElements.each{ element->
    if(element == specificElement){
        // do some work          
        return // but this will only leave this iteration and start the next 
    }
}

В приведенном выше операторе if я хотел бы остановить повторение в списке и оставить закрытие, чтобы избежать ненужных итераций.

Я видел решение, в котором исключение выбрасывается в закрытие и выловлено на улицу, но я не слишком люблю это решение.

Есть ли какие-либо решения для этого, кроме изменения кода, чтобы избежать такого алгоритма?

4b9b3361

Ответ 1

Я думаю, вы хотите использовать find вместо каждого (по крайней мере, для указанного примера). Закрытие не поддерживает прямой перерыв.

Под обложками groovy фактически не использует закрытие для find, он использует цикл for.

В качестве альтернативы вы можете написать собственную расширенную версию find/each iterator, которая принимает закрытие условного теста, и другое замыкание для вызова, если найдено совпадение, если оно будет разорвано, если соответствие выполнено.

Вот пример:

Object.metaClass.eachBreak = { ifClosure, workClosure ->
    for (Iterator iter = delegate.iterator(); iter.hasNext();) {
        def value = iter.next()
        if (ifClosure.call(value)) {
            workClosure.call(value)
            break
        }        
    }
}

def a = ["foo", "bar", "baz", "qux"]

a.eachBreak( { it.startsWith("b") } ) {
    println "working on $it"
}

// prints "working on bar"

Ответ 2

Если вы хотите обработать все элементы до тех пор, пока не будет найден какой-либо конкретный объект, вы также можете сделать что-то вроде этого:

largeListOfElements.find { element ->
    // do some work
    element == specificElement
}

Хотя вы можете использовать это с любым "условием прерывания". Я просто использовал это для обработки первых n элементов коллекции, возвращая

counter++ >= n

в конце замыкания.

Ответ 3

Я думаю, вы работаете над неправильным уровнем абстракции. Блок .each выполняет именно то, что он говорит: он выполняет закрытие один раз для каждого элемента. Вместо этого вам нужно использовать List.indexOf, чтобы найти правильный specificElement, а затем выполнить работу, которую вам нужно сделать.

Ответ 4

Как я понимаю groovy, способ сокращения этих типов циклов заключается в том, чтобы выбросить определяемое пользователем исключение. Я не знаю, что будет синтаксисом (а не программистом grrovy), но groovy работает на JVM, поэтому было бы что-то вроде:

class ThisOne extends Exception {Object foo; ThisOne(Object foo) {this.foo=foo;}}

try { x.each{ if(it.isOk()) throw new ThisOne(it); false} }
catch(ThisOne x) { print x.foo + " is ok"; }     

Ответ 5

После ответа paulmurray я не был уверен, что произойдет с Исключением, выброшенным из замыкания, поэтому я взломал тестовый пример JUnit, о котором легко подумать:

class TestCaseForThrowingExceptionFromInsideClosure {

    @Test
    void testEearlyReturnViaException() {
        try {
            [ 'a', 'b', 'c', 'd' ].each {                 
                System.out.println(it)
                if (it == 'c') {
                    throw new Exception("Found c")
                } 
            }
        }
        catch (Exception exe) {
            System.out.println(exe.message)
        }
    }
}  

Вывод выше:

a
b
c
Found c

Но помните, что "НЕ следует использовать Исключения для управления потоком" , см., в частности, этот вопрос: Почему бы не использовать исключения в качестве регулярного потока контроль?

Таким образом, вышеприведенное решение в любом случае меньше идеала. Просто используйте:

class TestCaseForThrowingExceptionFromInsideClosure {

    @Test
    void testEarlyReturnViaFind() {
        def curSolution
        [ 'a', 'b', 'c', 'd' ].find {                 
            System.out.println(it)
            curSolution = it
            return (it == 'c') // if true is returned, find() stops
        }
        System.out.println("Found ${curSolution}")
    }
}  

Вывод выше также:

a
b
c
Found c