Использование fn: генератора случайных чисел для генерации случайных чисел более одного раза - PullRequest
1 голос
/ 24 апреля 2019

Я пытаюсь написать простую функцию, чтобы при каждом вызове вызывать случайное письмо, но у меня возникают трудности при объединении моей идеи с концепцией функционального программирования. Некоторая помощь по пути будет оценена! Код у меня выглядит так:

<xd:doc>
        <xd:desc>Provides one random letter, if the type is provided it returns a letter of thet type</xd:desc>
        <xd:param name="type">The type of letter to return, one of (A,a,B,b)</xd:param>
    </xd:doc>
    <xsl:function name="gdpr:randomLetter" as="xs:string">
        <xsl:param name="type" as="xs:string"></xsl:param>
        <xsl:choose>
            <xsl:when test="$type = 'A'">
                <xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 7)[1]"/>
                <xsl:variable name="letters" select="('A','O','U','E','I','Y','Q')"/>
                <xsl:value-of select="$letters[$randomNumber]"/>
            </xsl:when>
            <xsl:when test="$type = 'a'">
                <xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 7)[1]"/>
                <xsl:variable name="letters" select="('a','o','u','e','i','y','q')"/>
                <xsl:value-of select="$letters[$randomNumber]"/>
            </xsl:when>
            <xsl:when test="$type = 'B'">
                <xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 19)[1]"/>
                <xsl:variable name="letters" select="('W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
                <xsl:value-of select="$letters[$randomNumber]"/>
            </xsl:when>
            <xsl:when test="$type = 'b'">
                <xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 19)[1]"/>
                <xsl:variable name="letters" select="('w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z')"/>
                <xsl:value-of select="$letters[$randomNumber]"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 52)[1]"/>
                <xsl:variable name="letters" select="('A','O','U','E','I','Y','Q','a','o','u','e','i','y','q','w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z','W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
                <xsl:value-of select="$letters[$randomNumber]"/>
            </xsl:otherwise>
        </xsl:choose>

    </xsl:function>

Ответы [ 4 ]

4 голосов
/ 25 апреля 2019

Ваш вопрос содержит проблему:

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

Но функция, которая выдает разные результаты при разных вызовах (с одинаковыми аргументами), не является истинной («чистой») функцией.

Одним из выходов из этого является использование того факта, что XSLT уже имеет «нечистые» функции вида: функция, которая создает новый узел, каждый раз возвращает новый узел, и вы можете выставить это, используя generate-id ( ). Чтобы вы могли написать

<xsl:function name="my:random" as="xs:double">
  <xsl:variable name="dummy"><a/></xsl:variable>
  <xsl:sequence select="fn:random-number-generator(generate-id($dummy))?permute(1 to 10)"/>
</xsl:function>

Единственная проблема с этим заключается в том, что вы правы в границах того, что четко определено в спецификации, и оптимизатор может не позволить вам избежать подобных уловок. Гораздо лучше, если вы можете, найти какой-то способ передачи разного аргумента функции каждый раз, когда она вызывается: например, порядковый номер или generate-id (), примененный к входному узлу, который вы сейчас обрабатываете.

2 голосов
/ 24 апреля 2019

В контексте XSLT 3, я думаю, один способ получить «новый» random-number-generator для каждого нужного вам узла - определить аккумулятор:

<xsl:accumulator name="rng" as="map(xs:string, item())" initial-value="random-number-generator(current-dateTime())">
    <xsl:accumulator-rule match="foo[@type]" select="$value?next()"/>
</xsl:accumulator>

Таким образом, вы могли бы реализоватьваша функция как

<xsl:function name="gdpr:randomLetter" as="item()*">
    <xsl:param name="type" as="xs:string"/>
    <xsl:param name="rng" as="map(xs:string, item())"/>
    <xsl:choose>
        <xsl:when test="$type = 'A'">
            <xsl:variable name="randomNumber" select="$rng?permute(1 to 7)[1]"/>
            <xsl:variable name="letters" select="('A','O','U','E','I','Y','Q')"/>
            <xsl:sequence select="$letters[$randomNumber]"/>
        </xsl:when>
        <xsl:when test="$type = 'a'">
            <xsl:variable name="randomNumber" select="$rng?permute(1 to 7)[1]"/>
            <xsl:variable name="letters" select="('a','o','u','e','i','y','q')"/>
            <xsl:sequence select="$letters[$randomNumber]"/>
        </xsl:when>
        <xsl:when test="$type = 'B'">
            <xsl:variable name="randomNumber" select="$rng?permute(1 to 19)[1]"/>
            <xsl:variable name="letters" select="('W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
            <xsl:sequence select="$letters[$randomNumber]"/>
        </xsl:when>
        <xsl:when test="$type = 'b'">
            <xsl:variable name="randomNumber" select="$rng?permute(1 to 19)[1]"/>
            <xsl:variable name="letters" select="('w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z')"/>
            <xsl:sequence select="$letters[$randomNumber]"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:variable name="randomNumber" select="$rng?permute(1 to 52)[1]"/>
            <xsl:variable name="letters" select="('A','O','U','E','I','Y','Q','a','o','u','e','i','y','q','w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z','W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
            <xsl:sequence select="$letters[$randomNumber]"/>
        </xsl:otherwise>
    </xsl:choose>        
</xsl:function>

, а затем вызовите ее, например,

<xsl:template match="foo[@type]">
    <xsl:copy>
        <xsl:value-of select="gdpr:randomLetter(@type, accumulator-before('rng'))"/>   
    </xsl:copy>
</xsl:template>

и убедитесь, что вы используете

<xsl:mode on-no-match="shallow-copy" use-accumulators="rng"/>
0 голосов
/ 30 апреля 2019

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

<xsl:function name="gdpr:rngRecurseStart">
     <xsl:param name="text"></xsl:param>
     <xsl:variable name="chars" select="functx:chars($text)"/>

     <xsl:copy-of select="gdpr:rngRecurse($chars,random-number-generator(current-dateTime()),'')"></xsl:copy-of>
 </xsl:function>

 <xsl:function name="gdpr:rngRecurse">
     <xsl:param name="chars"></xsl:param>
     <xsl:param name="rngGenerator"></xsl:param>
     <xsl:param name="newText"></xsl:param>
     <xsl:variable name="curentchar" select="$chars[1]"></xsl:variable>
     <xsl:variable name="newRngGenerator" select="$rngGenerator?next()"/>
     <xsl:choose>
         <xsl:when test="count($chars) >1">
             <xsl:variable name="transformedChar" select="gdpr:randomLetter2($newRngGenerator,$curentchar)"/>
             <xsl:variable name="resultText" select="concat($newText, $transformedChar)"/>
             <xsl:copy-of select="gdpr:rngRecurse(subsequence($chars,2),$newRngGenerator,$resultText)"></xsl:copy-of>
         </xsl:when>
         <xsl:when test="count($chars) =1">
             <xsl:variable name="transformedChar" select="gdpr:randomLetter2($newRngGenerator,$curentchar)"/>
             <xsl:variable name="resultText" select="concat($newText, $transformedChar)"/>
             <xsl:copy-of select="$resultText"></xsl:copy-of>
         </xsl:when>
         <xsl:otherwise><xsl:copy-of select="$newText"></xsl:copy-of></xsl:otherwise>
     </xsl:choose>

 </xsl:function>
0 голосов
/ 24 апреля 2019

Проблема в том, что fn:random-number-generator функция является детерминированной. Спецификации объясняют, что:

Обе формы функции · детерминированы · : вызов функции дважды с одинаковыми аргументами, в пределах одной · области выполнения · , дает те же результаты.

Вам необходимо правильно использовать функцию под ключом next, содержащуюся в полученной карте, после вызова функции random-number-generator. Как сказано в спецификации:

Запись с ключом «next» является функцией нулевой арности, которую можно вызвать вернуть другой генератор случайных чисел.

...