Вступление
В этой статье хочу поделиться с Вами моими попытками создать случайную (хотя уместнее буде сказать псевдослучайную) сортировку средствами XSLT, без использования сторонних приложений и расширений.
Изначально была задача сделать сортировку под PHP-ный XSLT процессор. Но захотелось сделать что-то более уневерсальное.
Опытный образец
Для начала нам понадобиться образец для тестирования сортировки. Не будем заниматься хитросплетениями и напишем просто.
Файл данных data.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="view.xsl"?>
<root>
<item id="1"/><item id="2"/><item id="3"/><item id="4"/><item id="5"/><item id="6"/><item id="7"/><item id="8"/><item id="9"/><item id="10"/>
</root>
Генерируем случайности
Для того чтоб что-то случайно отсортировать надо чтоб под рукой было что-нибудь случайное. И не что не подошло лучше, чем функция «generate-id()», которая возвращает уникальный идентификатор элемента в виде строки.
Посмотрим, что у нас получается у разных процессоров которые оказались под рукой.
Файл преобразования view.xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="/root/item">
<xsl:value-of select="generate-id()"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Результат
PHP | FireFox | Opera | MSXML | Safari |
id1690783 | id0xfd238240 | op5275038 | IDAKA0MB | idp100503080 |
id1690785 | id0xfd2383d0 | op5275022 | IDAMA0MB | idp100502144 |
id1690788 | id0xfd238470 | op5275006 | IDAOA0MB | idp100502072 |
id1690789 | id0xfd2384c0 | op5274990 | IDAQA0MB | idp100502936 |
id1690787 | id0xfd238510 | op5274974 | IDASA0MB | idp100502792 |
id1690784 | id0xfd2640b0 | op5274958 | IDAUA0MB | idp100502648 |
id1690782 | id0xfd2641f0 | op5274942 | IDAWA0MB | idp125378088 |
id1690779 | id0xfd264240 | op5274926 | IDAYA0MB | idp100502504 |
id1690777 | id0xfd264330 | op5274910 | IDA0A0MB | idp125377944 |
id1690775 | id0xfd2643d0 | op5274894 | IDA2A0MB | idp100503224 |
Глядя на таблицу видно две беды: идентификаторы идут по определенному порядку и каждый XSLT процессор генерирует идентификатор под свой лад. Не могу не написать про мелкомягких, и тут выделились.
Случайная сортировка
Не буду описывать мои неудачные попытки по преобразованию последовательности в случайности, испытания разных алгоритмов, количества выпитого алкоголя и бессонных ночах. Напишу то, что в результате получилось.
Файл преобразования view.xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="/root/item">
<xsl:sort select="translate(generate-id(), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', '0192834765019283476501928347650192834765019283476501') mod 3.1415" data-type="number"/>
<xsl:value-of select="@id"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Основным элементом тут является атрибут «select» элемента «xsl:sort». Итак приступим к разбору: берется уникальный идентификатор текущего элемента (при помощи функции «generate-id()»), у него заменяются все буквенные значения на цифровые (при помощи функции «translate()») и результат делим по модулю на число Пи (почему именно Пи, да что первое в голову пришло).
Хоть и не тянет на идеальное и элегантное решение, но зато работает.
Вывод определенного числа случайных элементов.
Модифицируем таблицу для вывода определенного числа случайных элементов из набора. В этом нам поможет функция определения номера текущего элемента «position()». Выведем 4 случайных элемента.
Файл преобразования view.xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="/root/item">
<xsl:sort select="translate(generate-id(), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', '0192834765019283476501928347650192834765019283476501') mod 3.1415" data-type="number"/>
<xsl:if test="position() < 5">
<xsl:value-of select="@id"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>.
Немного PHP
Так как в начале статьи упоминалось про PHP, то и закончу кодом для преобразования на этом языке.
<?php
$data = new DOMDocument('1.0', 'UTF-8');
$data->load('data.xml');
$view = new DOMDocument('1.0', 'UTF-8');
$view->load('view.xsl');
$xsl = new XSLTProcessor();
$xsl->importStyleSheet($view);
echo $xsl->transformToXML($data);
?>
Автор: parsek