Groovy Closures: искусство компактности
В языке Groovy помимо стандартных полей и методов существуют т.н. Closures. Средство это очень нетипичное для классического программирования, но чрезвычайно эффективное. Рассмотрим простой пример: нужно превратить «плохой» html-код вида
в «хороший»
Вот как это делается (результат окажется в переменной goodHtml):
В данном примере closure — это то, что заключено в фигурные скобки. В круглых скобках происходит разбивка строки на фрагменты, соответствующие регулярному выражению .*?<br>. Для каждого такого фрагмента и выполняется алгоритм в фигурных скобках. Об этом свидетельствует оператор each.
Регулярное выражение заключено в слэши, которые применяются вместо кавычек, чтобы избежать экранирования, т.е. вместо /.*?<br>/ можно было бы записать ".*?<br>". Но о регекспах в Groovy можно написать целую поэму, так что оставим это на следующий раз. А сейчас вернемся к closures.
Closure — это нечто среднее между методами и операторами циклов. Строго говоря, это, конечно же, методы (а может даже анонимные классы, если копнуть глубже). Однако с точки зрения синтаксиса пишутся так же легко, как циклы. Closures можно писать «по месту» (как было сделано в вышеприведенном примере) или объявлять и именовать, как методы. Например, некоторое время назад я писал о копировании потока в поток средствами Java. Вот как то же самое звучит в Groovy с помощью closures:
Вот как с помощью этого скопировать один файл в другой:
По сути очень похоже на стандартное объявление метода и его вызов, только «букаф меньше». Во-первых, пропущен тип возвращаемого значения. Во-вторых, аргументы, передаваемые методу, перечислены до знака -> (входной поток is и выходной os), что выглядит изящнее. Если для closure аргументы не перечислены, но внутри его идет какой-то перебор в цикле, то к перечисляемым элементам можно обращаться с помощью универсальной переменной it (см. первый пример).
Может этот экскурс в мир closures и слишком краток, но по крайней мере примеры кода позволяют оценить эффективность этого средства Groovy: Java-листинг первого фрагмента (замена строк с помощью регулярных выражений) был бы раз в 5 длиннее.
Строка 1<br>
Строка 2<br>
Строка 3<br>
Строка 4<br>
в «хороший»
<p>Строка 1</p>
<p>Строка 2</p>
<p>Строка 3</p>
<p>Строка 4</p>
Вот как это делается (результат окажется в переменной goodHtml):
goodHtml = ''
(badHtml =~ /.*?<br>/).each{
goodHtml += "<p>${it.substring(0, it.lastIndexOf('<')).trim()}</p>"
}
В данном примере closure — это то, что заключено в фигурные скобки. В круглых скобках происходит разбивка строки на фрагменты, соответствующие регулярному выражению .*?<br>. Для каждого такого фрагмента и выполняется алгоритм в фигурных скобках. Об этом свидетельствует оператор each.
Регулярное выражение заключено в слэши, которые применяются вместо кавычек, чтобы избежать экранирования, т.е. вместо /.*?<br>/ можно было бы записать ".*?<br>". Но о регекспах в Groovy можно написать целую поэму, так что оставим это на следующий раз. А сейчас вернемся к closures.
Closure — это нечто среднее между методами и операторами циклов. Строго говоря, это, конечно же, методы (а может даже анонимные классы, если копнуть глубже). Однако с точки зрения синтаксиса пишутся так же легко, как циклы. Closures можно писать «по месту» (как было сделано в вышеприведенном примере) или объявлять и именовать, как методы. Например, некоторое время назад я писал о копировании потока в поток средствами Java. Вот как то же самое звучит в Groovy с помощью closures:
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
def stream2stream = {is, os ->
src = Channels.newChannel(is);
dest = Channels.newChannel(os);
buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
dest.write(buffer);
buffer.compact();
}
buffer.flip();
while (buffer.hasRemaining())dest.write(buffer);
}
Вот как с помощью этого скопировать один файл в другой:
stream2stream(new FileInputStream(file1), new FileOutputStream(file2))
По сути очень похоже на стандартное объявление метода и его вызов, только «букаф меньше». Во-первых, пропущен тип возвращаемого значения. Во-вторых, аргументы, передаваемые методу, перечислены до знака -> (входной поток is и выходной os), что выглядит изящнее. Если для closure аргументы не перечислены, но внутри его идет какой-то перебор в цикле, то к перечисляемым элементам можно обращаться с помощью универсальной переменной it (см. первый пример).
Может этот экскурс в мир closures и слишком краток, но по крайней мере примеры кода позволяют оценить эффективность этого средства Groovy: Java-листинг первого фрагмента (замена строк с помощью регулярных выражений) был бы раз в 5 длиннее.
Комментарии (5)
RSS свернуть / развернутьGangsta
yababay
Mihael
yababay
Markony
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.