Лучший шаблон для моделирования "продолжить" в Заводной закрытия


кажется, что Groovy не поддерживает break и continue из-за закрытия. Каков наилучший способ имитировать это?

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
         // continue to next line...
    }
}
6 66

6 ответов:

вы можете только поддерживать продолжать чисто, не ломать. Особенно такие вещи, как каждой строка и каждый. Невозможность поддержки разрыва связана с тем, как эти методы оцениваются, нет никакого рассмотрения для не завершения цикла, который может быть передан методу. Вот как поддержать продолжение --

лучший подход (предполагая, что вам не нужно результирующее значение).

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
        return // returns from the closure
    }
}

если ваш образец очень просто, это хорошо для удобочитаемость.

revs.eachLine { line -> 
    if (!(line ==~ /-{28}/)) {
        // do what you would normally do
    }
}

другой вариант, имитирует то, что continue обычно делает на уровне байт-кода.

revs.eachLine { line -> 
    while (true) {
        if (line ==~ /-{28}/) {
            break
        }
        // rest of normal code
        break
    }

}

один из возможных способов поддержки break - это исключения:

try {
    revs.eachLine { line -> 
        if (line ==~ /-{28}/) {
            throw new Exception("Break")
        }
    }
} catch (Exception e) { } // just drop the exception

вы можете использовать пользовательский тип исключения, чтобы избежать маскировки других реальных исключений, особенно если в этом классе происходит другая обработка, которая может вызывать реальные исключения, такие как NumberFormatExceptions или IOExceptions.

замыкания не могут прерываться или продолжаться, потому что они не являются конструкциями цикла/итерации. Вместо этого они являются инструментами, используемыми для обработки/интерпретации/обработки итерационной логики. Вы можете игнорировать данные итерации, просто возвращаясь из закрытия без обработки, как в:

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
            return
    }

}

поддержка разрыва не происходит на уровне закрытия, но вместо этого подразумевается семантикой вызова метода, принявшего закрытие. Короче говоря, это означает, что вместо вызова "каждый" на что-то вроде коллекции который предназначен для обработки всей коллекции, которую вы должны вызвать find, которая будет обрабатывать до тех пор, пока не будет выполнено определенное условие. Большинство (все?) раз вы чувствуете необходимость вырваться из закрытия то, что вы действительно хотите сделать, это найти определенное условие во время итерации, которое делает метод find соответствующим не только вашим логическим потребностям, но и вашим намерениям. К сожалению, некоторые API не поддерживают метод поиска... Например, файл. Вполне возможно, что все время, потраченное на споры, должен ли язык включить break / continue можно было бы хорошо потратить, добавив метод find к этим забытым областям. Что-то вроде firstDirMatching(Closure c) или findLineMatching(Closure c) пройдет долгий путь и ответит на 99+% "почему я не могу сломаться...?"вопросы, которые появляются в списках рассылки. Тем не менее, тривиально добавлять эти методы самостоятельно через метакласс или категории.

class FileSupport {
   public static String findLineMatching(File f, Closure c) {
      f.withInputStream {
         def r = new BufferedReader(new InputStreamReader(it))
         for(def l = r.readLine(); null!=l; l = r.readLine())
             if(c.call(l)) return l
         return null
      }
   }
}

using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }

другие хаки, связанные с исключениями и другой магии может работать, но ввести дополнительные накладные расходы в некоторых ситуации и свернуть читаемость в других. Истинный ответ-посмотреть на ваш код и спросить, действительно ли вы повторяете или ищете вместо этого.

Если вы предварительно создаете статический объект исключения в Java, а затем бросаете (статическое) исключение изнутри закрытия, стоимость времени выполнения минимальна. Реальная стоимость заключается в создании исключения, а не в его выбрасывании. По словам Мартина Одерского (изобретателя Scala), многие JVM могут фактически оптимизировать инструкции броска для одиночных прыжков.

Это можно использовать для имитации разрыва:

final static BREAK = new Exception();
//...
try {
  ... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }

использовать возвращение до дальше и любой закрыть перерыв.

пример

файл содержимое:

1
2
----------------------------
3
4
5

Groovy-код:

new FileReader('myfile.txt').any { line ->
    if (line =~ /-+/)
        return // continue

    println line

    if (line == "3")
        true // break
}

выход:

1
2
3

в этом случае, вы, вероятно, должны думать о find() метод. Он останавливается после того, как в первый раз закрытие прошло к нему вернуться true.

С rx-java вы можете преобразовать итератор в наблюдаемых.

затем вы можете заменить дальше С фильтр и перерыв С takeWhile

вот пример:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}