Простые числа и XPath

Опубликовано 6/21/2009

Иногда мы на работе решаем интересные задачки. Вот, например, одна (почти первая) в постановке nop`а:

Частный случай

Дан такой xml:

<items>
  <item>1</item>
  <item>2</item>
  ...
  <item>N-1</item>
  <item>N</item>
</items>

т.е. выписаны все натуральные числа от 1 до N включительно. Про N ничего заранее не известно -- большое, маленькое, еще какое-то — мы не знаем.

Нужно. Написать xpath, выбирающий все item'ы с простыми числами. Подчеркиваю - xpath. Т.е. внутри тега xsl:stylesheet должен быть один примерно такой шаблон:

<xsl:template match="/">
  <xsl:copy-of select="......."/>
</xsl:template>

и больше ничего — ни переменных, ни других шаблонов, ни функций.

На выходе будет что-то типа:

<item>2</item>
<item>3</item>
<item>5</item>
<item>7</item>
...

Решение следующее:

<xsl:template match="/">
  <items>
    <xsl:copy-of
      select="items/item[
        not(preceding-sibling::item[(last() + 1) mod . = 0 and . != 1])
        and
        . != 1
      ]"/>
  </items>
</xsl:template>

Общая задача

Усложненный вариант — все тоже самое, но в xml просто набор item'ов с какими-то натуральными числами в каком-то порядке, например:

<items>
  <item>142</item>
  <item>73</item>
  <item>10000341</item>
  <item>10</item>
  ...
</items>

Решение:

<xsl:template match="/">
  <items>
    <xsl:copy-of select="items/item[
      not(
        str:tokenize(str:padding(. - 1, '1'), '')[
          (last() + 1 ) mod position()= 0
          and position() != 1
        ]
      ) and . != 1
      ]"/>
  </items>
</xsl:template>

Чтобы понять, что тут делается, надо прочитать про функции padding и tokenize на EXSLT.org.