Повторение строки N раз¶
Задача¶
Требуется повторить строку N раз, где N – параметр. Например, нужно дополнить строку пробелами, чтобы выровнять ее по правому или левому краю.
Решение¶
XSLT 1.0¶
Задачу можно решить красиво, применив рекурсивный алгоритм, который удваивает строку, пока не будет достигнута нужная длина. Надо только аккуратно рассмотреть случай, когда значение $count
нечетно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
XSLT 2.0¶
В версии 2.0 дублирование легко реализуется с помощью выражения for
. Мы перегружаем функцию dup
с целью имитировать имеющий значение по умолчанию аргумент в реализации для версии XSLT 1.0.
1 2 3 4 5 6 7 8 9 10 |
|
Обсуждение¶
XPath 1.0¶
Самый очевидный способ продублировать строку $count
раз – конкатенировать ее саму с собой $count - 1
раз. Это можно сделать рекурсивно, как показано ниже, но такой код работает неэффективно для сколько-нибудь большого числа повторений, поэтому применять его не рекомендуется.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Более разумный подход приведен в разделе «Решение». В нем число рекурсивных вызовов и конкатенаций уменьшено примерно до log2($count)
за счет повторного удвоения входной строки и уменьшения счетчика вдвое до тех пор, пока он не станет равен 1
. Реализация slow-dup
громоздка еще и потому, что требует искусственного параметра work
, в котором сохраняется исходная строка. Кроме того, вследствие рекурсии стек растет до $count - 1
уровней и требуется $count - 1
обращений к concat
. А в реализации dup
рост стека ограничен floor(log2($count))
уровнями и требуется лишь ceqling(log2($count))
вызовов concat
.
Техника, применяемая в slow-dup
, имеет одно достоинство – она позволяет дублировать не только строки, но и произвольные структуры. Достаточно заменить xsl:value-of
на xsl:copy-of
. Более быстрая версия dup
лишена этого преимущества, так как копии передаются как параметры, а это обходится дорого.
Ниже приведено еще одно решение, основанное на идее функции str:padding
из проекта EXSLT, но не совпадающее с ней дословно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
В этой реализации делается десять копий входной строки. Если это больше, чем необходимо, то результат усекается до нужной длины. В противном случае шаблон применяется рекурсивно. Это решение работает медленнее, так как часто производится больше конкатенаций, чем необходимо, и вызывается функция substring()
, которая в некоторых реализациях XSLT не эффективна. Зато оно обладает преимуществами при исполнении процессорами, которые не оптимизируют хвостовую рекурсию, поскольку число рекурсивных вызовов заметно меньше.
См. также¶
Так называемый метод Пиза также позволяет дублировать строку без рекурсии. Он обсуждается в документе. Суть его в том, чтобы использовать цикл for-each
для любого доступного источника узлов (часто самой таблицы стилей). Хотя на практике этот метод может показать высокую эффективность, я считаю его несовершенным из-за предположения о том, что узлов достаточно для выполнения требуемого числа итераций.